From 73f806e6cd4aa9d528715f5ec9e4b2b33cfeb62f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Seweryn=20Plaz=CC=87uk?= Date: Thu, 1 Feb 2024 17:19:23 +0100 Subject: [PATCH] Add thread safety to Clock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Seweryn Plażuk --- Kronos.xcodeproj/project.pbxproj | 4 ++++ Sources/Clock.swift | 15 +++++++++++++-- Sources/UnfairLock.swift | 29 +++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 Sources/UnfairLock.swift diff --git a/Kronos.xcodeproj/project.pbxproj b/Kronos.xcodeproj/project.pbxproj index 31dc1e6..a7bd26e 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 */; }; + E5E7A7702BFE559F0025D9D0 /* UnfairLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5E7A76F2BFE559F0025D9D0 /* UnfairLock.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -58,6 +59,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 = ""; }; + E5E7A76F2BFE559F0025D9D0 /* UnfairLock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnfairLock.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -124,6 +126,7 @@ 26447D7B1D6E54D400159BEE /* TimeFreeze.swift */, 930B39DC2051E6D300360BA2 /* TimeStorage.swift */, 5DB5A05F2BAAF67D0069CCF9 /* PrivacyInfo.xcprivacy */, + E5E7A76F2BFE559F0025D9D0 /* UnfairLock.swift */, ); path = Sources; sourceTree = ""; @@ -234,6 +237,7 @@ 26447D7E1D6E54D400159BEE /* InternetAddress.swift in Sources */, 26447D811D6E54D400159BEE /* NTPClient.swift in Sources */, 26447D7F1D6E54D400159BEE /* Data+Bytes.swift in Sources */, + E5E7A7702BFE559F0025D9D0 /* UnfairLock.swift in Sources */, 26447D841D6E54D400159BEE /* TimeFreeze.swift in Sources */, 26447D821D6E54D400159BEE /* NTPPacket.swift in Sources */, 26447D801D6E54D400159BEE /* NSTimer+ClosureKit.swift in Sources */, diff --git a/Sources/Clock.swift b/Sources/Clock.swift index fade988..b0c91a5 100644 --- a/Sources/Clock.swift +++ b/Sources/Clock.swift @@ -25,9 +25,20 @@ public typealias AnnotatedTime = ( /// print(Clock.now) /// ``` public struct Clock { + private static let lock = UnfairLock() + + private static var _stableTime: TimeFreeze? private static var stableTime: TimeFreeze? { - didSet { - self.storage.stableTime = self.stableTime + get { + self.lock.around { + return self._stableTime + } + } + set { + self.lock.around { + self._stableTime = newValue + self.storage.stableTime = newValue + } } } diff --git a/Sources/UnfairLock.swift b/Sources/UnfairLock.swift new file mode 100644 index 0000000..47b01c5 --- /dev/null +++ b/Sources/UnfairLock.swift @@ -0,0 +1,29 @@ +import Foundation + +final class UnfairLock { + private let unfairLock: os_unfair_lock_t + + init() { + unfairLock = .allocate(capacity: 1) + unfairLock.initialize(to: os_unfair_lock()) + } + + deinit { + unfairLock.deinitialize(count: 1) + unfairLock.deallocate() + } + + func around(_ block: () -> T) -> T { + lock() + defer { unlock() } + return block() + } + + private func lock() { + os_unfair_lock_lock(unfairLock) + } + + private func unlock() { + os_unfair_lock_unlock(unfairLock) + } +}