1
1
// This source file is part of the Swift.org open source project
2
2
//
3
- // Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
3
+ // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
4
4
// Licensed under Apache License v2.0 with Runtime Library Exception
5
5
//
6
6
// See http://swift.org/LICENSE.txt for license information
@@ -21,39 +21,71 @@ public protocol NSLocking {
21
21
func unlock( )
22
22
}
23
23
24
- open class NSLock : NSObject , NSLocking {
25
24
#if CYGWIN
26
- internal var mutex = UnsafeMutablePointer< pthread_mutex_t?> . allocate( capacity: 1 )
25
+ private typealias _PthreadMutexPointer = UnsafeMutablePointer < pthread_mutex_t ? >
26
+ private typealias _PthreadCondPointer = UnsafeMutablePointer < pthread_cond_t ? >
27
27
#else
28
- internal var mutex = UnsafeMutablePointer< pthread_mutex_t> . allocate( capacity: 1 )
28
+ private typealias _PthreadMutexPointer = UnsafeMutablePointer < pthread_mutex_t >
29
+ private typealias _PthreadCondPointer = UnsafeMutablePointer < pthread_cond_t >
29
30
#endif
30
-
31
+
32
+ open class NSLock : NSObject , NSLocking {
33
+ internal var mutex = _PthreadMutexPointer. allocate ( capacity: 1 )
34
+ #if os(OSX) || os(iOS)
35
+ private var timeoutCond = _PthreadCondPointer. allocate ( capacity: 1 )
36
+ private var timeoutMutex = _PthreadMutexPointer. allocate ( capacity: 1 )
37
+ #endif
38
+
31
39
public override init ( ) {
32
40
pthread_mutex_init ( mutex, nil )
41
+ #if os(OSX) || os(iOS)
42
+ pthread_cond_init ( timeoutCond, nil )
43
+ pthread_mutex_init ( timeoutMutex, nil )
44
+ #endif
33
45
}
34
46
35
47
deinit {
36
48
pthread_mutex_destroy ( mutex)
37
49
mutex. deinitialize ( )
38
50
mutex. deallocate ( capacity: 1 )
51
+ #if os(OSX) || os(iOS)
52
+ deallocateTimedLockData ( cond: timeoutCond, mutex: timeoutMutex)
53
+ #endif
39
54
}
40
55
41
56
open func lock( ) {
42
57
pthread_mutex_lock ( mutex)
43
58
}
44
-
59
+
45
60
open func unlock( ) {
46
61
pthread_mutex_unlock ( mutex)
62
+ #if os(OSX) || os(iOS)
63
+ // Wakeup any threads waiting in lock(before:)
64
+ pthread_mutex_lock ( timeoutMutex)
65
+ pthread_cond_broadcast ( timeoutCond)
66
+ pthread_mutex_unlock ( timeoutMutex)
67
+ #endif
47
68
}
48
-
69
+
49
70
open func `try`( ) -> Bool {
50
71
return pthread_mutex_trylock ( mutex) == 0
51
72
}
52
73
53
- open func lock( before limit: Date ) {
54
- NSUnimplemented ( )
74
+ open func lock( before limit: Date ) -> Bool {
75
+ if pthread_mutex_trylock ( mutex) == 0 {
76
+ return true
77
+ }
78
+
79
+ #if os(OSX) || os(iOS)
80
+ return timedLock ( mutex: mutex, endTime: limit, using: timeoutCond, with: timeoutMutex)
81
+ #else
82
+ guard var endTime = timeSpecFrom ( date: limit) else {
83
+ return false
84
+ }
85
+ return pthread_mutex_timedlock ( mutex, & endTime) == 0
86
+ #endif
55
87
}
56
-
88
+
57
89
open var name : String ?
58
90
}
59
91
@@ -143,12 +175,12 @@ open class NSConditionLock : NSObject, NSLocking {
143
175
}
144
176
145
177
open class NSRecursiveLock : NSObject , NSLocking {
146
- #if CYGWIN
147
- internal var mutex = UnsafeMutablePointer < pthread_mutex_t ?> . allocate ( capacity : 1 )
148
- #else
149
- internal var mutex = UnsafeMutablePointer < pthread_mutex_t > . allocate( capacity: 1 )
178
+ internal var mutex = _PthreadMutexPointer . allocate ( capacity : 1 )
179
+ #if os(OSX) || os(iOS )
180
+ private var timeoutCond = _PthreadCondPointer . allocate ( capacity : 1 )
181
+ private var timeoutMutex = _PthreadMutexPointer . allocate ( capacity: 1 )
150
182
#endif
151
-
183
+
152
184
public override init ( ) {
153
185
super. init ( )
154
186
#if CYGWIN
@@ -166,6 +198,9 @@ open class NSRecursiveLock: NSObject, NSLocking {
166
198
pthread_mutex_destroy ( mutex)
167
199
mutex. deinitialize ( )
168
200
mutex. deallocate ( capacity: 1 )
201
+ #if os(OSX) || os(iOS)
202
+ deallocateTimedLockData ( cond: timeoutCond, mutex: timeoutMutex)
203
+ #endif
169
204
}
170
205
171
206
open func lock( ) {
@@ -174,28 +209,40 @@ open class NSRecursiveLock: NSObject, NSLocking {
174
209
175
210
open func unlock( ) {
176
211
pthread_mutex_unlock ( mutex)
212
+ #if os(OSX) || os(iOS)
213
+ // Wakeup any threads waiting in lock(before:)
214
+ pthread_mutex_lock ( timeoutMutex)
215
+ pthread_cond_broadcast ( timeoutCond)
216
+ pthread_mutex_unlock ( timeoutMutex)
217
+ #endif
177
218
}
178
219
179
220
open func `try`( ) -> Bool {
180
221
return pthread_mutex_trylock ( mutex) == 0
181
222
}
182
223
183
- open func lock( before limit: Date ) {
184
- NSUnimplemented ( )
224
+ open func lock( before limit: Date ) -> Bool {
225
+ if pthread_mutex_trylock ( mutex) == 0 {
226
+ return true
227
+ }
228
+
229
+ #if os(OSX) || os(iOS)
230
+ return timedLock ( mutex: mutex, endTime: limit, using: timeoutCond, with: timeoutMutex)
231
+ #else
232
+ guard var endTime = timeSpecFrom ( date: limit) else {
233
+ return false
234
+ }
235
+ return pthread_mutex_timedlock ( mutex, & endTime) == 0
236
+ #endif
185
237
}
186
238
187
239
open var name : String ?
188
240
}
189
241
190
242
open class NSCondition : NSObject , NSLocking {
191
- #if CYGWIN
192
- internal var mutex = UnsafeMutablePointer< pthread_mutex_t?> . allocate( capacity: 1 )
193
- internal var cond = UnsafeMutablePointer< pthread_cond_t?> . allocate( capacity: 1 )
194
- #else
195
- internal var mutex = UnsafeMutablePointer< pthread_mutex_t> . allocate( capacity: 1 )
196
- internal var cond = UnsafeMutablePointer< pthread_cond_t> . allocate( capacity: 1 )
197
- #endif
198
-
243
+ internal var mutex = _PthreadMutexPointer. allocate ( capacity: 1 )
244
+ internal var cond = _PthreadCondPointer. allocate ( capacity: 1 )
245
+
199
246
public override init ( ) {
200
247
pthread_mutex_init ( mutex, nil )
201
248
pthread_cond_init ( cond, nil )
@@ -221,31 +268,12 @@ open class NSCondition: NSObject, NSLocking {
221
268
open func wait( ) {
222
269
pthread_cond_wait ( cond, mutex)
223
270
}
224
-
271
+
225
272
open func wait( until limit: Date ) -> Bool {
226
- let lim = limit. timeIntervalSinceReferenceDate
227
- let ti = lim - CFAbsoluteTimeGetCurrent( )
228
- if ti < 0.0 {
273
+ guard var timeout = timeSpecFrom ( date: limit) else {
229
274
return false
230
275
}
231
- var ts = timespec ( )
232
- ts. tv_sec = Int ( floor ( ti) )
233
- ts. tv_nsec = Int ( ( ti - Double( ts. tv_sec) ) * 1_000_000_000.0 )
234
- var tv = timeval ( )
235
- withUnsafeMutablePointer ( to: & tv) { t in
236
- gettimeofday ( t, nil )
237
- ts. tv_sec += t. pointee. tv_sec
238
- ts. tv_nsec += Int ( t. pointee. tv_usec) * 1000
239
- if ts. tv_nsec >= 1_000_000_000 {
240
- ts. tv_sec += ts. tv_nsec / 1_000_000_000
241
- ts. tv_nsec = ts. tv_nsec % 1_000_000_000
242
- }
243
- }
244
- let retVal : Int32 = withUnsafePointer ( to: & ts) { t in
245
- return pthread_cond_timedwait ( cond, mutex, t)
246
- }
247
-
248
- return retVal == 0
276
+ return pthread_cond_timedwait ( cond, mutex, & timeout) == 0
249
277
}
250
278
251
279
open func signal( ) {
@@ -258,3 +286,58 @@ open class NSCondition: NSObject, NSLocking {
258
286
259
287
open var name : String ?
260
288
}
289
+
290
+ private func timeSpecFrom( date: Date ) -> timespec ? {
291
+ guard date. timeIntervalSinceNow > 0 else {
292
+ return nil
293
+ }
294
+ let nsecPerSec : Int64 = 1_000_000_000
295
+ let interval = date. timeIntervalSince1970
296
+ let intervalNS = Int64 ( interval * Double( nsecPerSec) )
297
+
298
+ return timespec ( tv_sec: Int ( intervalNS / nsecPerSec) ,
299
+ tv_nsec: Int ( intervalNS % nsecPerSec) )
300
+ }
301
+
302
+ #if os(OSX) || os(iOS)
303
+
304
+ private func deallocateTimedLockData( cond: _PthreadCondPointer , mutex: _PthreadMutexPointer ) {
305
+ pthread_cond_destroy ( cond)
306
+ cond. deinitialize ( )
307
+ cond. deallocate ( capacity: 1 )
308
+
309
+ pthread_mutex_destroy ( mutex)
310
+ mutex. deinitialize ( )
311
+ mutex. deallocate ( capacity: 1 )
312
+ }
313
+
314
+ // Emulate pthread_mutex_timedlock using pthread_cond_timedwait.
315
+ // lock(before:) passes a condition variable/mutex pair to use.
316
+ // unlock() will use pthread_cond_broadcast() to wake any waits in progress.
317
+ private func timedLock( mutex: _PthreadMutexPointer , endTime: Date ,
318
+ using timeoutCond: _PthreadCondPointer ,
319
+ with timeoutMutex: _PthreadMutexPointer ) -> Bool {
320
+
321
+ var timeSpec = timeSpecFrom ( date: endTime)
322
+ while var ts = timeSpec {
323
+ let lockval = pthread_mutex_lock ( timeoutMutex)
324
+ precondition ( lockval == 0 )
325
+ let waitval = pthread_cond_timedwait ( timeoutCond, timeoutMutex, & ts)
326
+ precondition ( waitval == 0 || waitval == ETIMEDOUT)
327
+ let unlockval = pthread_mutex_unlock ( timeoutMutex)
328
+ precondition ( unlockval == 0 )
329
+
330
+ if waitval == ETIMEDOUT {
331
+ return false
332
+ }
333
+ let tryval = pthread_mutex_trylock ( mutex)
334
+ precondition ( tryval == 0 || tryval == EBUSY)
335
+ if tryval == 0 { // The lock was obtained.
336
+ return true
337
+ }
338
+ // pthread_cond_timedwait didnt timeout so wait some more.
339
+ timeSpec = timeSpecFrom ( date: endTime)
340
+ }
341
+ return false
342
+ }
343
+ #endif
0 commit comments