Repository navigation
fix: support cancellation during sandbox execution - #275
Merged
Merged
Conversation
1 of 7 tasks
baywet
force-pushed
the
fix/sandbox-interruption
branch
from
November 4, 2026 00:37
25eecb7 to
280a15c
Compare
baywet
marked this pull request as ready for review
November 4, 2026 12:05
jsturtevant
added a commit
that referenced
this pull request
Nov 6, 2026
Pin Node 26 in CI and publishing so builds use a supported LTS release rather than the Node 27 alpha selected by `latest`. Node 26 supports the existing manylinux_2_28 libraries, which preserves Linux wheel compatibility while allowing Node 26 patch updates. This addresses the build environment failure seen in #275 without changing its cancellation implementation. Signed-off-by: James Sturtevant <jsturtevant@gmail.com>
jsturtevant
force-pushed
the
fix/sandbox-interruption
branch
from
November 6, 2026 00:23
dfdc4bd to
664f74c
Compare
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
The interruption test can fail despite successful cancellation because kill() may return False while cancellation is pending, so the assertion should check that an attempt occurred rather than require a True result.
1 open issue
What changed in this PR
Adds interruption of running guest code across the Rust backend abstraction and SDK bindings, while preserving the existing .NET Run(string) API.
Changes:
- Exposes independently owned interrupt handles for JavaScript and Wasm backends.
- Connects .NET cancellation tokens and Python cross-thread interruption to native execution, preserving cancellation error types.
- Adds interruption and snapshot-recovery tests, plus documentation of cancellation limits.
| File | Description |
|---|---|
src/wasm_sandbox/src/lib.rs |
Exposes interruption and preserves typed execution errors. |
src/sdk/python/wasm_backend/src/lib.rs |
Adds interrupt handles and releases the GIL during execution. |
src/sdk/python/pyo3_common/src/lib.rs |
Shares Python interrupt bindings and error translation. |
src/sdk/python/hyperlight_js_backend/src/lib.rs |
Adds interrupt handles and releases the GIL during execution. |
src/sdk/python/core/tests/test_interruption.py |
Tests cross-thread interruption, handle lifetime, and recovery. |
src/sdk/python/core/hyperlight_sandbox/__init__.py |
Exposes and documents the public Python interruption API. |
src/sdk/dotnet/README.md |
Documents cancellation APIs, limits, and recovery. |
src/sdk/dotnet/ffi/src/lib.rs |
Exports interrupt-handle operations and classifies cancellation errors. |
src/sdk/dotnet/core/Tests/HyperlightSandbox.Tests/PInvokeLayerTests.cs |
Tests cancellation error mapping and late-cancellation behavior. |
src/sdk/dotnet/core/Tests/HyperlightSandbox.Tests/JavaScriptBackendTests.cs |
Tests JavaScript cancellation, recovery, and registration isolation. |
src/sdk/dotnet/core/Tests/HyperlightSandbox.Tests/IntegrationTests.cs |
Tests Python/Wasm cancellation and snapshot recovery. |
src/sdk/dotnet/core/PInvoke/SafeNativeMethods.cs |
Declares native interruption functions. |
src/sdk/dotnet/core/PInvoke/SafeHandles.cs |
Manages native interrupt-handle ownership. |
src/sdk/dotnet/core/PInvoke/FFIResult.cs |
Maps native cancellation to token-aware exceptions. |
src/sdk/dotnet/core/PInvoke/FFIErrorCode.cs |
Adds the native cancellation error code. |
src/sdk/dotnet/core/Api/Sandbox.cs |
Implements cancellable execution with interruption-worker cleanup. |
src/javascript_sandbox/src/lib.rs |
Exposes interruption and propagates cancellation and poison errors. |
src/hyperlight_sandbox/src/lib.rs |
Adds the shared interrupt handle and backend interface. |
src/hyperlight_sandbox/Cargo.toml |
Adds the native host dependency for interruption support. |
Cargo.lock |
Records the shared crate’s new dependency. |
Review effort: Balanced
jsturtevant
force-pushed
the
fix/sandbox-interruption
branch
from
November 6, 2026 06:54
220d7bc to
310e46c
Compare
jsturtevant
added a commit
that referenced
this pull request
Nov 6, 2026
Download the shared guest bundle after Rust cache restoration in every consumer job. On partial cache hits, rust-cache cleans target/ and deletes the previously downloaded bundle manifest, causing valid guests to fail verification. Keeping downloads after that cleanup preserves strict verification without rebuilding guests in each job. The regression failed in CI before the fix. This addresses the cache failure exposed by #275; it does not change cancellation behavior. --------- Signed-off-by: James Sturtevant <jsturtevant@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3d6807e7-70a6-4c43-8dc1-7bab20376cd4 Signed-off-by: Vincent Biret <vibiret@microsoft.com> Signed-off-by: James Sturtevant <jsturtevant@gmail.com>
Signed-off-by: James Sturtevant <jsturtevant@gmail.com>
Signed-off-by: James Sturtevant <jsturtevant@gmail.com>
Signed-off-by: James Sturtevant <jsturtevant@gmail.com>
Signed-off-by: James Sturtevant <jsturtevant@gmail.com>
Signed-off-by: James Sturtevant <jsturtevant@gmail.com>
Signed-off-by: James Sturtevant <jsturtevant@gmail.com>
jsturtevant
force-pushed
the
fix/sandbox-interruption
branch
from
November 6, 2026 14:54
5306728 to
b443b73
Compare
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Error wrapping hides native failure details and can cause misclassification, while an integration test fails without a built Python guest and Rust cancellation documentation omits pending-cancellation behavior.
3 open issues
1 resolved since last review
Review effort: Max
2 of 7 tasks
Contributor
jsturtevant
approved these changes
Nov 6, 2026
Refactor cancellation handling and error messaging in guest execution. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: James Sturtevant <jsturtevant@gmail.com>
Signed-off-by: James Sturtevant <jsturtevant@gmail.com>
jsturtevant
force-pushed
the
fix/sandbox-interruption
branch
from
November 6, 2026 17:44
a285a01 to
7dca6ab
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Summary
Related to #9.
Expose independently owned native interruption handles through the Rust backend abstraction and .NET FFI. Add
Sandbox.Run(string, CancellationToken)and makeRunAsyncforward cancellation into running guest execution, while preservingRun(string).Cancellation uses a dedicated interruption worker, retries the transition into guest execution, and waits for native execution and cancellation work to finish before returning. The callback never accesses mutable sandbox state or acquires the execution gate. Existing snapshot/restore behavior permits reuse after interruption.
There is no implicit SDK deadline: hosts can supply a timeout-backed
CancellationTokenSource. Runtime initialization and host callbacks cannot be interrupted; host tools need their own cancellation policy.Validation
Executed in Ubuntu x64 containers on a KVM-capable host using .NET SDK 10.0.403 and Rust 1.94. The net8.0 tests ran with
DOTNET_ROLL_FORWARD=Major.dotnet vstest src/sdk/dotnet/core/Tests/HyperlightSandbox.Tests/bin/Debug/net8.0/HyperlightSandbox.Tests.dll --TestCaseFilter:FullyQualifiedName~JavaScript_RunAsync_CancellationDuringExecution_StopsGuestAsync --Logger:"console;verbosity=normal"failed as expected after a genuine warm-up and five seconds of guest execution: expectedOperationCanceledException, but none was thrown.just wasm guest-build— passed, building the actual Python/Wasm guest.just fmtandjust fmt-check— passed.just lint-rustandjust dotnet lint-rust— passed.dotnet build src/sdk/dotnet/core/Tests/HyperlightSandbox.Tests/HyperlightSandbox.Tests.csproj --nologo -p:TreatWarningsAsErrors=true— passed, zero warnings/errors.just test-rust— passed, 144 unit tests and 5 HTTP integration tests; one existing documentation example remains ignored.just dotnet test-rust— passed, 78 FFI tests.dotnet vstest src/sdk/dotnet/core/Tests/HyperlightSandbox.Tests/bin/Debug/net8.0/HyperlightSandbox.Tests.dll --Logger:"console;verbosity=normal"— passed, all 147 tests, including real JavaScript immediate/timer cancellation and recovery, pre-canceled execution, isolation from completed cancellation registrations, and Python/Wasm interruption followed by snapshot restoration.The first Rust HTTP run failed because a network-disabled container could not resolve the existing HTTPS test's destination. The full Rust suite passed when run with its required networking. FFI and .NET guest tests remained resource-limited and network-disabled.
Windows execution, the Python SDK suite, and package publishing were not run locally; upstream CI remains pending. This PR must remain a draft.
AI assistance
Copilot assisted with implementation, tests, documentation, and this description.