Skip to content

Commit 9f44ed3

Browse files
committed
Fix OperationQueue scheduling logic to prevent disruption of operation lists
I believe the original intention was to adust `__nextPriorityOperation`. Doing so for `__nextOperation` has no sense and leads to crosslinking of the main operation list and corresponding priority operation list. OperationQueue in such state would crash or hang while attempting to schedule subsequent operation.
1 parent 7d521e4 commit 9f44ed3

File tree

2 files changed

+28
-1
lines changed

2 files changed

+28
-1
lines changed

Sources/Foundation/Operation.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -993,7 +993,7 @@ open class OperationQueue : NSObject, ProgressReporting {
993993
// if the cached state is possibly not valid then the isReady value needs to be re-updated
994994
if Operation.__NSOperationState.enqueued == operation._state && operation._fetchCachedIsReady(&retest) {
995995
if let previous = prev?.takeUnretainedValue() {
996-
previous.__nextOperation = next
996+
previous.__nextPriorityOperation = next
997997
} else {
998998
_setFirstPriorityOperation(prio, next)
999999
}

Tests/Foundation/Tests/TestOperationQueue.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class TestOperationQueue : XCTestCase {
3434
("test_CancelWhileSuspended", test_CancelWhileSuspended),
3535
("test_OperationOrder", test_OperationOrder),
3636
("test_OperationOrder2", test_OperationOrder2),
37+
("test_ExecutionOrder", test_ExecutionOrder),
3738
("test_WaitUntilFinished", test_WaitUntilFinished),
3839
("test_OperationWaitUntilFinished", test_OperationWaitUntilFinished),
3940
("test_CustomOperationReady", test_CustomOperationReady),
@@ -531,6 +532,32 @@ class TestOperationQueue : XCTestCase {
531532
XCTAssertEqual(array, [5, 4, 3, 2, 1])
532533
}
533534

535+
func test_ExecutionOrder() {
536+
let queue = OperationQueue()
537+
538+
let didRunOp1 = expectation(description: "Did run first operation")
539+
let didRunOp1Dependency = expectation(description: "Did run first operation dependency")
540+
let didRunOp2 = expectation(description: "Did run second operation")
541+
var didRunOp1DependencyFirst = false
542+
543+
let op1 = BlockOperation {
544+
didRunOp1.fulfill()
545+
XCTAssertTrue(didRunOp1DependencyFirst, "Dependency should be executed first")
546+
}
547+
let op1Dependency = BlockOperation {
548+
didRunOp1Dependency.fulfill()
549+
didRunOp1DependencyFirst = true
550+
}
551+
op1.addDependency(op1Dependency)
552+
queue.addOperations([op1, op1Dependency], waitUntilFinished: false)
553+
554+
queue.addOperation {
555+
didRunOp2.fulfill()
556+
}
557+
558+
waitForExpectations(timeout: 1.0)
559+
}
560+
534561
func test_WaitUntilFinished() {
535562
let queue1 = OperationQueue()
536563
let queue2 = OperationQueue()

0 commit comments

Comments
 (0)