Hi all,
I’d like to ask for some design guidance and cross-proposal alignment regarding “token” semantics for locks and concurrency primitives in JavaScript and the Web Platform.
This question sits at the intersection of:
- The Web Locks API (W3C)
- The Structs proposal (Mutex / Condition)
GitHub - tc39/proposal-structs: JavaScript Structs: Fixed Layout Objects - The Concurrency Control proposal
GitHub - tc39/proposal-concurrency-control: interfaces and data structures for concurrency control and integration into async iterator helpers - And the Explicit Resource Management /
using/await usingproposal
Background: Web Locks proposal
On the Web Platform side, I’ve been working on a proposal to allow navigator.locks.request() to return a disposable lock object (a “token” representing ownership), instead of only supporting the callback-based form:
-
Issue: “Support function-less
navigator.locks.request()returning a disposable lock object”
Proposal: Support function-less `navigator.locks.request()` returning a disposable lock object · Issue #122 · w3c/web-locks · GitHub -
Follow-up: “Align Web Locks API token object with TC39 concurrency primitives”
Proposal: Align Web Locks API token object with TC39 concurrency primitives · Issue #123 · w3c/web-locks · GitHub
The basic idea is to allow patterns like:
// Hypothetical Web Locks token-returning API
const token = await navigator.locks.request("resource", { ifAvailable: true });
if (token) {
await using token;
// critical section
} else {
// lock not available
}
In this model:
- The returned object is a token representing lock ownership
- Disposal (
[Symbol.asyncDispose]) orrelease()ends that ownership nullindicates that the lock could not be acquired (e.g.,ifAvailable: true)
Related TC39 designs: Mutex / Concurrency Control
On the TC39 side, several proposals already introduce similar “ownership token” concepts:
- Structs / Mutex & Condition
mutex.lock()returns something like a guard/token which represents ownership of the mutex. - Concurrency Control proposal
introduces primitives for mutual exclusion, conditions, atomic execution, etc., with token-like capabilities. - Explicit Resource Management
introducesusing/await usingandSymbol.asyncDisposefor structured lifetime management.
All of these share a common flavor:
Acquiring a resource returns a token object;
the lifetime of that token represents ownership;
releasing/disposing the token relinquishes the resource.
This is also exactly the model I would like Web Locks to move toward, instead of the purely callback-based style.
Questions about token semantics & alignment
Given all of the above, I’m wondering about cross-spec alignment and would really appreciate guidance from folks involved in these proposals.
Concretely:
-
Should we be aiming for a common conceptual “lock token” pattern?
For example, a shape like:interface LockToken { release(): void | Promise<void>; [Symbol.asyncDispose](): Promise<void>; }Is it helpful to think of Web Locks tokens, Mutex tokens, and other concurrency tokens as instances of the same conceptual pattern? Or should they remain intentionally separate?
-
How should
release()vs@@asyncDisposebe split?
For lock tokens, should:release()be sync and[Symbol.asyncDispose]()do the async waiting?- or should
release()itself be async? - or is this entirely up to each API, with no expected consistency?
-
What about failure / nullability?
In Web Locks,ifAvailable: truecan fail to acquire the lock. In that case, returningnullworks well withawait using(per TC39 feedback in another issue).
Is there any emerging guideline for “failed acquisition” in token-returning APIs that want to integrate withawait using? -
Naming & conceptual expectations
Is “token” the right word? Or should Web Locks preferLockToken,Guard,ReleasableLock, etc.?
Are there expectations (implicit or explicit) that TC39 proposals will use “token” with a particular semantic (e.g., non-transferable, single-owner, non-cloneable)?
Why I’m asking here (and not only in individual repos)
This question spans multiple in-progress designs:
- Web Locks: W3C API, but strongly influenced by JS concurrency patterns
- Mutex / Condition: low-level language primitives for mutual exclusion
- Concurrency Control: higher-level concurrency constructs
- Explicit Resource Management: lifetime / disposal semantics (
using)
Rather than opening narrowly scoped issues in each repo, I wanted to first ask at the design level:
Is there an intended shared mental model for “lock tokens” across these proposals (language + Web APIs)?
Or should each proposal define its own “token” semantics independently?
Any thoughts, pointers, or prior discussions on this would be very appreciated.
Links again for convenience:
-
Web Locks issues:
- Proposal: function-less
navigator.locks.request()returning a disposable lock object
Proposal: Support function-less `navigator.locks.request()` returning a disposable lock object · Issue #122 · w3c/web-locks · GitHub - Align Web Locks token object with TC39 concurrency primitives
Proposal: Align Web Locks API token object with TC39 concurrency primitives · Issue #123 · w3c/web-locks · GitHub
- Proposal: function-less
-
TC39 proposals:
- Structs: Mutex and Condition
GitHub - tc39/proposal-structs: JavaScript Structs: Fixed Layout Objects - Concurrency Control proposal
GitHub - tc39/proposal-concurrency-control: interfaces and data structures for concurrency control and integration into async iterator helpers - Explicit Resource Management /
using/await using
GitHub - tc39/proposal-explicit-resource-management: ECMAScript Explicit Resource Management
- Structs: Mutex and Condition