ProcedureKit: Completion block isn't called in ~0.1% of cases when a condition is attached.
If you have a operation and prevent it from running by a failed condition, the completion callback isn’t always called. Try the following with an iOS test target:
func test__multiple_operations_completion_blocks() {
let q = OperationQueue()
(0...5000).forEach { i in
let e = expectationWithDescription("wait\(i)")
let operation = BlockOperation(block: { _ in XCTFail() })
operation.addCompletionBlock({ e.fulfill() })
let condition = BlockCondition(block: { false })
operation.addCondition(condition)
q.addOperation(operation)
}
waitForExpectationsWithTimeout(8, handler: nil)
}
I see about 1-10 expections unfulfilled. Do I use conditions the wrong way?
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Comments: 18 (17 by maintainers)
Commits related to this issue
- Add conditions evaluation operation. Previously condition evaluation would happen on Operation level. However due to bug discovered in NSOperation we cannot safely manipulate NSOperation.isReady. Th... — committed to pronebird/Operative by deleted user 8 years ago
- Add conditions evaluation operation. Previously condition evaluation would happen on Operation level. However due to bug discovered in NSOperation we cannot safely manipulate NSOperation.isReady. The... — committed to pronebird/Operative by deleted user 8 years ago
This is now fixed in
development😀A massive thanks to @pronebird who did the bulk of the work in actually figuring out the issue was due to
isReadygetting overridden.The change, which refactors operation conditions as
ConditionanOperationand executed as a dependency may require some refactoring.Operationwill accept the old styleOperationConditionbut you will find that dependencies for the condition are added / queried when the condition is attached, which is different than from before.@pronebird okay, so, it looks like adding some asynchronous logic around
readycauses issues - either due to conditions (asynchronous evaluator) or mutual exclusivity (asynchronous exclusion lock). Will try to find the time to debug it more this week.I made a sample project that somewhat resembles a very simple version of Operations.
https://github.com/pronebird/NSOperationReproducingRaceCondition
There is no problem as long as operation transitions to
.Readyoncesuper.isReadyturns true. However, everything breaks if operation transitions to.Readyanytime later (think evaluator).That seems to be the case with Operations too, because Operations switches to
.Readyfrom withinisReadyif there are no any conditions set. I just ran a long test without any conditions and everything works fine.Besides, I looked at
__NSOperationInternalinternals and I don’t see any thread synchronization there except when they extract the reference to NSOperationQueue assigned to operation.