Skip to content
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

Can we break cancellation propagation in when(first:) after promise resolution when cancelRemaining is false? #21

Open
lilyball opened this issue Jun 25, 2018 · 2 comments
Labels
enhancement New feature or request question Further information is requested

Comments

@lilyball
Copy link
Owner

When using when(first:) without cancelRemaining, the returned Promise is considered a child of all of the input Promises. This means it will prevent cancellation propagation from any other child promises of those inputs. We should see if it's reasonable to "detach" the returned Promise from the input promises after the returned Promise has resolved so e.g. it won't prevent cancellation of any outstanding input promises whose other children have cancelled.

We can't simply propagate cancellation at this point because we don't want to cancel the other inputs if we're the only child.

@lilyball
Copy link
Owner Author

Here's a test case that describes the behavior I'm looking for:

    func testWhenAllowCancelPropagationForInputsWithoutCancelRemaining() {
        let sema1 = DispatchSemaphore(value: 1)
        let sema2 = DispatchSemaphore(value: 1)
        sema1.wait()
        sema2.wait()
        // Set up 2 input promises, hang another child off of one, have the 2nd resolve first, then
        // cancel the new child and see if the first propagates cancellation.
        let newChild: Promise<Int,String>
        let promise: Promise<Int,String>
        let promise1CancelExpectation: XCTestExpectation
        do {
            let promise1 = Promise<Int,String>(on: .utility, { (resolver) in
                resolver.onRequestCancel(on: .immediate, { (resolver) in
                    resolver.cancel()
                })
                sema1.wait()
                sema1.signal()
                resolver.fulfill(with: 0xDEADBEEF)
            })
            promise1CancelExpectation = XCTestExpectation(onCancel: promise1)
            let promise2 = Promise<Int,String>(on: .utility, { (resolver) in
                resolver.onRequestCancel(on: .immediate, { (resolver) in
                    resolver.cancel()
                })
                sema2.wait()
                sema2.signal()
                resolver.fulfill(with: 42)
            })
            promise = when(first: [promise1, promise2], cancelRemaining: false)
            newChild = promise1.then(on: .utility, { $0 })
        }
        let expectation = XCTestExpectation(onSuccess: promise, expectedValue: 42)
        sema2.signal()
        wait(for: [expectation], timeout: 1)
        XCTAssertNil(newChild.result)
        newChild.requestCancel()
        wait(for: [promise1CancelExpectation], timeout: 1)
    }

@lilyball lilyball added enhancement New feature or request question Further information is requested labels Jun 25, 2018
@lilyball
Copy link
Owner Author

This looks very non-trivial to do.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request question Further information is requested
Projects
None yet
Development

No branches or pull requests

1 participant