Skip to content

Fix BlockOperation with multiple execution blocks #2938

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 19, 2020

Conversation

lxbndr
Copy link
Contributor

@lxbndr lxbndr commented Dec 7, 2020

This is rebased version of #2497. Original request seems abandoned, but changes are useful. All credit goes to @dannliu.

Latest Foundation still has this issue: BlockOperation stores first execution block into the internal array along with a second execution block. As the result:

  • unexpected value is returned by executionBlocks
  • first block will be run twice.

Also @convention(block) specifier in executionBlocks makes dynamic cast crash on return. Looks like it could be safely removed.

@spevans
Copy link
Contributor

spevans commented Dec 7, 2020

@swift-ci test

@spevans
Copy link
Contributor

spevans commented Dec 7, 2020

@lxbndr I ran test_BlockOperationAddExecutionBlock against Catalina native Foundation and the test failed as it looks like OperationQueue can run both blocks concurrently, so sometimes the order was reversed a race-condition meant the array ended up empty.

I modified it as follows and it ran correctly multiple times, I suggest updating the test-case. It also suggests that Darwin may run the blocks differently to Linux.

diff --git a/Tests/Foundation/Tests/TestOperationQueue.swift b/Tests/Foundation/Tests/TestOperationQueue.swift
index 4e55e939..d944f090 100644
--- a/Tests/Foundation/Tests/TestOperationQueue.swift
+++ b/Tests/Foundation/Tests/TestOperationQueue.swift
@@ -702,12 +702,17 @@ class TestOperationQueue : XCTestCase {
     }
 
     func test_BlockOperationAddExecutionBlock() {
-        var msgOperations = [String]()
+        let lock = NSLock()
+        var msgOperations = Set<String>()
         let blockOperation = BlockOperation {
-            msgOperations.append("block1 executed")
+            lock.lock()
+            msgOperations.insert("block1 executed")
+            lock.unlock()
         }
         blockOperation.addExecutionBlock {
-            msgOperations.append("block2 executed")
+            lock.lock()
+            msgOperations.insert("block2 executed")
+            lock.unlock()
         }
         XCTAssert(blockOperation.executionBlocks.count == 2)
         let queue = OperationQueue()

@lxbndr
Copy link
Contributor Author

lxbndr commented Dec 7, 2020

@spevans you're right, there is concurrency issue. Didn't noticed that first, as my run of Darwin compatibility tests was lucky to pass.

As we are modifying original request code anyway, would you mind if I change test to use XCTest expectations instead of manual locking?

@spevans
Copy link
Contributor

spevans commented Dec 7, 2020

I dont mind as long as the test runs ok on Darwin. I had to run it quite a few times to see the failures.

@lxbndr lxbndr force-pushed the unblock-block-operation branch from 122933f to a9664c9 Compare December 7, 2020 20:11
@lxbndr
Copy link
Contributor Author

lxbndr commented Dec 7, 2020

@spevans done. Thanks for pointing the issue!

@spevans
Copy link
Contributor

spevans commented Dec 7, 2020

@swift-ci test

@tomerd tomerd requested review from millenomi and compnerd December 18, 2020 17:28
Steps:
1. Create BlockOperation with an init block.
2. Add another one with addExecutionBlock.
3. Access the executionBlocks property, it contains 3 blocks.

Co-authored-by: Danny Liu <[email protected]>
@lxbndr lxbndr force-pushed the unblock-block-operation branch from a9664c9 to 30ef6af Compare December 19, 2020 12:50
@spevans
Copy link
Contributor

spevans commented Dec 19, 2020

@swift-ci test

1 similar comment
@spevans
Copy link
Contributor

spevans commented Dec 19, 2020

@swift-ci test

@spevans spevans merged commit 9966689 into swiftlang:main Dec 19, 2020
@lxbndr lxbndr deleted the unblock-block-operation branch December 20, 2020 00:43
@lxbndr
Copy link
Contributor Author

lxbndr commented Dec 20, 2020

@spevans should I port it to release branches?

@spevans
Copy link
Contributor

spevans commented Dec 20, 2020

If its a bug fix then yes its fine to port to the release branches.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants