Skip to content
This repository was archived by the owner on Dec 15, 2024. It is now read-only.

Commit 25e73f4

Browse files
committed
Updated Lock to match Foundation API
1 parent fc476cf commit 25e73f4

File tree

1 file changed

+180
-109
lines changed

1 file changed

+180
-109
lines changed

Sources/SwiftFoundation/Lock.swift

Lines changed: 180 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -12,147 +12,218 @@
1212
import Glibc
1313
#endif
1414

15-
/// Protocol defining lock and unlock operations.
1615
public protocol Locking {
1716

18-
/// Block until acquiring lock.
1917
func lock()
20-
21-
/// Relinquish lock.
2218
func unlock()
2319
}
2420

25-
/// Used to coordinate the operation of multiple threads of execution within the same application.
26-
/// A lock object can be used to mediate access to an application’s global data or to protect a critical section of code,
27-
/// allowing it to run atomically.
28-
///
29-
/// - Note: `Lock` uses POSIX threads to implement its locking behavior.
30-
/// When sending an unlock message to a `Lock`, you must be sure that message is sent from the same thread
31-
/// that sent the initial lock message. Unlocking a lock from a different thread can result in undefined behavior.
32-
/// You should not use this class to implement a recursive lock.
33-
/// Calling the lock method twice on the same thread will lock up your thread permanently.
34-
/// Unlocking a lock that is not locked is considered a programmer error and should be fixed in your code.
35-
/// The `Lock` reports such errors by printing an error message to the console when they occur.
36-
public final class Lock: Locking {
37-
38-
// MARK: - Properties
39-
40-
/// You can use a name string to identify a lock within your code.
21+
public class Lock: Locking {
22+
23+
private var mutex = UnsafeMutablePointer<pthread_mutex_t>(allocatingCapacity: 1)
24+
25+
public init() {
26+
pthread_mutex_init(mutex, nil)
27+
}
28+
29+
deinit {
30+
pthread_mutex_destroy(mutex)
31+
mutex.deinitialize()
32+
mutex.deallocateCapacity(1)
33+
}
34+
35+
public func lock() {
36+
pthread_mutex_lock(mutex)
37+
}
38+
39+
public func unlock() {
40+
pthread_mutex_unlock(mutex)
41+
}
42+
43+
public func tryLock() -> Bool {
44+
return pthread_mutex_trylock(mutex) == 0
45+
}
46+
4147
public var name: String?
48+
}
49+
50+
extension Lock {
51+
internal func synchronized<T>(_ closure: @noescape () -> T) -> T {
52+
self.lock()
53+
defer { self.unlock() }
54+
return closure()
55+
}
56+
}
57+
58+
public class NSConditionLock : NSObject, Locking {
59+
internal var _cond = Condition()
60+
internal var _value: Int
61+
internal var _thread: pthread_t?
62+
63+
public convenience override init() {
64+
self.init(condition: 0)
65+
}
4266

43-
private var internalMutex = pthread_mutex_t()
67+
public init(condition: Int) {
68+
_value = condition
69+
}
4470

45-
// MARK: - Intialization
71+
public func lock() {
72+
let _ = lockBeforeDate(Date.distantFuture)
73+
}
4674

47-
deinit { pthread_mutex_destroy(&internalMutex) }
75+
public func unlock() {
76+
_cond.lock()
77+
_thread = nil
78+
_cond.broadcast()
79+
_cond.unlock()
80+
}
4881

49-
public init() {
50-
51-
pthread_mutex_init(&internalMutex, nil)
82+
public var condition: Int {
83+
return _value
5284
}
5385

54-
// MARK: - Methods
86+
public func lockWhenCondition(_ condition: Int) {
87+
let _ = lockWhenCondition(condition, beforeDate: Date.distantFuture)
88+
}
5589

56-
/// Attempts to acquire a lock before a given time.
57-
///
58-
/// - Discussion: The thread is blocked until the receiver acquires the lock or limit is reached.
59-
public func lock(before limit: Date) {
60-
61-
assert(Date() < limit, "\(limit) must be after the current date.")
62-
63-
guard tryLock() else {
64-
65-
repeat { sched_yield() }
66-
67-
while limit - Date() > 0
68-
69-
return
90+
public func tryLock() -> Bool {
91+
return lockBeforeDate(Date.distantPast)
92+
}
93+
94+
public func tryLockWhenCondition(_ condition: Int) -> Bool {
95+
return lockWhenCondition(condition, beforeDate: Date.distantPast)
96+
}
97+
98+
public func unlockWithCondition(_ condition: Int) {
99+
_cond.lock()
100+
_thread = nil
101+
_value = condition
102+
_cond.broadcast()
103+
_cond.unlock()
104+
}
105+
106+
public func lockBeforeDate(_ limit: Date) -> Bool {
107+
_cond.lock()
108+
while _thread == nil {
109+
if !_cond.waitUntilDate(limit) {
110+
_cond.unlock()
111+
return false
112+
}
70113
}
114+
_thread = pthread_self()
115+
_cond.unlock()
116+
return true
117+
}
118+
119+
public func lockWhenCondition(_ condition: Int, beforeDate limit: Date) -> Bool {
120+
_cond.lock()
121+
while _thread != nil || _value != condition {
122+
if !_cond.waitUntilDate(limit) {
123+
_cond.unlock()
124+
return false
125+
}
126+
}
127+
_thread = pthread_self()
128+
_cond.unlock()
129+
return true
130+
}
131+
132+
public var name: String?
133+
}
134+
135+
public class RecursiveLock: NSObject, Locking {
136+
internal var mutex = UnsafeMutablePointer<pthread_mutex_t>(allocatingCapacity: 1)
137+
138+
public override init() {
139+
super.init()
140+
var attrib = pthread_mutexattr_t()
141+
withUnsafeMutablePointer(&attrib) { attrs in
142+
pthread_mutexattr_settype(attrs, Int32(PTHREAD_MUTEX_RECURSIVE))
143+
pthread_mutex_init(mutex, attrs)
144+
}
145+
}
146+
147+
deinit {
148+
pthread_mutex_destroy(mutex)
149+
mutex.deinitialize()
150+
mutex.deallocateCapacity(1)
71151
}
72152

73-
@inline(__always)
74153
public func lock() {
75-
76-
let errorCode = pthread_mutex_lock(&internalMutex)
77-
78-
assert(errorCode == 0, "Could not lock mutex. \(POSIXError(rawValue: errorCode)!)")
154+
pthread_mutex_lock(mutex)
79155
}
80156

81-
@inline(__always)
82157
public func unlock() {
83-
84-
let errorCode = pthread_mutex_unlock(&internalMutex)
85-
86-
assert(errorCode == 0, "Could not unlock mutex. \(POSIXError(rawValue: errorCode)!)")
158+
pthread_mutex_unlock(mutex)
87159
}
88160

89-
/// Try to acquire lock and return immediately.
90-
@inline(__always)
91161
public func tryLock() -> Bool {
92-
93-
return pthread_mutex_trylock(&internalMutex) == 0
162+
return pthread_mutex_trylock(mutex) == 0
94163
}
164+
165+
public var name: String?
95166
}
96167

97-
// MARK: - Private Types
98-
99-
private extension Lock {
168+
public class Condition: NSObject, Locking {
169+
internal var mutex = UnsafeMutablePointer<pthread_mutex_t>(allocatingCapacity: 1)
170+
internal var cond = UnsafeMutablePointer<pthread_cond_t>(allocatingCapacity: 1)
100171

101-
/// POSIX Mutex (`Lock`) Attribute
102-
private final class Attribute {
103-
104-
// MARK: - Singletons
105-
106-
private static let Normal = Attribute(type: .normal)
107-
108-
private static let ErrorCheck = Attribute(type: .errorCheck)
109-
110-
private static let Recursive = Attribute(type: .recursive)
111-
112-
// MARK: - Properties
113-
114-
private var internalAttribute = pthread_mutexattr_t()
115-
116-
// MARK: - Initialization
117-
118-
deinit { pthread_mutexattr_destroy(&internalAttribute) }
119-
120-
private init() {
121-
122-
pthread_mutexattr_init(&internalAttribute)
172+
public override init() {
173+
pthread_mutex_init(mutex, nil)
174+
pthread_cond_init(cond, nil)
175+
}
176+
177+
deinit {
178+
pthread_mutex_destroy(mutex)
179+
pthread_cond_destroy(cond)
180+
mutex.deinitialize()
181+
cond.deinitialize()
182+
mutex.deallocateCapacity(1)
183+
cond.deallocateCapacity(1)
184+
}
185+
186+
public func lock() {
187+
pthread_mutex_lock(mutex)
188+
}
189+
190+
public func unlock() {
191+
pthread_mutex_unlock(mutex)
192+
}
193+
194+
public func wait() {
195+
pthread_cond_wait(cond, mutex)
196+
}
197+
198+
public func waitUntilDate(_ limit: Date) -> Bool {
199+
let lim = limit.timeIntervalSinceReferenceDate
200+
let ti = lim - CFAbsoluteTimeGetCurrent()
201+
if ti < 0.0 {
202+
return false
123203
}
124-
125-
private convenience init(type: AttributeType) {
126-
127-
self.init()
128-
129-
self.type = type
204+
var ts = timespec()
205+
ts.tv_sec = Int(floor(ti))
206+
ts.tv_nsec = Int((ti - Double(ts.tv_sec)) * 1000000000.0)
207+
var tv = timeval()
208+
withUnsafeMutablePointer(&tv) { t in
209+
gettimeofday(t, nil)
210+
ts.tv_sec += t.pointee.tv_sec
211+
ts.tv_nsec += Int((t.pointee.tv_usec * 1000000) / 1000000000)
130212
}
131-
132-
// MARK: - Methods
133-
134-
private var type: AttributeType {
135-
136-
get {
137-
138-
var typeRawValue: CInt = 0
139-
140-
pthread_mutexattr_gettype(&internalAttribute, &typeRawValue)
141-
142-
return AttributeType(rawValue: typeRawValue)!
143-
}
144-
145-
set { pthread_mutexattr_settype(&internalAttribute, newValue.rawValue) }
213+
let retVal: Int32 = withUnsafePointer(&ts) { t in
214+
return pthread_cond_timedwait(cond, mutex, t)
146215
}
216+
217+
return retVal == 0
147218
}
148219

149-
/// POSIX Mutex (`Lock`) Attribute Type
150-
private enum AttributeType: CInt {
151-
152-
case normal = 0
153-
case errorCheck = 1
154-
case recursive = 2
155-
156-
private init() { self = normal }
220+
public func signal() {
221+
pthread_cond_signal(cond)
222+
}
223+
224+
public func broadcast() {
225+
pthread_cond_broadcast(cond)
157226
}
227+
228+
public var name: String?
158229
}

0 commit comments

Comments
 (0)