Skip to content

Engine-level cancellation safepoints; remove cancellation_requested sysop#3158

Merged
antoniosarosi merged 8 commits intocanaryfrom
codex/cancellation-safepoints
Feb 22, 2026
Merged

Engine-level cancellation safepoints; remove cancellation_requested sysop#3158
antoniosarosi merged 8 commits intocanaryfrom
codex/cancellation-safepoints

Conversation

@antoniosarosi
Copy link
Copy Markdown
Contributor

@antoniosarosi antoniosarosi commented Feb 21, 2026

centralize cancellation checks in bex_engine via a safepoint helper in the VM event loop
add safepoint checks at VM loop boundaries and ScheduleFuture boundaries so cancelled calls do not dispatch new sysops
remove baml.sys.cancellation_requested() from builtins, native sysop impl, and LLM orchestration BAML
update cancellation test coverage to drop the removed sysop cases
refresh baml_std snapshots for lexer/parser/HIR/MIR/codegen changes

Validation
cargo test -p bex_engine --test cancellation -- --nocapture
cargo test -p bex_engine --test orchestration -- --nocapture
cargo test -p baml_tests __baml_std -- --nocapture

Summary by CodeRabbit

  • Changes

    • Refactored cancellation handling to use centralized safepoints instead of per-path checks, improving consistency across orchestration workflows.
    • Removed the cancellation_requested() API function; cancellation is now managed entirely by the engine.
  • Tests

    • Removed tests for deprecated cancellation query functionality.

@vercel
Copy link
Copy Markdown

vercel Bot commented Feb 21, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
beps Ready Ready Preview, Comment Feb 22, 2026 2:24am
promptfiddle Ready Ready Preview, Comment Feb 22, 2026 2:24am

Request Review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 21, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR refactors cancellation handling in the BAML engine from distributed per-path checks to centralized engine-level safepoints. Distributed cancellation guards are removed from LLM orchestration paths and the cancellation_requested() syscall is eliminated. Centralized cancellation_safepoint() and AbortHandlesGuard primitives are introduced in the VM engine to coordinate cancellation uniformly.

Changes

Cohort / File(s) Summary
LLM Orchestration Cleanup
baml_language/crates/baml_builtins/baml/llm.baml
Removed per-attempt and per-subcall cancellation checks in execute_client, execute_client_once (Primitive/Fallback/RoundRobin paths), and call_llm_function; cancellation now defers to centralized engine-level handling.
API Removals
baml_language/crates/baml_builtins/src/lib.rs, baml_language/crates/sys_native/src/lib.rs
Removed cancellation_requested() syscall (sys_op returning bool) and its NativeSysOps implementation; eliminates public API for querying cancellation status.
Engine Centralization
baml_language/crates/bex_engine/src/lib.rs
Introduced AbortHandlesGuard RAII wrapper for async task abort handles; added private abort_inflight_tasks() and cancellation_safepoint() methods to handle cancellation atomically at engine level; integrated safepoints throughout VM event loop entry, sys_op execution boundaries, and VM state transitions.
Test Updates
baml_language/crates/bex_engine/tests/cancellation.rs
Removed two tests exercising cancellation_requested sync path and pre-cancel behavior; renumbered and reflowed remaining test suite entries to consecutive sequence.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main changes: introducing engine-level cancellation safepoints and removing the cancellation_requested sysop, which aligns with the core objectives of the PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/cancellation-safepoints

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Feb 22, 2026

Binary size checks passed

7 passed

Artifact Platform Gzip Baseline Delta Status
bridge_cffi Linux 3.8 MB 4.0 MB -155.2 KB (-3.9%) OK
bridge_cffi-stripped Linux 2.3 MB 2.1 MB +168.9 KB (+7.9%) OK
bridge_cffi macOS 3.1 MB 3.3 MB -155.6 KB (-4.7%) OK
bridge_cffi-stripped macOS 1.9 MB 1.7 MB +137.2 KB (+8.0%) OK
bridge_cffi Windows 3.1 MB 3.3 MB -156.1 KB (-4.8%) OK
bridge_cffi-stripped Windows 1.9 MB 1.8 MB +144.6 KB (+8.2%) OK
bridge_wasm WASM 1.8 MB 1.7 MB +52.7 KB (+3.0%) OK

Generated by cargo size-gate · workflow run

coderabbitai[bot]
coderabbitai Bot previously requested changes Feb 22, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
baml_language/crates/bex_engine/src/lib.rs (1)

1075-1136: 🧹 Nitpick | 🔵 Trivial

Redundant safepoints in ScheduleFuture arm.

The safepoint at line 1089 (after execute_sys_op) already guards entry into both Ready and Async branches. The subsequent checks at lines 1093 and 1110 can never observe a newly-cancelled token because no .await point intervenes between them. Consider removing the per-branch checks to reduce noise.

♻️ Proposed simplification
                     let sys_op_result =
                         self.execute_sys_op(pending.operation, &args, call_id, cancel);
                     Self::cancellation_safepoint(cancel, &abort_handles)?;

                     match sys_op_result {
                         SysOpResult::Ready(result) => {
-                            Self::cancellation_safepoint(cancel, &abort_handles)?;
-
                             // Sync operation - set future to Ready without touching stack.
                             ...
                         }
                         SysOpResult::Async(fut) => {
-                            Self::cancellation_safepoint(cancel, &abort_handles)?;
-
                             // Async operation — wrap in Abortable and spawn.
                             ...
                         }
                     }

Comment thread baml_language/crates/bex_engine/src/lib.rs Outdated
Comment thread baml_language/crates/bex_engine/src/lib.rs
coderabbitai[bot]
coderabbitai Bot previously requested changes Feb 22, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Comment thread baml_language/crates/bex_engine/src/lib.rs Outdated
Comment thread baml_language/crates/bex_engine/src/lib.rs Outdated
@antoniosarosi antoniosarosi dismissed stale reviews from coderabbitai[bot] and coderabbitai[bot] February 22, 2026 00:21

Dismissing stale CodeRabbit CHANGES_REQUESTED after follow-up commits resolved all review threads and addressed actionable findings.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
baml_language/crates/bex_engine/src/lib.rs (1)

1033-1061: ⚠️ Potential issue | 🟡 Minor

FunctionEnd event is never emitted under "cancel wins" semantics, orphaning the FunctionStart.

Previously, the Complete arm was the only guarantee that FunctionEnd would always be emitted (non-Complete exits on error paths already skipped it). This PR's "cancel wins" safepoint at Line 1037 introduces a new code path where a VM that has fully computed its result still does not emit FunctionEnd, leaving a FunctionStart without a paired FunctionEnd in the event store.

If downstream consumers (e.g., bex_events::event_store::events_for_span, collectors) assume the Complete path always produces a FunctionEnd, they will now silently receive an incomplete event stream for cancellations that race with VM completion.

Consider emitting a FunctionEnd with a cancellation-indicating result before returning Err(Cancelled), or document that consumers must handle unpaired spans:

📌 One approach — emit FunctionEnd before returning Cancelled
 VmExecState::Complete(value) => {
     // "Cancel wins" semantics: if cancellation races with a
     // completed VM step, report `Cancelled` rather than
     // returning a success value.
-    Self::cancellation_safepoint(cancel, &abort_handles)?;
-
-    // Emit FunctionEnd for the root entry-point span if tracing
-    if let Some(state) = span_state.as_mut() {
+    let cancelled = cancel.is_cancelled();
+    if cancelled {
+        Self::abort_inflight_tasks(&abort_handles);
+    }
+
+    // Always emit FunctionEnd so consumers see paired span events.
+    if let Some(state) = span_state.as_mut() {
         if let Some(root_span) = state.stack.pop() {
             ...
             bex_events::event_store::emit(end_event);
         }
     }
+
+    if cancelled {
+        return Err(EngineError::Cancelled);
+    }
+
     return self.heap.with_gc_protection(...);
 }

Comment thread baml_language/crates/bex_engine/src/lib.rs Outdated
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Feb 22, 2026

Merging this PR will improve performance by 12.48%

⚠️ Unknown Walltime execution environment detected

Using the Walltime instrument on standard Hosted Runners will lead to inconsistent data.

For the most accurate results, we recommend using CodSpeed Macro Runners: bare-metal machines fine-tuned for performance measurement consistency.

⚡ 7 improved benchmarks
✅ 8 untouched benchmarks
⏩ 91 skipped benchmarks1

Performance Changes

Mode Benchmark BASE HEAD Efficiency
WallTime bench_incremental_no_change 121.5 µs 109.9 µs +10.53%
WallTime bench_incremental_close_string 1,022 µs 914 µs +11.82%
WallTime bench_incremental_add_attribute 1,018.3 µs 914.9 µs +11.3%
WallTime bench_incremental_add_user_field 1.1 ms 1 ms +10.14%
WallTime bench_single_simple_file 1,037.7 µs 930 µs +11.58%
WallTime bench_incremental_add_string_char 1,024.7 µs 911 µs +12.48%
WallTime bench_empty_project 957.9 µs 853.7 µs +12.2%

Comparing codex/cancellation-safepoints (7adc0c4) with canary (a91edc3)

Open in CodSpeed

Footnotes

  1. 91 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

coderabbitai[bot]
coderabbitai Bot previously requested changes Feb 22, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Comment thread baml_language/crates/baml_tests/Cargo.toml Outdated
Comment thread baml_language/crates/baml_tests/src/bytecode_snapshot.rs Outdated
Comment thread baml_language/crates/baml_tests/src/bytecode_snapshot.rs Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
baml_language/crates/bex_engine/src/lib.rs (1)

1126-1152: 🧹 Nitpick | 🔵 Trivial

Safepoints at lines 1134 and 1152 are redundant with the one at line 1129.

Between the post-execute_sys_op safepoint (line 1129) and the Ready/Async match arms (lines 1134, 1152), no async work, blocking, or significant computation occurs — just pattern matching. A concurrent cancellation in that window is caught at the next loop-top safepoint anyway.

These extra checks cost only an atomic load each, so they're harmless. Just noting the redundancy in case you want to trim.

Comment thread baml_language/crates/bex_engine/src/lib.rs Outdated
@antoniosarosi antoniosarosi added this pull request to the merge queue Feb 22, 2026
Merged via the queue into canary with commit f61c4f6 Feb 22, 2026
43 checks passed
@antoniosarosi antoniosarosi deleted the codex/cancellation-safepoints branch February 22, 2026 02:24
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.

1 participant