Skip to content

Commit 062e8b5

Browse files
IlyasShabijasnell
authored andcommitted
worker: add web locks api
PR-URL: #58666 Fixes: #36502 Refs: https://w3c.github.io/web-locks Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Ethan Arrowood <[email protected]> Reviewed-By: Filip Skokan <[email protected]> Reviewed-By: Marco Ippolito <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Yagiz Nizipli <[email protected]> Reviewed-By: Anna Henningsen <[email protected]>
1 parent 0629a17 commit 062e8b5

File tree

70 files changed

+5030
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+5030
-0
lines changed

doc/api/globals.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -768,6 +768,55 @@ consisting of the runtime name and major version number.
768768
console.log(`The user-agent is ${navigator.userAgent}`); // Prints "Node.js/21"
769769
```
770770

771+
### `navigator.locks`
772+
773+
<!-- YAML
774+
added: REPLACEME
775+
-->
776+
777+
> Stability: 1 - Experimental
778+
779+
The `navigator.locks` read-only property returns a [`LockManager`][] instance that
780+
can be used to coordinate access to resources that may be shared across multiple
781+
threads within the same process. This global implementation matches the semantics
782+
of the [browser `LockManager`][] API.
783+
784+
```mjs
785+
// Request an exclusive lock
786+
await navigator.locks.request('my_resource', async (lock) => {
787+
// The lock has been acquired.
788+
console.log(`Lock acquired: ${lock.name}`);
789+
// Lock is automatically released when the function returns
790+
});
791+
792+
// Request a shared lock
793+
await navigator.locks.request('shared_resource', { mode: 'shared' }, async (lock) => {
794+
// Multiple shared locks can be held simultaneously
795+
console.log(`Shared lock acquired: ${lock.name}`);
796+
});
797+
```
798+
799+
```cjs
800+
// Request an exclusive lock
801+
navigator.locks.request('my_resource', async (lock) => {
802+
// The lock has been acquired.
803+
console.log(`Lock acquired: ${lock.name}`);
804+
// Lock is automatically released when the function returns
805+
}).then(() => {
806+
console.log('Lock released');
807+
});
808+
809+
// Request a shared lock
810+
navigator.locks.request('shared_resource', { mode: 'shared' }, async (lock) => {
811+
// Multiple shared locks can be held simultaneously
812+
console.log(`Shared lock acquired: ${lock.name}`);
813+
}).then(() => {
814+
console.log('Shared lock released');
815+
});
816+
```
817+
818+
See [`worker.locks`][] for detailed API documentation.
819+
771820
## Class: `PerformanceEntry`
772821

773822
<!-- YAML
@@ -1263,6 +1312,7 @@ A browser-compatible implementation of [`WritableStreamDefaultWriter`][].
12631312
[`CountQueuingStrategy`]: webstreams.md#class-countqueuingstrategy
12641313
[`DecompressionStream`]: webstreams.md#class-decompressionstream
12651314
[`EventTarget` and `Event` API]: events.md#eventtarget-and-event-api
1315+
[`LockManager`]: worker_threads.md#class-lockmanager
12661316
[`MessageChannel`]: worker_threads.md#class-messagechannel
12671317
[`MessagePort`]: worker_threads.md#class-messageport
12681318
[`PerformanceEntry`]: perf_hooks.md#class-performanceentry
@@ -1313,6 +1363,8 @@ A browser-compatible implementation of [`WritableStreamDefaultWriter`][].
13131363
[`setTimeout`]: timers.md#settimeoutcallback-delay-args
13141364
[`structuredClone`]: https://developer.mozilla.org/en-US/docs/Web/API/structuredClone
13151365
[`window.navigator`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/navigator
1366+
[`worker.locks`]: worker_threads.md#workerlocks
1367+
[browser `LockManager`]: https://developer.mozilla.org/en-US/docs/Web/API/LockManager
13161368
[buffer section]: buffer.md
13171369
[built-in objects]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects
13181370
[timers]: timers.md

doc/api/worker_threads.md

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,153 @@ if (isMainThread) {
755755
}
756756
```
757757
758+
## `worker.locks`
759+
760+
<!-- YAML
761+
added: REPLACEME
762+
-->
763+
764+
> Stability: 1 - Experimental
765+
766+
* {LockManager}
767+
768+
An instance of a [`LockManager`][LockManager] that can be used to coordinate
769+
access to resources that may be shared across multiple threads within the same
770+
process. The API mirrors the semantics of the
771+
[browser `LockManager`][]
772+
773+
### Class: `Lock`
774+
775+
<!-- YAML
776+
added: REPLACEME
777+
-->
778+
779+
The `Lock` interface provides information about a lock that has been granted via
780+
[`locks.request()`][locks.request()]
781+
782+
#### `lock.name`
783+
784+
<!-- YAML
785+
added: REPLACEME
786+
-->
787+
788+
* {string}
789+
790+
The name of the lock.
791+
792+
#### `lock.mode`
793+
794+
<!-- YAML
795+
added: REPLACEME
796+
-->
797+
798+
* {string}
799+
800+
The mode of the lock. Either `shared` or `exclusive`.
801+
802+
### Class: `LockManager`
803+
804+
<!-- YAML
805+
added: REPLACEME
806+
-->
807+
808+
The `LockManager` interface provides methods for requesting and introspecting
809+
locks. To obtain a `LockManager` instance use
810+
811+
```mjs
812+
import { locks } from 'node:worker_threads';
813+
```
814+
815+
```cjs
816+
'use strict';
817+
818+
const { locks } = require('node:worker_threads');
819+
```
820+
821+
This implementation matches the [browser `LockManager`][] API.
822+
823+
#### `locks.request(name[, options], callback)`
824+
825+
<!-- YAML
826+
added: REPLACEME
827+
-->
828+
829+
* `name` {string}
830+
* `options` {Object}
831+
* `mode` {string} Either `'exclusive'` or `'shared'`. **Default:** `'exclusive'`.
832+
* `ifAvailable` {boolean} If `true`, the request will only be granted if the
833+
lock is not already held. If it cannot be granted, `callback` will be
834+
invoked with `null` instead of a `Lock` instance. **Default:** `false`.
835+
* `steal` {boolean} If `true`, any existing locks with the same name are
836+
released and the request is granted immediately, pre-empting any queued
837+
requests. **Default:** `false`.
838+
* `signal` {AbortSignal} that can be used to abort a
839+
pending (but not yet granted) lock request.
840+
* `callback` {Function} Invoked once the lock is granted (or immediately with
841+
`null` if `ifAvailable` is `true` and the lock is unavailable). The lock is
842+
released automatically when the function returns, or—if the function returns
843+
a promise—when that promise settles.
844+
* Returns: {Promise} Resolves once the lock has been released.
845+
846+
```mjs
847+
import { locks } from 'node:worker_threads';
848+
849+
await locks.request('my_resource', async (lock) => {
850+
// The lock has been acquired.
851+
});
852+
// The lock has been released here.
853+
```
854+
855+
```cjs
856+
'use strict';
857+
858+
const { locks } = require('node:worker_threads');
859+
860+
locks.request('my_resource', async (lock) => {
861+
// The lock has been acquired.
862+
}).then(() => {
863+
// The lock has been released here.
864+
});
865+
```
866+
867+
#### `locks.query()`
868+
869+
<!-- YAML
870+
added: REPLACEME
871+
-->
872+
873+
* Returns: {Promise}
874+
875+
Resolves with a `LockManagerSnapshot` describing the currently held and pending
876+
locks for the current process.
877+
878+
```mjs
879+
import { locks } from 'node:worker_threads';
880+
881+
const snapshot = await locks.query();
882+
for (const lock of snapshot.held) {
883+
console.log(`held lock: name ${lock.name}, mode ${lock.mode}`);
884+
}
885+
for (const pending of snapshot.pending) {
886+
console.log(`pending lock: name ${pending.name}, mode ${pending.mode}`);
887+
}
888+
```
889+
890+
```cjs
891+
'use strict';
892+
893+
const { locks } = require('node:worker_threads');
894+
895+
locks.query().then((snapshot) => {
896+
for (const lock of snapshot.held) {
897+
console.log(`held lock: name ${lock.name}, mode ${lock.mode}`);
898+
}
899+
for (const pending of snapshot.pending) {
900+
console.log(`pending lock: name ${pending.name}, mode ${pending.mode}`);
901+
}
902+
});
903+
```
904+
758905
## Class: `BroadcastChannel extends EventTarget`
759906
760907
<!-- YAML
@@ -1937,6 +2084,7 @@ thread spawned will spawn another until the application crashes.
19372084
[Addons worker support]: addons.md#worker-support
19382085
[ECMAScript module loader]: esm.md#data-imports
19392086
[HTML structured clone algorithm]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm
2087+
[LockManager]: #class-lockmanager
19402088
[Signals events]: process.md#signal-events
19412089
[Web Workers]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API
19422090
[`'close'` event]: #event-close
@@ -1992,7 +2140,9 @@ thread spawned will spawn another until the application crashes.
19922140
[`worker.terminate()`]: #workerterminate
19932141
[`worker.threadId`]: #workerthreadid_1
19942142
[async-resource-worker-pool]: async_context.md#using-asyncresource-for-a-worker-thread-pool
2143+
[browser `LockManager`]: https://developer.mozilla.org/en-US/docs/Web/API/LockManager
19952144
[browser `MessagePort`]: https://developer.mozilla.org/en-US/docs/Web/API/MessagePort
19962145
[child processes]: child_process.md
19972146
[contextified]: vm.md#what-does-it-mean-to-contextify-an-object
2147+
[locks.request()]: #locksrequestname-options-callback
19982148
[v8.serdes]: v8.md#serialization-api

0 commit comments

Comments
 (0)