From 68a266571831d4cf3e13f40762d7bdd0389de634 Mon Sep 17 00:00:00 2001 From: ishabi Date: Thu, 30 Oct 2025 23:49:22 +0100 Subject: [PATCH] src: add locksCounters() to monitor Web Locks usage --- doc/api/globals.md | 4 + doc/api/process.md | 55 +++++ doc/api/worker_threads.md | 6 +- lib/internal/bootstrap/node.js | 1 + lib/internal/process/per_thread.js | 19 ++ src/node_locks.cc | 51 ++++- src/node_locks.h | 18 ++ src/node_process_methods.cc | 23 ++ test/parallel/test-process-locks-counters.js | 226 +++++++++++++++++++ 9 files changed, 401 insertions(+), 2 deletions(-) create mode 100644 test/parallel/test-process-locks-counters.js diff --git a/doc/api/globals.md b/doc/api/globals.md index 5521f1446cb13c..c99f9b891c9b8f 100644 --- a/doc/api/globals.md +++ b/doc/api/globals.md @@ -839,6 +839,9 @@ navigator.locks.request('shared_resource', { mode: 'shared' }, async (lock) => { See [`worker_threads.locks`][] for detailed API documentation. +Use [`process.locksCounters()`][] to monitor lock acquisitions, contention, and +queue sizes across the process. + ## Class: `PerformanceEntry` + +* Returns: {Object} Web Locks usage statistics for the current process. + * `totalAborts` {bigint} Lock requests aborted (either `ifAvailable` could not + be granted, or the callback rejected/threw). + * `totalSteals` {bigint} Lock requests granted with `{ steal: true }`. + * `totalExclusiveAcquired` {bigint} Total exclusive locks acquired. + * `totalSharedAcquired` {bigint} Total shared locks acquired. + * `holdersExclusive` {number} Exclusive locks currently held. + * `holdersShared` {number} Shared locks currently held. + * `pendingExclusive` {number} Exclusive lock requests currently queued. + * `pendingShared` {number} Shared lock requests currently queued. + +Returns an object containing lock usage metrics for the current process to provide visibility into +[`navigator.locks`][] (Web Locks API). + +```mjs +import process from 'node:process'; +import { locks } from 'node:worker_threads'; + +const before = process.locksCounters(); + +await locks.request('my_resource', async () => { + const current = process.locksCounters(); + console.log(current.holdersExclusive); // 1 + console.log(current.totalExclusiveAcquired - before.totalExclusiveAcquired); // 1n +}); + +const after = process.locksCounters(); +console.log(after.holdersExclusive); // 0 (released) +console.log(after.totalExclusiveAcquired - before.totalExclusiveAcquired); // 1n (cumulative) +``` + +```cjs +const process = require('node:process'); +const { locks } = require('node:worker_threads'); + +const before = process.locksCounters(); + +locks.request('my_resource', async () => { + const current = process.locksCounters(); + console.log(current.holdersExclusive); // 1 + console.log(current.totalExclusiveAcquired - before.totalExclusiveAcquired); // 1n +}).then(() => { + const after = process.locksCounters(); + console.log(after.holdersExclusive); // 0 (released) + console.log(after.totalExclusiveAcquired - before.totalExclusiveAcquired); // 1n (cumulative) +}); +``` + ## `process.send(message[, sendHandle[, options]][, callback])`