Skip to content

s3: stage simple-request results through the task pointer instead of a &mut self receiver - #38361

Open
robobun wants to merge 1 commit into
farm/9b0da86b/s3-teardown-abort-by-idfrom
farm/cb4745f3/s3-simple-request-in-flight-access
Open

s3: stage simple-request results through the task pointer instead of a &mut self receiver#38361
robobun wants to merge 1 commit into
farm/9b0da86b/s3-teardown-abort-by-idfrom
farm/cb4745f3/s3-simple-request-in-flight-access

Conversation

@robobun

@robobun robobun commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #38353: the base of this PR is that PR's branch, so the diff here is only this PR's own change (3 files). It lands after #38353, at which point it is rebased onto main.

Problem

  • S3HttpSimpleTask::stage_http_result (src/runtime/webcore/s3/simple_request.rs) is a &mut self method that the HTTP thread runs from http_callback for every result callback of a request that is still out.
  • While it runs, the JS thread may run S3HttpSimpleTask::stop_for_vm_teardown on the same task (dispatched from stop_active_handles, src/runtime/jsc_hooks.rs:1819, for every request still registered: at worker terminate(), at process.exit(), and at the sweep bun test runs between files). With s3: abort in-flight tasks at VM teardown by request id instead of reading task.http #38353 underneath, that function stores into signal_store.aborted and reads async_http_id, both fields of the task allocation.
  • A &mut self argument is a protected exclusive borrow of the whole task for the duration of the call, interior-mutable bytes included (Background), so that store during the call is undefined behaviour under both of Rust's aliasing models, and rustc passes the same claim to LLVM as noalias on the argument. Miri rejects a reduction of the shape at the teardown store: Tree Borrows, this foreign write access would cause the protected tag ... to become Disabled; protected tags must never be Disabled, pointing at &mut self; Stacked Borrows, would remove [Unique ...] which is strongly protected (output below).
  • The HTTP thread's other hold on signal_store has the same problem one level down: signals::Store::to and to_with_backpressure (src/http/Signals.rs) took &mut self to hand out the flags' addresses, so every pointer the HTTP client reads the abort flag through (Signals) descended from an exclusive borrow of a store the owner goes on storing into. Under Stacked Borrows the owner's later store invalidates those pointers; under Tree Borrows an unprotected &mut over interior-mutable bytes happens to tolerate it. This applies to all three owners of a Store (both S3 tasks and FetchTasklet).
  • Natively benign today: stage_http_result never re-reads the flag and the Signals pointers are only used for atomic loads, so nothing observable goes wrong. There is no runtime repro; these are contract fixes found by analysis. The sibling findings on these task types are s3: reach the streaming download task through its raw pointer on both threads #38351 (the streaming task's &mut self methods) and s3: abort in-flight tasks at VM teardown by request id instead of reading task.http #38353 (stop_for_vm_teardown reading http); this PR is the remaining piece, on top of s3: abort in-flight tasks at VM teardown by request id instead of reading task.http #38353.

Fix

  • stage_http_result becomes unsafe fn stage_http_result(this: *mut Self, ..) and reaches the task through (*this).field, so each reference it forms covers one of result, response_buffer, http for one statement and the two fields teardown touches are never inside one. http_callback calls Self::stage_http_result(this, ..). The body is otherwise the same statements in the same order ((*this).result = .. still drops the old value as self.result = .. did). This is the shape the file's other in-flight functions already have (release_at_shutdown, stop_for_vm_teardown, and schedule from s3: abort in-flight tasks at VM teardown by request id instead of reading task.http #38353), and the tree's convention for objects another thread can touch during the call (src/CLAUDE.md, "Pointer provenance at FFI boundaries").
  • The # Safety text of both functions is written once, against the post-s3: abort in-flight tasks at VM teardown by request id instead of reading task.http #38353 state: the concurrent toucher is stop_for_vm_teardown, and what it touches is signal_store and async_http_id. That set is checkable by listing who holds a pointer to a live task: the HTTP callback context (HTTP thread), the active-handle registry (read only by stop_active_handles), and the task queue, which receives the task only from the final callback, after the HTTP thread's last access (on_response runs from there, src/runtime/dispatch.rs:365, or its teardown release at :1235); grep -rn S3HttpSimpleTask src shows no other holder.
  • Store::to and to_with_backpressure take &self. They only take the fields' addresses, so nothing needed the exclusive borrow; the four callers compile unchanged. This is the same class as the receiver (a reference over signal_store that the owner's later stores conflict with), so it rides in this PR rather than a fourth one; it has no test of its own because it changes no behaviour, and the lint below is what this PR's gate rests on.
  • Considered instead, and rejected: giving Store its own allocation so that a &mut over a task never covers the flags. It would not make an in-flight &mut self method sound: teardown would still have to read the store's pointer out of the task, and a foreign read of bytes under a protected &mut, followed by the method's own writes, is undefined behaviour under the same rules as the store into them is. http_callback would also still have to be pointer-shaped, since its post lets the JS thread free the task before it returns. So the receiver rule is needed either way, and the extra allocation per request would buy nothing.
  • Lint: instead of a new file, the S3 task lint from s3: abort in-flight tasks at VM teardown by request id instead of reading task.http #38353 (test/internal/source-lints/s3-task-http-field.test.ts, which already scans the two task files and attributes code to functions) gains the second rule of the same window: the functions that run on an in-flight S3HttpSimpleTask (http_callback, stage_http_result, release_at_shutdown, stop_for_vm_teardown) must take the task as a raw pointer. It checks that the listed functions are still declared (a rename cannot drop one out silently) and its parsing against banned and allowed spellings. The simple task's post-hand-back helpers (error_with_body, fail_if_contains_error, release_portable, Drop) keep their receivers, which is why this is a list; the streaming task has no post-hand-back phase, so its rule is every method, which is s3: reach the streaming download task through its raw pointer on both threads #38351's lint. Against the base (s3: abort in-flight tasks at VM teardown by request id instead of reading task.http #38353's source) the new rule reports exactly simple_request.rs:416: fn stage_http_result(&mut self, ..) and s3: abort in-flight tasks at VM teardown by request id instead of reading task.http #38353's own checks still pass; with this change everything passes (verified both ways with git stash push -- src/).
  • Verified on the debug build of this stack: the whole test/internal/source-lints/ directory (105 tests); the credential-free S3 files (s3-connection-close, s3-storage-class, s3-requester-pays, s3-insecure, s3-list-checksum-algorithm, s3-list-encode-overflow, s3-stream-cancel-leak, s3-stream-error-gc, s3-write-to-file-sync-close, s3-argument-validation: 56 tests); the five "VM teardown ordering" tests in worker_threads.test.ts, including s3: abort in-flight tasks at VM teardown by request id instead of reading task.http #38353's two, which terminate a worker with requests in flight; fetch's abort tests (fetch-abort-queued, fetch-abort-socket-close-race, fetch-abort-stream-body, abort-signal-leak, the last needing a longer timeout than 5 s on debug as on the base), since fetch is a Store::to_with_backpressure caller; cargo clippy --no-deps on bun_http and bun_runtime and rustfmt clean. The Miri reduction below passes in the fixed shape under both models; it is not committed because it would exercise a copy of the shape, which the lint pins in the real file.
  • Not here: FetchTasklet's HTTP-thread callback holds a function-long &mut over the tasklet (from_raw_mut, FetchTasklet.rs:2417) while abort_task runs on the JS thread; that type's cross-thread ownership is being reworked in fetch: encode FetchTasklet's cross-thread ownership in the type system #31745, with fetch: release the FetchTasklet through its raw pointer, not a &mut receiver #37703 converting its release path, so it is left to those.

Background

  • Protected references: under Stacked Borrows and Tree Borrows (the model bun run rust:miri uses), a reference passed as a function argument is protected until the function returns. For &mut T that means no other access at all to the bytes it covers; Tree Borrows gives a protected &mut that strength even over interior-mutable bytes such as atomics (only an unprotected one relaxes them), and only a shared &T leaves interior-mutable bytes open to other threads. A raw pointer asserts nothing, and a reference to one field covers only that field, which is why the fixed function takes *mut Self and borrows per field.
  • Pointers inherit from what they were made through: a raw pointer taken out of a &mut Store is, to the aliasing models, a child of that exclusive borrow, and an access through the original owner that conflicts with the &mut can invalidate the children with it (Stacked Borrows does; Tree Borrows lets an unprotected &mut over interior-mutable bytes survive it). Taking the addresses through &Store removes the exclusive link.
  • S3HttpSimpleTask: one heap object per non-streaming S3 operation (stat, get, put, delete, list, multipart commit/part). The JS thread builds it and schedules it; the HTTP thread stages each result into it and, on the final one, posts the task itself back to the JS thread, which runs on_response, delivers the result and frees it. While it is out it is registered as an active handle so the VM's stop phase can abort it.
  • signal_store / Signals: the task-owned block of atomic flags (aborted among them) and the struct of pointers to them that the HTTP client carries; setting aborted is how the owner asks for the request to fail. The stop phase sets it on the JS thread and asks the HTTP thread to close the socket; the task comes back through the normal final callback.
Miri reduction of the receiver shape (struct with an atomic field; one thread inside the method, the other storing into the atomic during the call)
$ MIRIFLAGS=-Zmiri-tree-borrows cargo miri run --bin receiver          # &mut self, as on main
error: Undefined Behavior: write access through <4352> at alloc678[0x44] is forbidden
  --> src/receiver.rs:58:9
   |
58 |         (*this).aborted.store(true, Ordering::Relaxed);
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
   |
   = help: the accessed tag <4352> is foreign to the protected tag <4316> (i.e., it is not a child)
   = help: this foreign write access would cause the protected tag <4316> (currently Reserved) to become Disabled
   = help: protected tags must never be Disabled
help: the protected tag <4316> was created here, in the initial state Reserved
  --> src/receiver.rs:39:26
   |
39 |     fn stage_http_result(&mut self, async_http: *mut AsyncHttp, body: &[u8]) {
   |                          ^^^^^^^^^

$ MIRIFLAGS=-Zmiri-tree-borrows cargo miri run --bin receiver -- fixed  # this: *mut Self, field places
ok (fixed)

$ cargo miri run --bin receiver                                         # Stacked Borrows, as on main
error: Undefined Behavior: not granting access to tag <4547> because that would remove [Unique for <4507>] which is strongly protected

$ cargo miri run --bin receiver -- fixed
ok (fixed)

The reduction's method does the same three kinds of access as the real one (a Vec append into one field, a plain field assignment, a ptr::write into a MaybeUninit field) and parks inside the call on a static flag, kept outside the struct, until the other thread has stored into the atomic.

Earlier shape of this PR

This PR first went up against main as the receiver change plus its own lint file (s3-simple-task-raw-access.test.ts), without the Signals.rs change. Review of that shape found that it conflicted with #38353 inside this very function (both rewrite its comments), that its # Safety text could only be complete once #38353's http read was gone, that a per-type lint file duplicated the scanning #38353's lint already does, and that the Store::to(&mut self) chain left the HTTP thread's hold on signal_store in the same state the PR was fixing one level up. The current shape is the result: stacked on #38353, lint folded into its file, Signals.rs included.

@coderabbitai

coderabbitai Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@robobun, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 9 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 57944e96-287c-45da-91fb-79fa6d24da65

📥 Commits

Reviewing files that changed from the base of the PR and between 3753c8b and 380c8e3.

📒 Files selected for processing (2)
  • src/runtime/webcore/s3/simple_request.rs
  • test/internal/source-lints/s3-simple-task-raw-access.test.ts

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

@robobun

robobun commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator Author

Status

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I reviewed this PR and didn't find any bugs. Since it rewrites the aliasing contract on a cross-thread unsafe path (&mut self*mut Self with per-field places) and is one of three coordinated sibling PRs (#38351, #38353) with a stated comment conflict, a maintainer sign-off on the reasoning and landing order would still be worthwhile.

What was reviewed:

  • Verified the new body is statement-for-statement identical to the old one — (*this).result = ... still runs drop glue on the old value like self.result = ... did, and the metadata carry-over / ptr::write overwrite are unchanged.
  • Confirmed stage_http_result has no other callers; each (*this).field borrow covers only that field, so signal_store is never inside a live reference.
  • Checked the source-lint regex against the actual declarations (doc comments and S3HttpSimpleTask::release_at_shutdown at the callback registration site don't false-match; each of the four names appears exactly once).
Extended reasoning...

Overview

This PR converts S3HttpSimpleTask::stage_http_result from a &mut self method to an unsafe fn(this: *mut Self, ..) associated function, and updates its sole caller http_callback plus the SAFETY comments on both. It also adds a new source-lint test (test/internal/source-lints/s3-simple-task-raw-access.test.ts) that pins the four in-flight-task functions to raw-pointer receivers and self-tests its own regex against banned/allowed spellings.

Security risks

None user-facing. This is an aliasing-model contract fix in native code: the JS thread's stop_for_vm_teardown can atomically store into signal_store.aborted while the HTTP thread is inside stage_http_result, and a protected &mut self argument covering that byte is UB under Stacked/Tree Borrows and is passed to LLVM as noalias. The fix narrows every reference to a single non-signal_store field per statement. No new attack surface.

Level of scrutiny

High. This is exactly the review guide's most-blocked category: unsafe Rust, cross-thread lifetime, rewritten SAFETY comments. The change is mechanically small and I believe correct — (*this).result = X drops the old value just as self.result = X did; .take(), body_into(&mut ...), and as_mut_ptr() each form a reference to one field only; the ptr::write/ptr::read overwrite is byte-identical. But the correctness argument rests on Tree/Stacked Borrows protector semantics for interior-mutable bytes under a protected &mut, which a maintainer should confirm matches the tree's stated model (src/CLAUDE.md "Pointer provenance at FFI boundaries").

Other factors

  • The PR is one of three coordinated siblings (#38351 for the streaming task, #38353 for stop_for_vm_teardown reading http), and the description flags a small comment merge conflict with #38353 on the lines above the ptr::write. Landing order is a human call.
  • The new lint test references two sibling lints (s3-download-task-raw-access.test.ts, s3-post-through-handle-field.test.ts) that don't exist on this branch — they belong to the sibling PRs. That's a comment-only forward reference, not a load-bearing dependency.
  • The lint's "every in-flight function is still declared" check uses toEqual on sorted arrays, so it also catches a second declaration of the same name (would produce a length mismatch). The full-line-comment strip regex correctly handles /// doc comments and preserves line numbers via [ \t]* (not \s*).
  • No runtime repro exists (the PR says so); the test is a source-shape lint following the existing test/internal/source-lints/ pattern (e.g. self-receiver-reclaim.test.ts), which is the tree's convention for this class of finding.

Comment thread src/runtime/webcore/s3/simple_request.rs Outdated
Comment thread src/runtime/webcore/s3/simple_request.rs Outdated
Comment thread src/runtime/webcore/s3/simple_request.rs Outdated
Comment thread src/runtime/webcore/s3/simple_request.rs Outdated
Comment thread src/runtime/webcore/s3/simple_request.rs
Comment thread src/runtime/webcore/s3/simple_request.rs
@robobun

robobun commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator Author

Two follow-up pushes since the review round, no change to the fix itself:

The CodeRabbit comments so far are rate-limit notices, not reviews.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I reviewed this PR and didn't find any bugs. Because it touches unsafe cross-thread code in the S3 request path and is one of three coordinated PRs (#38351, #38353) with an acknowledged merge conflict between them, a human look would still be worthwhile.

What was reviewed:

  • stage_http_result body is statement-for-statement identical to main; only the receiver shape changed (&mut self*mut Self with (*this).field places), so the change strictly removes an aliasing claim without altering behaviour.
  • http_callback's call site no longer forms &mut *this; the is_done handoff block is unchanged.
  • Traced the new lint's regex against all four in-flight declarations in the file and against its own self-check fixtures — the parser reads each first param correctly, and the referenced sibling lint self-receiver-reclaim.test.ts exists on this branch.
Extended reasoning...

Overview

The PR converts S3HttpSimpleTask::stage_http_result from a &mut self method to unsafe fn(this: *mut Self, ..) so the HTTP-thread callback never holds a protected exclusive borrow of the whole task while the JS thread's stop_for_vm_teardown may concurrently store into the task's signal_store.aborted atomic. The function body is otherwise identical (metadata carry-over, body_into, detach_lifetime, ptr::write — same statements, same order); http_callback's call site changes from (*this).stage_http_result(..) (which implicitly formed &mut *this) to Self::stage_http_result(this, ..). SAFETY comments on both functions are reworded to stop claiming exclusive ownership. A new source-lint test (test/internal/source-lints/s3-simple-task-raw-access.test.ts) pins the four in-flight functions to raw-pointer receivers, guards against renames, and self-checks its regex.

Security risks

None. This is an internal receiver-shape change on the S3 client's HTTP-thread callback; no user-facing surface, no input validation, no auth/crypto path is touched.

Level of scrutiny

High — this is unsafe Rust in a cross-thread path, which REVIEW.md flags as the most-blocked category. That said, the change is strictly a narrowing of what memory a live reference covers: the body semantics are byte-for-byte preserved, and the new shape matches the existing release_at_shutdown / stop_for_vm_teardown in the same file. It can only remove UB, not introduce it. I verified each (*this).field access maps 1:1 to the old self.field access, that the assignment to (*this).result still runs the old value's drop (as self.result = .. did), and that the ptr::write/ptr::read pair is unchanged.

Other factors

  • The comment-cop bot flagged comment length on an earlier revision; the author shortened the contracts in a49340c and all six threads are resolved. What remains is the required # Safety text plus the pre-existing AsyncHTTP hand-back comment moved verbatim from main.
  • The lint test's self-check assertion order matches the IN_FLIGHT_FNS iteration order over matchAll source order; I traced it against the fixture and it holds. The (?!\w) lookahead correctly excludes stage_http_result_for_tests, and full-line // stripping preserves line numbers via [ \t]* (not \s*).
  • This is one of three sibling PRs on the same hazard class; #38353 has a two-line comment conflict with this function. A maintainer coordinating the merge order is the main reason I'm deferring rather than approving.

@robobun

robobun commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator Author

Nothing further to change from the second review pass. State of a49340c: no open review threads; the GitHub Actions jobs (Format, cargo clippy, Source lints, which runs the new lint, Lint JavaScript, comment-cop) are green; Buildkite build 95453 is still running.

@robobun

robobun commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 7:05 AM PT - Aug 14th, 2026

@robobun, your commit cc3aea6 has some failures in Build #95935 (All Failures)


🧪   To try this PR locally:

bunx bun-pr 38361

That installs a local version of the PR into your bun-38361 executable, so you can run:

bun-38361 --bun

@robobun

robobun commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator Author

380c8e3 is an empty retrigger. Build 95453 ran 177 jobs green; its only non-passing jobs were the two darwin 14 aarch64 - test-bun jobs, which expired unassigned, as they did for every build queued in that window (95421 through 95481), and that pool is assigning again. No diff change since a49340c; the CodeRabbit comments remain rate-limit notices.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I reviewed this PR and didn't find any bugs. Because it rewrites concurrent unsafe access on the HTTP-thread side of S3HttpSimpleTask and is one of three coordinated aliasing-model fixes (#38351, #38353), a human look would still be worthwhile.

What was reviewed:

  • stage_http_result body is statement-for-statement identical to main (metadata carry-over, body_into, detach_lifetime, ptr::write) — only the receiver shape changed.
  • Per-field places touched (result, response_buffer.list, http) are disjoint from signal_store, so the concurrent atomic store from stop_for_vm_teardown no longer overlaps a protected borrow.
  • The source-lint's regex correctly reads all four in-flight declarations (including release_at_shutdown(this: *mut ())) and its rename guard uses exact-array equality.
Extended reasoning...

Overview

The PR converts S3HttpSimpleTask::stage_http_result from a &mut self method to unsafe fn stage_http_result(this: *mut Self, ..), updates the one call site in http_callback to pass the raw pointer, and rewords the # Safety contracts on both functions to stop claiming exclusive ownership during the call. It adds a source-lint (test/internal/source-lints/s3-simple-task-raw-access.test.ts) that pins the four functions running on an in-flight task to raw-pointer receivers.

Security risks

None. No user-facing surface, parsing, or trust boundary is touched; the change is a receiver-shape adjustment on an internal HTTP-thread callback.

Level of scrutiny

High. This is native concurrent unsafe Rust in a hand-off path between the HTTP thread and the JS thread, squarely in REVIEW.md's most-blocked category (memory safety, thread affinity). The transformation itself is mechanical and the body is provably the same statements in the same order, but the justification — that a protected &mut self argument covers interior-mutable bytes under Tree Borrows and therefore races with stop_for_vm_teardown's atomic store — is a subtle aliasing-model claim. It is well-argued (Miri reduction, both models) and the fixed shape matches the file's existing release_at_shutdown/stop_for_vm_teardown pattern, but this is the kind of reasoning a maintainer who knows the S3 task lifecycle should confirm rather than take on automated say-so.

Other factors

  • The PR is one of three coordinated siblings (#38351 for the streaming task, #38353 for the http read in stop_for_vm_teardown). A human reviewing any one of them will likely want to see the set together, and #38353 has a known comment conflict with this change.
  • All comment-cop threads on the safety-doc length are resolved; the remaining comments are the required # Safety sections plus the pre-existing AsyncHTTP hand-back block moved inside the new unsafe {}.
  • The lint's self-test covers both the before/after spellings and negative cases (name-prefix, comments), and the rename guard (toEqual on sorted names) prevents a silent drop-out. I checked the [^,()]*(?:\(\))? capture against release_at_shutdown(this: *mut ()) — it reads this: *mut () correctly.
  • No observable runtime behaviour changes, so the risk of regression is low; the value of a human look is confirming the concurrency invariant (only stop_for_vm_teardown touches the task concurrently, and only signal_store) is complete.

@robobun

robobun commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator Author

On the point the review leaves for a human (is stop_for_vm_teardown really the only concurrent access, and signal_store the only thing it touches): the PR description now carries the enumeration. A live task is reachable from exactly three places: the HTTP callback context (HTTP thread), the active-handle registry (read only by stop_active_handles, src/runtime/jsc_hooks.rs:1819, which calls stop_for_vm_teardown), and the task queue, which only receives the task from the final callback, after the HTTP thread's last access (the post goes through a clone of the handle); on_response runs from there (src/runtime/dispatch.rs:365, or its teardown release at :1235). The HTTP client's Signals pointers into signal_store are used on the HTTP thread outside the callback. grep -rn S3HttpSimpleTask src shows no other holder. On main, stop_for_vm_teardown also reads http.async_http_id; that read is #38353's subject and, like the store, now lands outside anything this function borrows.

S3HttpSimpleTask::stage_http_result ran on the HTTP thread as a &mut self
method while the JS thread's stop_for_vm_teardown could store into the
same task's signal_store. Take the task as *mut Self and borrow one field
per statement; the s3 task lint now also checks that the functions which
run on an in-flight simple task take it as a raw pointer.

signals::Store::to and to_with_backpressure only take the flags'
addresses, so they take &self: the pointers the HTTP thread keeps must
not descend from an exclusive borrow of a store the owner goes on
storing into.
@robobun
robobun changed the base branch from main to farm/9b0da86b/s3-teardown-abort-by-id August 14, 2026 11:32
@robobun
robobun force-pushed the farm/cb4745f3/s3-simple-request-in-flight-access branch from 380c8e3 to cc3aea6 Compare August 14, 2026 11:32
Comment thread src/http/Signals.rs
Comment thread src/runtime/webcore/s3/download_stream.rs
Comment thread src/runtime/webcore/s3/download_stream.rs
Comment thread src/runtime/webcore/s3/download_stream.rs
Comment thread src/runtime/webcore/s3/download_stream.rs
Comment thread src/runtime/webcore/s3/download_stream.rs
Comment thread src/runtime/webcore/s3/simple_request.rs
Comment thread src/runtime/webcore/s3/simple_request.rs
Comment thread src/runtime/webcore/s3/simple_request.rs
Comment thread src/runtime/webcore/s3/simple_request.rs
Comment thread src/runtime/webcore/s3/simple_request.rs
Comment thread src/runtime/webcore/s3/simple_request.rs
@robobun

robobun commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator Author

Restructured (head cc3aea6), following a review of the standalone shape:

The description describes the current shape; the earlier one is summarised in a details block at the end. The comment-length threads that just appeared were raised against a diff that includes #38353's commit; the ones on schedule, the struct fields and download_stream.rs are that PR's hunks, and are answered inline here only to say so.

Comment thread test/js/node/worker_threads/worker_threads.test.ts
@robobun
robobun changed the base branch from farm/9b0da86b/s3-teardown-abort-by-id to main August 14, 2026 11:55
@robobun
robobun changed the base branch from main to farm/9b0da86b/s3-teardown-abort-by-id August 14, 2026 11:56
@robobun

robobun commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator Author

Housekeeping: after the base change GitHub kept serving this PR's diff against main for about twenty minutes (six files, including #38353's), which is what the last review round and the comment-length bot were looking at. Re-setting the base fixed it; the PR now shows its own three files (Signals.rs, the two simple_request.rs hunks, the added lint rule), the same set as farm/9b0da86b/s3-teardown-abort-by-id...farm/cb4745f3/s3-simple-request-in-flight-access. The one finding from that round, on #38353's buffered-download test, is relayed to #38353 and the thread here is resolved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant