diff --git a/Foundation/Operation.swift b/Foundation/Operation.swift index ab09c1fd8b..643fcc16e2 100644 --- a/Foundation/Operation.swift +++ b/Foundation/Operation.swift @@ -326,6 +326,7 @@ open class OperationQueue: NSObject { } } let queueGroup = DispatchGroup() + var unscheduledWorkItems: [DispatchWorkItem] = [] #endif var _operations = _OperationList() @@ -364,9 +365,6 @@ open class OperationQueue: NSObject { } } let queue = DispatchQueue(label: effectiveName, attributes: attr) - if _suspended { - queue.suspend() - } __underlyingQueue = queue lock.unlock() return queue @@ -432,13 +430,13 @@ open class OperationQueue: NSObject { _operations.insert(operation) } lock.unlock() - ops.forEach { (operation: Operation) -> Void in #if DEPLOYMENT_ENABLE_LIBDISPATCH + let items = ops.map { (operation: Operation) -> DispatchWorkItem in if let group = waitGroup { group.enter() } - let block = DispatchWorkItem(flags: .enforceQoS) { () -> Void in + return DispatchWorkItem(flags: .enforceQoS) { () -> Void in if let sema = self._concurrencyGate { sema.wait() self._runOperation() @@ -450,10 +448,17 @@ open class OperationQueue: NSObject { group.leave() } } - _underlyingQueue.async(group: queueGroup, execute: block) -#endif } -#if DEPLOYMENT_ENABLE_LIBDISPATCH + + let queue = _underlyingQueue + lock.lock() + if _suspended { + unscheduledWorkItems += items + } else { + items.forEach { queue.async(group: queueGroup, execute: $0) } + } + lock.unlock() + if let group = waitGroup { group.wait() } @@ -498,19 +503,16 @@ open class OperationQueue: NSObject { } set { lock.lock() - if _suspended != newValue { - _suspended = newValue -#if DEPLOYMENT_ENABLE_LIBDISPATCH - if let queue = __underlyingQueue { - if newValue { - queue.suspend() - } else { - queue.resume() - } + _suspended = newValue + let items = unscheduledWorkItems + unscheduledWorkItems.removeAll() + lock.unlock() + + if !newValue { + items.forEach { + _underlyingQueue.async(group: queueGroup, execute: $0) } -#endif } - lock.unlock() } } diff --git a/TestFoundation/TestOperationQueue.swift b/TestFoundation/TestOperationQueue.swift index e78e68634f..06ad6b6c7f 100644 --- a/TestFoundation/TestOperationQueue.swift +++ b/TestFoundation/TestOperationQueue.swift @@ -22,6 +22,7 @@ class TestOperationQueue : XCTestCase { ("test_CurrentQueueOnBackgroundQueueWithSelfCancel", test_CurrentQueueOnBackgroundQueueWithSelfCancel), ("test_CurrentQueueWithCustomUnderlyingQueue", test_CurrentQueueWithCustomUnderlyingQueue), ("test_CurrentQueueWithUnderlyingQueueResetToNil", test_CurrentQueueWithUnderlyingQueueResetToNil), + ("test_isSuspended", test_isSuspended), ] } @@ -155,6 +156,29 @@ class TestOperationQueue : XCTestCase { waitForExpectations(timeout: 1) } + func test_isSuspended() { + let expectation1 = self.expectation(description: "DispatchQueue execution") + let expectation2 = self.expectation(description: "OperationQueue execution") + + let dispatchQueue = DispatchQueue(label: "underlying_queue") + let operationQueue = OperationQueue() + operationQueue.maxConcurrentOperationCount = 1 + operationQueue.underlyingQueue = dispatchQueue + operationQueue.isSuspended = true + + operationQueue.addOperation { + XCTAssert(OperationQueue.current?.underlyingQueue === dispatchQueue) + expectation2.fulfill() + } + + dispatchQueue.async { + operationQueue.isSuspended = false + expectation1.fulfill() + } + + waitForExpectations(timeout: 1) + } + func test_CurrentQueueWithUnderlyingQueueResetToNil() { let expectation = self.expectation(description: "Background execution")