diff --git a/Kronos.xcodeproj/project.pbxproj b/Kronos.xcodeproj/project.pbxproj index cbb7197..91d68fb 100644 --- a/Kronos.xcodeproj/project.pbxproj +++ b/Kronos.xcodeproj/project.pbxproj @@ -23,6 +23,7 @@ 930B39DD2051E6D300360BA2 /* TimeStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 930B39DC2051E6D300360BA2 /* TimeStorage.swift */; }; 930B39E02051F26500360BA2 /* TimeStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 930B39DE2051F25300360BA2 /* TimeStorageTests.swift */; }; C201748E1BD5509D00E4FE18 /* Kronos.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C20174831BD5509D00E4FE18 /* Kronos.framework */; }; + E5481CD32B6BF3010072186E /* UnfairLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5481CD22B6BF3010072186E /* UnfairLock.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -57,6 +58,7 @@ C2C036D41C2B180D003FB853 /* UniversalFramework_Base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = UniversalFramework_Base.xcconfig; sourceTree = ""; }; C2C036D51C2B180D003FB853 /* UniversalFramework_Framework.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = UniversalFramework_Framework.xcconfig; sourceTree = ""; }; C2C036D61C2B180D003FB853 /* UniversalFramework_Test.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = UniversalFramework_Test.xcconfig; sourceTree = ""; }; + E5481CD22B6BF3010072186E /* UnfairLock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnfairLock.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -122,6 +124,7 @@ 26447D7A1D6E54D400159BEE /* NTPProtocol.swift */, 26447D7B1D6E54D400159BEE /* TimeFreeze.swift */, 930B39DC2051E6D300360BA2 /* TimeStorage.swift */, + E5481CD22B6BF3010072186E /* UnfairLock.swift */, ); path = Sources; sourceTree = ""; @@ -230,6 +233,7 @@ 26447D7C1D6E54D400159BEE /* Clock.swift in Sources */, 26447D7D1D6E54D400159BEE /* DNSResolver.swift in Sources */, 26447D7E1D6E54D400159BEE /* InternetAddress.swift in Sources */, + E5481CD32B6BF3010072186E /* UnfairLock.swift in Sources */, 26447D811D6E54D400159BEE /* NTPClient.swift in Sources */, 26447D7F1D6E54D400159BEE /* Data+Bytes.swift in Sources */, 26447D841D6E54D400159BEE /* TimeFreeze.swift in Sources */, diff --git a/Sources/Clock.swift b/Sources/Clock.swift index fade988..0831951 100644 --- a/Sources/Clock.swift +++ b/Sources/Clock.swift @@ -25,15 +25,38 @@ public typealias AnnotatedTime = ( /// print(Clock.now) /// ``` public struct Clock { + private static var unfairLock = UnfairLock() + private static var _stableTime: TimeFreeze? + private static var _storage = TimeStorage(storagePolicy: .standard) + private static var stableTime: TimeFreeze? { - didSet { - self.storage.stableTime = self.stableTime + get { + self.unfairLock.synchronized { + return self._stableTime + } + } + set { + self.unfairLock.synchronized { + self._stableTime = newValue + self._storage.stableTime = newValue + } } } /// Determines where the most current stable time is stored. Use TimeStoragePolicy.appGroup to share /// between your app and an extension. - public static var storage = TimeStorage(storagePolicy: .standard) + public static var storage: TimeStorage { + get { + self.unfairLock.synchronized { + return self._storage + } + } + set { + self.unfairLock.synchronized { + self._storage = newValue + } + } + } /// The most accurate timestamp that we have so far (nil if no synchronization was done yet) public static var timestamp: TimeInterval? { diff --git a/Sources/UnfairLock.swift b/Sources/UnfairLock.swift new file mode 100644 index 0000000..b0f40d2 --- /dev/null +++ b/Sources/UnfairLock.swift @@ -0,0 +1,29 @@ +import os.lock + +final class UnfairLock { + private let lockPointer: os_unfair_lock_t + + init() { + self.lockPointer = .allocate(capacity: 1) + self.lockPointer.initialize(to: os_unfair_lock()) + } + + deinit { + self.lockPointer.deinitialize(count: 1) + self.lockPointer.deallocate() + } + + func lock() { + os_unfair_lock_lock(self.lockPointer) + } + + func unlock() { + os_unfair_lock_unlock(self.lockPointer) + } + + func synchronized(_ block: () -> T) -> T { + self.lock() + defer { self.unlock() } + return block() + } +}