Renames UnfairLock to TSMutex and refreshes its interface to remove methods that are unused except in tests. TSMutex has a generic parameter State that supports non-sendable and non-copyable values. UnfairLock is a type alias of TSMutex<Void>. The withLock() function is also no longer inherited from NSLocking, instead being implemented to be similar to OSAllocatedUnfairLock's API. Finally, the class is audited for sendability and compiles without warnings under strict concurrency checking.
97 lines
2.3 KiB
Swift
97 lines
2.3 KiB
Swift
//
|
|
// Copyright 2020 Signal Messenger, LLC
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
//
|
|
|
|
import SignalServiceKit
|
|
import XCTest
|
|
|
|
final class UnfairLockTest: XCTestCase {
|
|
// MARK: - Lock + Unlock
|
|
|
|
func testSimpleLockAndUnlock() {
|
|
// Setup
|
|
let dut = UnfairLock()
|
|
nonisolated(unsafe) var sharedVal = 0
|
|
|
|
// Test
|
|
fanout(1000) {
|
|
dut.lock()
|
|
sharedVal += 1
|
|
dut.unlock()
|
|
}
|
|
|
|
// Verify
|
|
XCTAssertEqual(sharedVal, 1000, "Lock failed to prevent data race.")
|
|
}
|
|
|
|
func testLockedClosure() {
|
|
// Setup
|
|
let dut = UnfairLock()
|
|
nonisolated(unsafe) var sharedVal = 0
|
|
|
|
// Test
|
|
fanout(1000) {
|
|
dut.withLock {
|
|
sharedVal += 1
|
|
}
|
|
}
|
|
|
|
// Verify
|
|
XCTAssertEqual(sharedVal, 1000, "Lock failed to prevent data race.")
|
|
}
|
|
|
|
// MARK: - Return Value
|
|
|
|
func testPropagatedReturnValue() {
|
|
// Setup
|
|
let dut = UnfairLock()
|
|
let outerVal: String? = "Hello, this is an optional string"
|
|
|
|
// Test
|
|
let returnedVal = dut.withLock {
|
|
return outerVal?.appending("!")
|
|
}
|
|
|
|
// Expect
|
|
XCTAssertEqual(returnedVal, "Hello, this is an optional string!")
|
|
}
|
|
|
|
// MARK: - Throwing Inner Closure
|
|
|
|
func testThrowingLockedClosure() {
|
|
// Setup
|
|
let dut = UnfairLock()
|
|
nonisolated(unsafe) var didAcquireLock = false
|
|
nonisolated(unsafe) var didReacquireLock = false
|
|
var didCatchError = false
|
|
|
|
// Test
|
|
let toThrow = NSError(domain: "UnfairLockTests", code: 2, userInfo: nil)
|
|
do {
|
|
try dut.withLock {
|
|
didAcquireLock = true
|
|
throw toThrow
|
|
}
|
|
} catch {
|
|
XCTAssertEqual(toThrow, (error as NSError))
|
|
didCatchError = true
|
|
}
|
|
|
|
dut.withLock {
|
|
didReacquireLock = true
|
|
}
|
|
|
|
// Verify
|
|
XCTAssertTrue(didAcquireLock)
|
|
XCTAssertTrue(didCatchError)
|
|
XCTAssertTrue(didReacquireLock)
|
|
}
|
|
|
|
// MARK: - Test Helpers
|
|
|
|
func fanout(_ iterations: Int, _ block: @Sendable () -> Void) {
|
|
DispatchQueue.concurrentPerform(iterations: iterations) { _ in block() }
|
|
}
|
|
}
|