Add blockConcurrencyWhile binding for Durable Objects - #1039
Conversation
Merging this PR will not alter performance
|
Bind the runtime's blockConcurrencyWhile via State::block_concurrency_while (errors reset the object) and block_concurrency_while_infallible (errors returned as values). Addresses cloudflare#177; a literal async constructor remains unsupported, so document the lazy-init handler pattern.
The gate now closes as a side effect of the call rather than on first poll of the returned future, so calling it un-awaited from new() gates all event delivery during async initialization, matching the JavaScript constructor idiom. The _infallible variant is dropped: errors as values are expressed by returning them inside Ok.
9188a17 to
79f7db0
Compare
|
Thanks for posting this one. To try to better align with the JS semantics I just posted up a change. AI summary below - The original wrapper deferred the blockConcurrencyWhile call to first poll of the returned future, which silently diverged from JS semantics: in JS the gate closes as a side effect of the call, and awaiting the promise is optional. That call-time/await-time fusion is what made the constructor idiom impossible and forced the lazy-init pattern (which needs an initialized check replicated across every handler — fetch, alarm, all websocket callbacks — and breaks if any await precedes the check). The reworked API is a single eager method: pub fn block_concurrency_while<F, Fut, T>(&self, closure: F) -> impl Future<Output = Result<T>>The JS call happens in the fn body (closure via once_into_js, so its lifetime is decoupled from the future). Both JS idioms then fall out of one signature: await it in a handler for an atomic critical section returning T, or call it un-awaited from new() to gate all event delivery during async init — exactly matching un-awaited blockConcurrencyWhile in a JS constructor. This also let me drop infallible: errors-as-values is just returning them inside Ok (T = Result<, E>), now covered by a doc note and a test. Tests carried over with one strengthened: the lazy-init test became a constructor-gating test — new() fires the init (two awaits, 0-sentinel field), and the spec asserts the very first request already observes the loaded value with the closure having run exactly once. The RMW and reset-on-Err tests are unchanged and still pass against real workerd via miniflare. |
|
@lukevalenta would value your confirmation the changes I added work for your use case before landing further. |
NOTE: I used AI to help draft this PR, but I understand and approve the code and think this would be a useful feature for the Rust bindings.
Bind the runtime's blockConcurrencyWhile via State::block_concurrency_while (errors reset the object) and block_concurrency_while_infallible (errors returned as values). This mostly addresses #177, but since a literal async constructor isn't supported we instead document a lazy-init handler pattern.