Merge pull request #1193 from spevans/pr_nslock

diff --git a/Docs/Status.md b/Docs/Status.md
index fa06c03..7a58b1d 100644
--- a/Docs/Status.md
+++ b/Docs/Status.md
@@ -47,7 +47,7 @@
     |------------------------------|-----------------|---------------|--------------------------------------------------------------------------------------------------------------------|
     | `URLAuthenticationChallenge` | Unimplemented   | None          |                                                                                                                    |
     | `URLCache`                   | Unimplemented   | None          |                                                                                                                    |
-    | `URLCredential`              | Complete | Incomplete    | 									                                                                                  |
+    | `URLCredential`              | Complete        | Incomplete    |                                                                                                                    |
     | `URLCredentialStorage`       | Unimplemented   | None          |                                                                                                                    |
     | `NSURLError*`                | Complete        | N/A           |                                                                                                                    |
     | `URLProtectionSpace`         | Unimplemented   | None          |                                                                                                                    |
@@ -96,13 +96,13 @@
     | `DateComponentFormatter`        | Unimplemented   | None          |                                                                                           |
     | `DateIntervalFormatter`         | Unimplemented   | None          |                                                                                           |
     | `EnergyFormatter`               | Unimplemented   | None          |                                                                                           |
-    | `ISO8601DateFormatter`        | Unimplemented   | None          |                                                                                           |
-    | `LengthFormatter`               | Complete 	    | Substantial   |                                                                                           |
+    | `ISO8601DateFormatter`          | Unimplemented   | None          |                                                                                           |
+    | `LengthFormatter`               | Complete        | Substantial   |                                                                                           |
     | `MassFormatter`                 | Complete        | Substantial   | Needs localization                                                                        |
     | `NumberFormatter`               | Mostly Complete | Substantial   | `objectValue(_:range:)` remains unimplemented                                             |
     | `PersonNameComponentsFormatter` | Unimplemented   | None          |                                                                                           |
     | `ByteCountFormatter`            | Mostly Complete | Substantial   | `init?(coder:)` remains unimplemented                                                     |
-    | `DateFormatter`                 | Mostly Complete | Incomplete    | `objectValue(_:range:)` remain unimplemented 										 |
+    | `DateFormatter`                 | Mostly Complete | Incomplete    | `objectValue(_:range:)` remain unimplemented                                              |
     | `Formatter`                     | Complete        | N/A           |                                                                                           |
     | `MeasurementFormatter`          | Unimplemented   | None          |                                                                                           |
 
@@ -123,8 +123,8 @@
 
     | Entity Name                 | Status          | Test Coverage | Notes                                                                         |
     |-----------------------------|-----------------|---------------|-------------------------------------------------------------------------------|
-    | `NSJSONSerialization`       | Mostly Complete | Substantial   | `jsonObject(with:options:)` with streams remains unimplemented             |
-    | `NSKeyedArchiver`           | Complete        | Substantial   |                              |
+    | `NSJSONSerialization`       | Mostly Complete | Substantial   | `jsonObject(with:options:)` with streams remains unimplemented                |
+    | `NSKeyedArchiver`           | Complete        | Substantial   |                                                                               |
     | `NSKeyedCoderOldStyleArray` | N/A             | N/A           | For internal use only                                                         |
     | `NSKeyedUnarchiver`         | Mostly Complete | Substantial   | `decodingFailurePolicy.set` remains unimplemented                             |
     | `NSKeyedArchiverHelpers`    | N/A             | N/A           | For internal use only                                                         |
@@ -152,7 +152,7 @@
 
     | Entity Name           | Status          | Test Coverage | Notes                                                                                                                                                               |
     |-----------------------|-----------------|---------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-    | `NSOrderedSet`        | Mostly Complete | Substantial   | `NS[Mutable]Copying`, and `array` & `set` (and associated indexing methods) remain unimplemented                        |
+    | `NSOrderedSet`        | Mostly Complete | Substantial   | `NS[Mutable]Copying`, and `array` & `set` (and associated indexing methods) remain unimplemented                                                                    |
     | `NSMutableOrderedSet` | Mostly Complete | Substantial   | `NSCoding` and `sortRange(_:options:, usingComparator:)` with non-empty options remain unimplemented                                                                |
     | `NSCFArray`           | N/A             | N/A           | For internal use only                                                                                                                                               |
     | `NSIndexSet`          | Mostly Complete | Incomplete    | `NSCoding`, `NSCopying`, and concurrent `enumerateWithOptions(_:range:paramType:returnType:block:)` remain unimplemented                                            |
@@ -197,10 +197,10 @@
 
     | Entity Name                 | Status          | Test Coverage | Notes                                                                                                                                                            |
     |-----------------------------|-----------------|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-    | `NSRegularExpression`       | Complete        | Substantial   |                                                                                                                                 |
+    | `NSRegularExpression`       | Complete        | Substantial   |                                                                                                                                                                  |
     | `Scanner`                   | Mostly Complete | Incomplete    | `scanHex<T: _FloatLike>(_:locale:locationToScanFrom:to:)` and `localizedScannerWithString(_:)` remain unimplemented                                              |
     | `NSTextCheckingResult`      | Mostly Complete | Incomplete    | `NSCoding`, `NSCopying`, `resultType`, and `range(at:)` remain unimplemented                                                                                     |
-    | `NSAttributedString`        | Incomplete      | Incomplete    | `NSCoding`, `NS[Mutable]Copying`, `attributedSubstring(from:)`, `isEqual(to:)`, `init(NSAttributedString:)` remain unimplemented |
+    | `NSAttributedString`        | Incomplete      | Incomplete    | `NSCoding`, `NS[Mutable]Copying`, `attributedSubstring(from:)`, `isEqual(to:)`, `init(NSAttributedString:)` remain unimplemented                                 |
     | `NSMutableAttributedString` | Unimplemented   | Incomplete    | Only `addAttribute(_:value:range:)` is implemented                                                                                                               |
     | `NSCharacterSet`            | Mostly Complete | Incomplete    | `NSCoding` remains unimplemented                                                                                                                                 |
     | `NSMutableCharacterSet`     | Mostly Complete | None          | Decoding remains unimplemented                                                                                                                                   |
@@ -216,14 +216,14 @@
 
     | Entity Name                       | Status          | Test Coverage | Notes                                                                         |
     |-----------------------------------|-----------------|---------------|-------------------------------------------------------------------------------|
-    | `NSRange`                         | Complete 		  | Incomplete    | 															                |
+    | `NSRange`                         | Complete        | Incomplete    |                                                                               |
     | `Decimal`                         | Unimplemented   | None          |                                                                               |
     | `NSDecimalNumber`                 | Unimplemented   | None          |                                                                               |
     | `NSDecimalNumberHandler`          | Unimplemented   | None          |                                                                               |
     | `CGPoint`                         | Complete        | Substantial   |                                                                               |
     | `CGSize`                          | Complete        | Substantial   |                                                                               |
     | `CGRect`                          | Complete        | Substantial   |                                                                               |
-    | `NSEdgeInsets`                    | Mostly Complete | None          | 															                |
+    | `NSEdgeInsets`                    | Mostly Complete | None          |                                                                               |
     | `NSGeometry`                      | Mostly Complete | Substantial   | `NSIntegralRectWithOptions` `.AlignRectFlipped` support remains unimplemented |
     | `CGFloat`                         | Complete        | Substantial   |                                                                               |
     | `AffineTransform`                 | Complete        | None          |                                                                               |
@@ -265,7 +265,7 @@
     | Entity Name    | Statues         | Test Coverage | Notes                                                                                                                         |
     |----------------|-----------------|---------------|-------------------------------------------------------------------------------------------------------------------------------|
     | `UserDefaults` | Incomplete      | None          | `dictionaryRepresentation()`, domain support, and forced objects remain unimplemented. Unit tests are currently commented out |
-    | `NSLocale`     | Complete		   | Incomplete    | Only unit test asserts locale key constant names																             |
+    | `NSLocale`     | Complete        | Incomplete    | Only unit test asserts locale key constant names                                                                              |
     | `Locale`       | Complete        | Incomplete    | Only unit test asserts value copying                                                                                          |
 
 * **OS**: Mechanisms for interacting with the operating system on a file system level as well as process and thread level
@@ -282,24 +282,24 @@
     | `Operation`      | Complete        | Incomplete    |                                                                                                                           |
     | `BlockOperation` | Complete        | Incomplete    |                                                                                                                           |
     | `OperationQueue` | Complete        | Incomplete    |                                                                                                                           |
-    | `Lock`           | Mostly Complete | None          | `lock(before:)` remains unimplemented                                                                                     |
+    | `Lock`           | Complete        | Incomplete    |                                                                                                                           |
     | `ConditionLock`  | Complete        | None          |                                                                                                                           |
-    | `RecursiveLock`  | Mostly Complete | None          | `lock(before:)` remains unimplemented                                                                                     |
-    | `Condition`      | Complete        | None          |                                                                                                                           |
+    | `RecursiveLock`  | Complete        | None          |                                                                                                                           |
+    | `Condition`      | Complete        | Incomplete    |                                                                                                                           |
 
 * **DateTime**: Classes for representing dates, timezones, and calendars.
 
     | Entity Name        | Status          | Test Coverage | Notes                                                                                                                           |
     |--------------------|-----------------|---------------|---------------------------------------------------------------------------------------------------------------------------------|
-    | `NSCalendar`       | Complete		   | None          | `autoupdatingCurrent`, and `enumerateDates` remain unimplemented                    								  |
-    | `NSDateComponents` | Complete		   | None          | 								                                                                     |
+    | `NSCalendar`       | Complete        | None          | `autoupdatingCurrent`, and `enumerateDates` remain unimplemented                                                                |
+    | `NSDateComponents` | Complete        | None          |                                                                                                                                 |
     | `Calendar`         | Complete        | Incomplete    |                                                                                                                                 |
     | `DateComponents`   | Complete        | Incomplete    |                                                                                                                                 |
-    | `NSDate`           | Complete		   | Incomplete    |                                    |
+    | `NSDate`           | Complete        | Incomplete    |                                                                                                                                 |
     | `NSDateInterval`   | Complete        | None          |                                                                                                                                 |
     | `DateInterval`     | Complete        | None          |                                                                                                                                 |
     | `Date`             | Complete        | Incomplete    |                                                                                                                                 |
-    | `NSTimeZone`       | Mostly Complete | Incomplete    | `local`, `timeZoneDataVersion` and setting `abbreviationDictionary` remain unimplemented 					  |
+    | `NSTimeZone`       | Mostly Complete | Incomplete    | `local`, `timeZoneDataVersion` and setting `abbreviationDictionary` remain unimplemented                                        |
     | `TimeZone`         | Complete        | Incomplete    |                                                                                                                                 |
 
 * **Notifications**: Classes for loosely coupling events from a set of many observers.
diff --git a/Foundation.xcodeproj/project.pbxproj b/Foundation.xcodeproj/project.pbxproj
index eeace9e..4fff062 100644
--- a/Foundation.xcodeproj/project.pbxproj
+++ b/Foundation.xcodeproj/project.pbxproj
@@ -326,6 +326,7 @@
 		B910957B1EEF237800A71930 /* NSString-UTF16-BE-data.txt in Resources */ = {isa = PBXBuildFile; fileRef = B91095791EEF237800A71930 /* NSString-UTF16-BE-data.txt */; };
 		B933A79E1F3055F700FE6846 /* NSString-UTF32-BE-data.txt in Resources */ = {isa = PBXBuildFile; fileRef = B933A79C1F3055F600FE6846 /* NSString-UTF32-BE-data.txt */; };
 		B933A79F1F3055F700FE6846 /* NSString-UTF32-LE-data.txt in Resources */ = {isa = PBXBuildFile; fileRef = B933A79D1F3055F600FE6846 /* NSString-UTF32-LE-data.txt */; };
+		B951B5EC1F4E2A2000D8B332 /* TestNSLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = B951B5EB1F4E2A2000D8B332 /* TestNSLock.swift */; };
 		B9974B961EDF4A22007F15B8 /* TransferState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9974B8F1EDF4A22007F15B8 /* TransferState.swift */; };
 		B9974B971EDF4A22007F15B8 /* MultiHandle.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9974B901EDF4A22007F15B8 /* MultiHandle.swift */; };
 		B9974B981EDF4A22007F15B8 /* libcurlHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9974B911EDF4A22007F15B8 /* libcurlHelpers.swift */; };
@@ -795,6 +796,7 @@
 		B91095791EEF237800A71930 /* NSString-UTF16-BE-data.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "NSString-UTF16-BE-data.txt"; sourceTree = "<group>"; };
 		B933A79C1F3055F600FE6846 /* NSString-UTF32-BE-data.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = "NSString-UTF32-BE-data.txt"; sourceTree = "<group>"; };
 		B933A79D1F3055F600FE6846 /* NSString-UTF32-LE-data.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = "NSString-UTF32-LE-data.txt"; sourceTree = "<group>"; };
+		B951B5EB1F4E2A2000D8B332 /* TestNSLock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestNSLock.swift; sourceTree = "<group>"; };
 		B9974B8F1EDF4A22007F15B8 /* TransferState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TransferState.swift; path = http/TransferState.swift; sourceTree = "<group>"; };
 		B9974B901EDF4A22007F15B8 /* MultiHandle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MultiHandle.swift; path = http/MultiHandle.swift; sourceTree = "<group>"; };
 		B9974B911EDF4A22007F15B8 /* libcurlHelpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = libcurlHelpers.swift; path = http/libcurlHelpers.swift; sourceTree = "<group>"; };
@@ -1498,6 +1500,7 @@
 				400E22641C1A4E58007C5933 /* TestProcessInfo.swift */,
 				EA01AAEB1DA839C4008F4E07 /* TestProgress.swift */,
 				5E5835F31C20C9B500C81317 /* TestThread.swift */,
+				B951B5EB1F4E2A2000D8B332 /* TestNSLock.swift */,
 				CC5249BF1D341D23007CB54D /* TestUnitConverter.swift */,
 				D4FE895A1D703D1100DA7986 /* TestURLRequest.swift */,
 				5B6F17961C48631C00935030 /* TestUtils.swift */,
@@ -2445,6 +2448,7 @@
 				D512D17C1CD883F00032E6A5 /* TestFileHandle.swift in Sources */,
 				D4FE895B1D703D1100DA7986 /* TestURLRequest.swift in Sources */,
 				684C79011F62B611005BD73E /* TestNSNumberBridging.swift in Sources */,
+				B951B5EC1F4E2A2000D8B332 /* TestNSLock.swift in Sources */,
 				5B13B33A1C582D4C00651CE2 /* TestNSNumber.swift in Sources */,
 				5B13B3521C582D4C00651CE2 /* TestNSValue.swift in Sources */,
 				5B13B3311C582D4C00651CE2 /* TestIndexPath.swift in Sources */,
diff --git a/Foundation/NSLock.swift b/Foundation/NSLock.swift
index f4b41d0..886b5ba 100644
--- a/Foundation/NSLock.swift
+++ b/Foundation/NSLock.swift
@@ -1,6 +1,6 @@
 // This source file is part of the Swift.org open source project
 //
-// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
+// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
 // Licensed under Apache License v2.0 with Runtime Library Exception
 //
 // See http://swift.org/LICENSE.txt for license information
@@ -21,39 +21,71 @@
     func unlock()
 }
 
-open class NSLock: NSObject, NSLocking {
 #if CYGWIN
-    internal var mutex = UnsafeMutablePointer<pthread_mutex_t?>.allocate(capacity: 1)
+private typealias _PthreadMutexPointer = UnsafeMutablePointer<pthread_mutex_t?>
+private typealias _PthreadCondPointer = UnsafeMutablePointer<pthread_cond_t?>
 #else
-    internal var mutex = UnsafeMutablePointer<pthread_mutex_t>.allocate(capacity: 1)
+private typealias _PthreadMutexPointer = UnsafeMutablePointer<pthread_mutex_t>
+private typealias _PthreadCondPointer = UnsafeMutablePointer<pthread_cond_t>
 #endif
-    
+
+open class NSLock: NSObject, NSLocking {
+    internal var mutex = _PthreadMutexPointer.allocate(capacity: 1)
+#if os(OSX) || os(iOS)
+    private var timeoutCond = _PthreadCondPointer.allocate(capacity: 1)
+    private var timeoutMutex = _PthreadMutexPointer.allocate(capacity: 1)
+#endif
+
     public override init() {
         pthread_mutex_init(mutex, nil)
+#if os(OSX) || os(iOS)
+        pthread_cond_init(timeoutCond, nil)
+        pthread_mutex_init(timeoutMutex, nil)
+#endif
     }
     
     deinit {
         pthread_mutex_destroy(mutex)
         mutex.deinitialize()
         mutex.deallocate(capacity: 1)
+#if os(OSX) || os(iOS)
+        deallocateTimedLockData(cond: timeoutCond, mutex: timeoutMutex)
+#endif
     }
     
     open func lock() {
         pthread_mutex_lock(mutex)
     }
-    
+
     open func unlock() {
         pthread_mutex_unlock(mutex)
+#if os(OSX) || os(iOS)
+        // Wakeup any threads waiting in lock(before:)
+        pthread_mutex_lock(timeoutMutex)
+        pthread_cond_broadcast(timeoutCond)
+        pthread_mutex_unlock(timeoutMutex)
+#endif
     }
-    
+
     open func `try`() -> Bool {
         return pthread_mutex_trylock(mutex) == 0
     }
     
-    open func lock(before limit: Date) {
-        NSUnimplemented()
+    open func lock(before limit: Date) -> Bool {
+        if pthread_mutex_trylock(mutex) == 0 {
+            return true
+        }
+
+#if os(OSX) || os(iOS)
+        return timedLock(mutex: mutex, endTime: limit, using: timeoutCond, with: timeoutMutex)
+#else
+        guard var endTime = timeSpecFrom(date: limit) else {
+            return false
+        }
+        return pthread_mutex_timedlock(mutex, &endTime) == 0
+#endif
     }
-    
+
     open var name: String?
 }
 
@@ -143,12 +175,12 @@
 }
 
 open class NSRecursiveLock: NSObject, NSLocking {
-#if CYGWIN
-    internal var mutex = UnsafeMutablePointer<pthread_mutex_t?>.allocate(capacity: 1)
-#else
-    internal var mutex = UnsafeMutablePointer<pthread_mutex_t>.allocate(capacity: 1)
+    internal var mutex = _PthreadMutexPointer.allocate(capacity: 1)
+#if os(OSX) || os(iOS)
+    private var timeoutCond = _PthreadCondPointer.allocate(capacity: 1)
+    private var timeoutMutex = _PthreadMutexPointer.allocate(capacity: 1)
 #endif
-    
+
     public override init() {
         super.init()
 #if CYGWIN
@@ -166,6 +198,9 @@
         pthread_mutex_destroy(mutex)
         mutex.deinitialize()
         mutex.deallocate(capacity: 1)
+#if os(OSX) || os(iOS)
+        deallocateTimedLockData(cond: timeoutCond, mutex: timeoutMutex)
+#endif
     }
     
     open func lock() {
@@ -174,28 +209,40 @@
     
     open func unlock() {
         pthread_mutex_unlock(mutex)
+#if os(OSX) || os(iOS)
+        // Wakeup any threads waiting in lock(before:)
+        pthread_mutex_lock(timeoutMutex)
+        pthread_cond_broadcast(timeoutCond)
+        pthread_mutex_unlock(timeoutMutex)
+#endif
     }
     
     open func `try`() -> Bool {
         return pthread_mutex_trylock(mutex) == 0
     }
     
-    open func lock(before limit: Date) {
-        NSUnimplemented()
+    open func lock(before limit: Date) -> Bool {
+        if pthread_mutex_trylock(mutex) == 0 {
+            return true
+        }
+
+#if os(OSX) || os(iOS)
+        return timedLock(mutex: mutex, endTime: limit, using: timeoutCond, with: timeoutMutex)
+#else
+        guard var endTime = timeSpecFrom(date: limit) else {
+            return false
+        }
+        return pthread_mutex_timedlock(mutex, &endTime) == 0
+#endif
     }
 
     open var name: String?
 }
 
 open class NSCondition: NSObject, NSLocking {
-#if CYGWIN
-    internal var mutex = UnsafeMutablePointer<pthread_mutex_t?>.allocate(capacity: 1)
-    internal var cond = UnsafeMutablePointer<pthread_cond_t?>.allocate(capacity: 1)
-#else
-    internal var mutex = UnsafeMutablePointer<pthread_mutex_t>.allocate(capacity: 1)
-    internal var cond = UnsafeMutablePointer<pthread_cond_t>.allocate(capacity: 1)
-#endif
-    
+    internal var mutex = _PthreadMutexPointer.allocate(capacity: 1)
+    internal var cond = _PthreadCondPointer.allocate(capacity: 1)
+
     public override init() {
         pthread_mutex_init(mutex, nil)
         pthread_cond_init(cond, nil)
@@ -221,31 +268,12 @@
     open func wait() {
         pthread_cond_wait(cond, mutex)
     }
-    
+
     open func wait(until limit: Date) -> Bool {
-        let lim = limit.timeIntervalSinceReferenceDate
-        let ti = lim - CFAbsoluteTimeGetCurrent()
-        if ti < 0.0 {
+        guard var timeout = timeSpecFrom(date: limit) else {
             return false
         }
-        var ts = timespec()
-        ts.tv_sec = Int(floor(ti))
-        ts.tv_nsec = Int((ti - Double(ts.tv_sec)) * 1_000_000_000.0)
-        var tv = timeval()
-        withUnsafeMutablePointer(to: &tv) { t in
-            gettimeofday(t, nil)
-            ts.tv_sec += t.pointee.tv_sec
-            ts.tv_nsec += Int(t.pointee.tv_usec) * 1000
-            if ts.tv_nsec >= 1_000_000_000 {
-                ts.tv_sec += ts.tv_nsec / 1_000_000_000
-                ts.tv_nsec = ts.tv_nsec % 1_000_000_000
-            }
-        }
-        let retVal: Int32 = withUnsafePointer(to: &ts) { t in
-            return pthread_cond_timedwait(cond, mutex, t)
-        }
-
-        return retVal == 0
+        return pthread_cond_timedwait(cond, mutex, &timeout) == 0
     }
     
     open func signal() {
@@ -258,3 +286,58 @@
     
     open var name: String?
 }
+
+private func timeSpecFrom(date: Date) -> timespec? {
+    guard date.timeIntervalSinceNow > 0 else {
+        return nil
+    }
+    let nsecPerSec: Int64 = 1_000_000_000
+    let interval = date.timeIntervalSince1970
+    let intervalNS = Int64(interval * Double(nsecPerSec))
+
+    return timespec(tv_sec: Int(intervalNS / nsecPerSec),
+                    tv_nsec: Int(intervalNS % nsecPerSec))
+}
+
+#if os(OSX) || os(iOS)
+
+private func deallocateTimedLockData(cond: _PthreadCondPointer, mutex: _PthreadMutexPointer) {
+    pthread_cond_destroy(cond)
+    cond.deinitialize()
+    cond.deallocate(capacity: 1)
+
+    pthread_mutex_destroy(mutex)
+    mutex.deinitialize()
+    mutex.deallocate(capacity: 1)
+}
+
+// Emulate pthread_mutex_timedlock using pthread_cond_timedwait.
+// lock(before:) passes a condition variable/mutex pair to use.
+// unlock() will use pthread_cond_broadcast() to wake any waits in progress.
+private func timedLock(mutex: _PthreadMutexPointer, endTime: Date,
+                       using timeoutCond: _PthreadCondPointer,
+                       with timeoutMutex: _PthreadMutexPointer) -> Bool {
+
+    var timeSpec = timeSpecFrom(date: endTime)
+    while var ts = timeSpec {
+        let lockval = pthread_mutex_lock(timeoutMutex)
+        precondition(lockval == 0)
+        let waitval = pthread_cond_timedwait(timeoutCond, timeoutMutex, &ts)
+        precondition(waitval == 0 || waitval == ETIMEDOUT)
+        let unlockval = pthread_mutex_unlock(timeoutMutex)
+        precondition(unlockval == 0)
+
+        if waitval == ETIMEDOUT {
+            return false
+        }
+        let tryval = pthread_mutex_trylock(mutex)
+        precondition(tryval == 0 || tryval == EBUSY)
+        if tryval == 0 { // The lock was obtained.
+            return true
+        }
+        // pthread_cond_timedwait didnt timeout so wait some more.
+        timeSpec = timeSpecFrom(date: endTime)
+    }
+    return false
+}
+#endif
diff --git a/TestFoundation/TestNSLock.swift b/TestFoundation/TestNSLock.swift
new file mode 100644
index 0000000..9191ade
--- /dev/null
+++ b/TestFoundation/TestNSLock.swift
@@ -0,0 +1,125 @@
+// This source file is part of the Swift.org open source project
+//
+// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
+// Licensed under Apache License v2.0 with Runtime Library Exception
+//
+// See http://swift.org/LICENSE.txt for license information
+// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
+//
+
+#if DEPLOYMENT_RUNTIME_OBJC || os(Linux)
+import Foundation
+import XCTest
+#else
+import SwiftFoundation
+import SwiftXCTest
+#endif
+
+import CoreFoundation
+
+class TestNSLock: XCTestCase {
+    static var allTests: [(String, (TestNSLock) -> () throws -> Void)] {
+        return [
+
+            ("test_lockWait", test_lockWait),
+            ("test_threadsAndLocks", test_threadsAndLocks),
+
+        ]
+    }
+
+
+    func test_lockWait() {
+        let condition = NSCondition()
+        let lock = NSLock()
+
+        func test(waitTime: CFTimeInterval, shouldLock: Bool) -> Bool {
+            let locked = lock.lock(before: Date.init(timeIntervalSinceNow: waitTime))
+            if locked {
+                lock.unlock()
+            }
+            return locked == shouldLock
+        }
+
+        let thread = Thread() {
+            condition.lock()
+            condition.signal()
+            condition.unlock()
+
+            lock.lock()
+            Thread.sleep(forTimeInterval: 8)
+            lock.unlock()
+        }
+        condition.lock()
+        thread.start()
+        condition.wait()
+        condition.unlock()
+
+        XCTAssertTrue(test(waitTime: 0, shouldLock: false))
+        XCTAssertTrue(test(waitTime: -1, shouldLock: false))
+        XCTAssertTrue(test(waitTime: 1, shouldLock: false))
+        XCTAssertTrue(test(waitTime: 4, shouldLock: false))
+        XCTAssertTrue(test(waitTime: 8, shouldLock: true))
+        XCTAssertTrue(test(waitTime: -1, shouldLock: true))
+    }
+
+
+    func test_threadsAndLocks() {
+        let condition = NSCondition()
+        let lock = NSLock()
+        let threadCount = 1000
+        let endSeconds: Double = 10
+
+        let endTime = Date.init(timeIntervalSinceNow: endSeconds)
+        var threadsStarted = Array<Bool>(repeating: false, count: threadCount)
+        let arrayLock = NSLock()
+
+        for t in 0..<threadCount {
+            let thread = Thread() {
+                arrayLock.lock()
+                threadsStarted[t] = true
+                arrayLock.unlock()
+
+                condition.lock()
+                condition.wait()
+                condition.unlock()
+                for _ in 1...100 {
+                    let r = (endSeconds * drand48()) / 100
+                    Thread.sleep(forTimeInterval: r)
+                    if lock.lock(before: endTime) {
+                        lock.unlock()
+                    }
+                }
+                arrayLock.lock()
+                threadsStarted[t] = false
+                arrayLock.unlock()
+            }
+            thread.start()
+        }
+
+        var totalThreads = 0
+        repeat {
+            arrayLock.lock()
+            totalThreads = threadsStarted.filter {$0 == true }.count
+            arrayLock.unlock()
+        } while totalThreads < threadCount
+        XCTAssertEqual(totalThreads, threadCount)
+
+        condition.lock()
+        condition.broadcast()
+        condition.unlock()
+
+        Thread.sleep(until: endTime)
+        repeat {
+            arrayLock.lock()
+            totalThreads = threadsStarted.filter {$0 == false }.count
+            arrayLock.unlock()
+        } while totalThreads < threadCount
+        XCTAssertEqual(totalThreads, threadCount)
+
+        let gotLock = lock.try()
+        XCTAssertTrue(gotLock)
+        if gotLock {
+            lock.unlock()
+        }
+    }
+}
diff --git a/TestFoundation/main.swift b/TestFoundation/main.swift
index a1f6bf9..227c609 100644
--- a/TestFoundation/main.swift
+++ b/TestFoundation/main.swift
@@ -111,4 +111,5 @@
     testCase(TestJSONEncoder.allTests),
     testCase(TestCodable.allTests),
     testCase(TestUnit.allTests),
+    testCase(TestNSLock.allTests),
 ])