From 84ab4095f3f611bc3798cd5d5d27a61392dcdc7e Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Sun, 13 Dec 2020 20:56:09 +0100 Subject: [PATCH 1/8] worker: implement Web Locks API This is based upon https://github.com/nodejs/node/pull/22719 and exposes the same API. Instead of a C++-backed approach, this variant implements the API mostly in JS. Refs: https://github.com/nodejs/node/pull/22719 Co-authored-by: Gus Caplan --- doc/api/worker_threads.md | 107 +++++ lib/internal/worker.js | 8 + lib/internal/worker/io.js | 3 +- lib/internal/worker/locks.js | 526 ++++++++++++++++++++++++ lib/worker_threads.js | 5 + node.gyp | 1 + src/env.h | 2 + src/node_messaging.cc | 46 ++- src/node_messaging.h | 20 +- test/parallel/test-bootstrap-modules.js | 1 + test/parallel/test-worker-locks.js | 198 +++++++++ tools/doc/type-parser.js | 2 + 12 files changed, 908 insertions(+), 11 deletions(-) create mode 100644 lib/internal/worker/locks.js create mode 100644 test/parallel/test-worker-locks.js diff --git a/doc/api/worker_threads.md b/doc/api/worker_threads.md index 7de28b99e403dc..fab66189d24059 100644 --- a/doc/api/worker_threads.md +++ b/doc/api/worker_threads.md @@ -274,6 +274,108 @@ if (isMainThread) { } ``` +## `worker.locks` + + +> Stability: 1 - Experimental + +* {LockManager} + +An instance of a [`LockManager`][]. + +### Class: `Lock` + + +The Lock interface provides the name and mode of a previously requested lock, +which is received in the callback to [`locks.request()`][]. + +#### `lock.name` + + +* {string} + +The name of this lock. + +#### `lock.mode` + + +* {string} + +The mode of this lock. Either `shared` or `exclusive`. + +### Class: `LockManager` + + +The `LockManager` interface provides methods for requesting a new [`Lock`][] +object and querying for an existing `Lock` object. To get an instance of +`LockManager`, call `worker_threads.locks`. + +With the exception of `AbortController` support, this implementation matches +the [browser `LockManager`][] API. + +#### `locks.request(name[, options], callback)` + + +* `name` {string} +* `options` {Object} + * `mode` {string} Either `'exclusive'` or `'shared'`. **Default:** + `'exclusive'`. + * `ifAvailable` {boolean} If `true`, the lock request will only be + granted if it is not already held. If it cannot be granted, the + callback will be invoked with `null` instead of a `Lock` instance. + **Default:** `false`. + * `steal` {boolean} If `true`, then any held locks with the same name will be + released, and the request will be granted, preempting any queued requests + for it. **Default:** `false`. +* `callback` {Function} The function to be invoked while the lock is acquired. + The lock will be released when the function ends, or if the function returns + a promise, when that promise settles. +* Returns: {Promise} + +Requests a [`Lock`][] object with parameters specifying its name and +characteristics. + +```js +worker_threads.locks.request('my_resource', async (lock) => { + // The lock was granted. +}).then(() => { + // The lock is released here. +}); +``` + +#### `locks.query()` + + +* Returns: {Promise} + +Returns a Promise that resolves with a [`LockManagerSnapshot`][] which contains +information about held and pending locks. + +```js +worker_threads.locks.query().then((state) => { + state.held.forEach((lock) => { + console.log(`held lock: name ${lock.name}, mode ${lock.mode}`); + }); + state.pending.forEach((request) => { + console.log(`requested lock: name ${request.name}, mode ${request.mode}`); + }); +}); +``` + ## Class: `BroadcastChannel extends EventTarget`