Unverified Commit b318179f authored by Kenton Varda's avatar Kenton Varda Committed by GitHub

Merge pull request #830 from capnproto/fix-exclusiveJoin

Fix exclusiveJoin() bug when both branches complete simultaneously.
parents cf34b937 23901780
...@@ -809,5 +809,22 @@ TEST(Async, Poll) { ...@@ -809,5 +809,22 @@ TEST(Async, Poll) {
paf.promise.wait(waitScope); paf.promise.wait(waitScope);
} }
KJ_TEST("exclusiveJoin both events complete simultaneously") {
// Previously, if both branches of an exclusiveJoin() completed simultaneously, then the parent
// event could be armed twice. This is an error, but the exact results of this error depend on
// the parent PromiseNode type. One case where it matters is ArrayJoinPromiseNode, which counts
// events and decides it is done when it has received exactly the number of events expected.
EventLoop loop;
WaitScope waitScope(loop);
auto builder = kj::heapArrayBuilder<kj::Promise<uint>>(2);
builder.add(kj::Promise<uint>(123).exclusiveJoin(kj::Promise<uint>(456)));
builder.add(kj::NEVER_DONE);
auto joined = kj::joinPromises(builder.finish());
KJ_EXPECT(!joined.poll(waitScope));
}
} // namespace } // namespace
} // namespace kj } // namespace kj
...@@ -945,14 +945,19 @@ bool ExclusiveJoinPromiseNode::Branch::get(ExceptionOrValue& output) { ...@@ -945,14 +945,19 @@ bool ExclusiveJoinPromiseNode::Branch::get(ExceptionOrValue& output) {
} }
Maybe<Own<Event>> ExclusiveJoinPromiseNode::Branch::fire() { Maybe<Own<Event>> ExclusiveJoinPromiseNode::Branch::fire() {
// Cancel the branch that didn't return first. Ignore exceptions caused by cancellation. if (dependency) {
if (this == &joinNode.left) { // Cancel the branch that didn't return first. Ignore exceptions caused by cancellation.
kj::runCatchingExceptions([&]() { joinNode.right.dependency = nullptr; }); if (this == &joinNode.left) {
kj::runCatchingExceptions([&]() { joinNode.right.dependency = nullptr; });
} else {
kj::runCatchingExceptions([&]() { joinNode.left.dependency = nullptr; });
}
joinNode.onReadyEvent.arm();
} else { } else {
kj::runCatchingExceptions([&]() { joinNode.left.dependency = nullptr; }); // The other branch already fired, and this branch was canceled. It's possible for both
// branches to fire if both were armed simultaneously.
} }
joinNode.onReadyEvent.arm();
return nullptr; return nullptr;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment