From b30f149dc93195a5d5744d031c30180ba768f00f Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Sat, 8 Oct 2022 10:57:59 -0400 Subject: [PATCH] Use mutex on iOS 15 instead of os_unfair_lock See #178 --- Tusker/MultiThreadDictionary.swift | 45 +++++++++++++++++++++--------- TuskerTests/TuskerTests.swift | 40 ++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 13 deletions(-) diff --git a/Tusker/MultiThreadDictionary.swift b/Tusker/MultiThreadDictionary.swift index 1a0d4661..8b5c316b 100644 --- a/Tusker/MultiThreadDictionary.swift +++ b/Tusker/MultiThreadDictionary.swift @@ -19,7 +19,7 @@ class MultiThreadDictionary { if #available(iOS 16.0, *) { self.lock = OSAllocatedUnfairLock(initialState: [:]) } else { - self.lock = UnfairLock(initialState: [:]) + self.lock = MutexLock(initialState: [:]) } } @@ -65,22 +65,41 @@ fileprivate protocol Lock { extension OSAllocatedUnfairLock: Lock { } -// from http://www.russbishop.net/the-law -fileprivate class UnfairLock: Lock { - private var lock: UnsafeMutablePointer +// something is wrong with the UnfairLock impl and it results in segv_accerrs +fileprivate class MutexLock: Lock { private var state: State + private var lock = NSLock() + init(initialState: State) { self.state = initialState - self.lock = .allocate(capacity: 1) - self.lock.initialize(to: os_unfair_lock()) } - deinit { - self.lock.deinitialize(count: 1) - self.lock.deallocate() - } - func withLock(_ body: (inout State) throws -> R) rethrows -> R where R: Sendable { - os_unfair_lock_lock(lock) - defer { os_unfair_lock_unlock(lock) } + + func withLock(_ body: @Sendable (inout State) throws -> R) rethrows -> R where R : Sendable { + if !lock.lock(before: Date(timeIntervalSinceNow: 1)) { + // if we can't acquire the lock after 1 second, something has gone catastrophically wrong + fatalError() + } + defer { lock.unlock() } return try body(&state) } } + +//// from http://www.russbishop.net/the-law +//fileprivate class UnfairLock: Lock { +// private var lock: UnsafeMutablePointer +// private var state: State +// init(initialState: State) { +// self.state = initialState +// self.lock = .allocate(capacity: 1) +// self.lock.initialize(to: os_unfair_lock()) +// } +// deinit { +// self.lock.deinitialize(count: 1) +// self.lock.deallocate() +// } +// func withLock(_ body: (inout State) throws -> R) rethrows -> R where R: Sendable { +// os_unfair_lock_lock(lock) +// defer { os_unfair_lock_unlock(lock) } +// return try body(&state) +// } +//} diff --git a/TuskerTests/TuskerTests.swift b/TuskerTests/TuskerTests.swift index 1d8f9a78..2ba572fa 100644 --- a/TuskerTests/TuskerTests.swift +++ b/TuskerTests/TuskerTests.swift @@ -30,5 +30,45 @@ class TuskerTests: XCTestCase { // Put the code you want to measure the time of here. } } + + + func testFuckingLock() { + let lock = MutexLock<[Int: Bool]>(initialState: [:]) + for i in 0..<100 { + Thread.detachNewThread { + for j in 0..<50_000 { + lock.withLock { + $0[i * 50_000 + j] = true + } + } + } + } + while true { + if lock.withLock({ $0.count }) == 5_000_000 { + break + } + } + lock.withLock({ _ in + print("WHAT THE FUUUUUUUUUUUUCK") + }) + } } + +fileprivate class MutexLock { + private var state: State + private var lock = NSLock() + + init(initialState: State) { + self.state = initialState + } + + func withLock(_ body: @Sendable (inout State) throws -> R) rethrows -> R where R : Sendable { + if !lock.lock(before: Date(timeIntervalSinceNow: 1)) { + // if we can't acquire the lock after 1 second, something has gone catastrophically wrong + fatalError() + } + defer { lock.unlock() } + return try body(&state) + } +}