Merge pull request #924 from ianpartridge/nsregularexpression

diff --git a/Foundation/NSOperation.swift b/Foundation/NSOperation.swift
index fcc9b38..988aef8 100644
--- a/Foundation/NSOperation.swift
+++ b/Foundation/NSOperation.swift
@@ -47,7 +47,6 @@
 #endif
     }
     
-    /// - Note: Operations that are asynchronous from the execution of the operation queue itself are not supported since there is no KVO to trigger the finish.
     open func start() {
         main()
         finish()
@@ -159,6 +158,24 @@
     }
 }
 
+/// The following two methods are added to provide support for Operations which
+/// are asynchronous from the execution of the operation queue itself.  On Darwin,
+/// this is supported via KVO notifications.  In the absence of KVO on non-Darwin
+/// platforms, these two methods (which are defined in NSObject on Darwin) are
+/// temporarily added here.  They should be removed once a permanent solution is
+/// found.
+extension Operation {
+    public func willChangeValue(forKey key: String) {
+        // do nothing
+    }
+
+    public func didChangeValue(forKey key: String) {
+        if key == "isFinished" && isFinished {
+            finish()
+        }
+    }
+}
+
 extension Operation {
     public enum QueuePriority : Int {
         case veryLow
diff --git a/TestFoundation/TestNSOperationQueue.swift b/TestFoundation/TestNSOperationQueue.swift
index cc74b86..783115f 100644
--- a/TestFoundation/TestNSOperationQueue.swift
+++ b/TestFoundation/TestNSOperationQueue.swift
@@ -16,12 +16,14 @@
 import SwiftFoundation
 import SwiftXCTest
 #endif
+import Dispatch
 
 class TestNSOperationQueue : XCTestCase {
     static var allTests: [(String, (TestNSOperationQueue) -> () throws -> Void)] {
         return [
             ("test_OperationPriorities", test_OperationPriorities),
-            ("test_OperationCount", test_OperationCount)
+            ("test_OperationCount", test_OperationCount),
+            ("test_AsyncOperation", test_AsyncOperation)
         ]
     }
     
@@ -65,4 +67,76 @@
         XCTAssertEqual(msgOperations[2], "Operation2 executed")
         XCTAssertEqual(msgOperations[3], "Operation4 executed")
     }
+
+    func test_AsyncOperation() {
+        let operation = AsyncOperation()
+        XCTAssertFalse(operation.isExecuting)
+        XCTAssertFalse(operation.isFinished)
+
+        operation.start()
+
+        while !operation.isFinished {
+            // do nothing
+        }
+
+        XCTAssertFalse(operation.isExecuting)
+        XCTAssertTrue(operation.isFinished)
+    }
+}
+
+class AsyncOperation: Operation {
+
+    private let queue = DispatchQueue(label: "async.operation.queue")
+    private let lock = NSLock()
+
+    private var _executing = false
+    private var _finished = false
+
+    override internal(set) var isExecuting: Bool {
+        get {
+            return _executing
+        }
+        set {
+            if _executing != newValue {
+                willChangeValue(forKey: "isExecuting")
+                _executing = newValue
+                didChangeValue(forKey: "isExecuting")
+            }
+        }
+    }
+
+    override internal(set) var isFinished: Bool {
+        get {
+            return _finished
+        }
+        set {
+            if _finished != newValue {
+                willChangeValue(forKey: "isFinished")
+                _finished = newValue
+                didChangeValue(forKey: "isFinished")
+            }
+        }
+    }
+
+    override var isAsynchronous: Bool {
+        return true
+    }
+
+    override func start() {
+        if isCancelled {
+            isFinished = true
+            return
+        }
+
+        isExecuting = true
+
+        queue.async {
+            sleep(1)
+            self.lock.lock()
+            self.isExecuting = false
+            self.isFinished = true
+            self.lock.unlock()
+        }
+    }
+
 }