Repository navigation
Preserve async cancellation on task completion - #3234
dajiaohuang wants to merge 4 commits into
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (1)
Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 2 remain after this review. 📝 WalkthroughWalkthroughChangesThe completion setters in Async cancellation
Priority: ➖ Normal Estimated code review effort: 2 (Simple) | ~8 minutes Change: Bug fix · Severity of issue fixed: Medium Merge Risk: ⚪ Minimal · up to A task that is successfully cancelled while running retains its cancelled state when its work later completes; no actionable merge-blocking risk remains. Architecture SummaryArchitecture risk: 🔵 Low · up to The change affects 1 system. Changed systems: Architecture concerns Review detailsSystems and components
Before / after behavior
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. A rabbit checks the task’s small gate, Comment |
khustup2
left a comment
There was a problem hiding this comment.
The guard changes a core semantic of the async layer, so it needs more than a source review:
- Which caller observed set_value() after cancel()? Give the call path.
- Today that path hits ASSERT(!has_result) in debug builds. Is the intended behavior to drop the value silently, or to assert? Explain why silent drop is correct for every caller.
- on_result_updated() is skipped when cancelled. Confirm no waiter depends on that notification after a cancel, or show that cancel() already notified them.
- Add a unit test in the async test suite: cancel a promise, then set_value, then assert is_cancelled() and that no callback fires.
|
Confirmed the real call path: dataset API and PostgreSQL refresh/read callers use Added a threaded test using the actual producer/handle/result/promise headers. Four cancelled-producer cases, normal completion, and future readiness pass. Restoring only the pre-fix handle header reproduces resurrection and exits 134. This is a standalone header test with queue linkage isolated, not a complete queue/extension suite. For waiters, actual |
|
Added explicit public-API coverage in 4ccb3a2: construct an async::promise from its producer handle, register a callback, call promise.cancel(), then supply the late set_value. The test asserts the promise remains cancelled, is not ready, and neither the original callback nor a later subscriber runs. The get_future case now also supplies a late value after cancellation and verifies broken_promise readiness remains intact. The updated native C++20 executable passes these cases plus the four running-producer cases and normal completion. Restoring the pre-fix handle header still aborts in the running-producer regression (exit 134); the fixed header passes (exit 0). That negative control belongs to the running-producer case, not a claim that each public-API control independently fails on old code. Queue linkage remains explicitly isolated; no actual scheduler or complete-extension execution is claimed. |
khustup2
left a comment
There was a problem hiding this comment.
Confirmed against the current source. has_result() is result_.has_value() && result_->fulfilled(), and a cancelled handle is not fulfilled (cancelled is index 1 of the result variant; fulfilled is value or exception). So a late set_value()/set_exception() passes ASSERT(!has_result) and overwrites the cancelled state — the resurrection you describe. The four guards are the right fix and your call-path analysis (bg_queue_promise checks cancellation before the producer runs, not during) matches ours. The standalone test under cpp/async/tests will not be picked up by our test tree; we will carry the fix with a gtest in cpp/tests/async. Approving the source change.
Summary
Preserve terminal cancellation when a producer already running later returns a value, returns void, or throws. Result setters check cancellation under the existing result lock. Added a threaded regression against the real async headers and corrected the stale
get_futurecancellation warning.Fixes #3233.
Caller and contract
async::run_on_main/run_on_bgcreate abg_queue_promisethroughimpl::run_; the submitted functor callsbg_queue_promise::call. Real callers include dataset API mutations incpp/deeplake_api/dataset.hppand refresh/read operations incpp/deeplake_pg/table_data_impl.hpp.callchecks cancellation only before entering the producer, so cancellation during that work is a legitimate late-completion path. Queue task removal explicitly does not interrupt an already running task.Dropping that late completion preserves
promise::cancel's callback suppression contract. The previousASSERT(!has_result(...))does not detect this case: cancelled results are not fulfilled. Replacing the result can erase terminal cancellation and admit a later subscriber.Successful cancellation destroys the callback's captured
std::promise;get_futuretherefore becomes ready withstd::future_errc::broken_promise. The existing warning claiming it stayed locked was stale. No new waiter-notification mechanism or future-result contract is introduced.Validation
bg_queue_promise,handle_base,result,promise, locking and Abseil headers.get_futurecancellation/broken-promise readiness.handle_base.hpprestored, the executable aborts because the late result resurrects the cancelled handle (exit 134); the fixed header passes (exit 0).git diff --checkpasses.config.hppand explicitly isolated queue-linkage stubs for an unsubmitted queue ID. Callbacks use the null/inline queue; actual scheduler execution would terminate the test. Real queue scheduling, the complete async suite and the full Deep Lake extension were not exercised.Review follow-up
Removed the standalone native test files requested by review. Production changes are unchanged. Native gtests need integration into cpp/tests/async. Earlier isolated component checks are historical evidence only; full native integration and patched-extension acceptance remain unverified.