diff --git a/Sources/Foundation/NSLock.swift b/Sources/Foundation/NSLock.swift index 59d334f1ad..f4384b70ac 100644 --- a/Sources/Foundation/NSLock.swift +++ b/Sources/Foundation/NSLock.swift @@ -22,6 +22,19 @@ public protocol NSLocking { func unlock() } +extension NSLocking { + @_alwaysEmitIntoClient + @_disfavoredOverload + public func withLock(_ body: () throws -> R) rethrows -> R { + self.lock() + defer { + self.unlock() + } + + return try body() + } +} + #if os(Windows) private typealias _MutexPointer = UnsafeMutablePointer private typealias _RecursiveMutexPointer = UnsafeMutablePointer diff --git a/Tests/Foundation/Tests/TestNSLock.swift b/Tests/Foundation/Tests/TestNSLock.swift index 3e40a8a38b..58a1b00060 100644 --- a/Tests/Foundation/Tests/TestNSLock.swift +++ b/Tests/Foundation/Tests/TestNSLock.swift @@ -14,6 +14,7 @@ class TestNSLock: XCTestCase { ("test_lockWait", test_lockWait), ("test_threadsAndLocks", test_threadsAndLocks), ("test_recursiveLock", test_recursiveLock), + ("test_withLock", test_withLock), ] } @@ -187,4 +188,33 @@ class TestNSLock: XCTestCase { threadCompletedCondition.unlock() } + + func test_withLock() { + let lock = NSLock() + + var counter = 0 + let counterIncrementPerThread = 10_000 + + let threadCount = 10 + + let threadCompletedExpectation = expectation(description: "Expected threads to complete.") + threadCompletedExpectation.expectedFulfillmentCount = threadCount + + for _ in 0..