benchmark_lock.ts

A machine-wide benchmark lock: one measuring process at a time. Two benchmark runs at once don't give two noisy results, they give two wrong ones, because each changes the other's caches, frequency ceiling, and scheduling.

The lock is a file at a fixed path, BENCHMARK_LOCK_PATH, shared by every checkout and every tool on the machine that uses this module. It is created exclusively and names its holder. Acquiring never waits: a second run is refused, not queued. A holder whose process is gone is stale, and its lock is taken over.

Two limits, both narrow. A process id reused by an unrelated process makes a stale lock look held, and the refusal names the file to delete. And two runs that take over the same stale lock in the same instant can both believe they hold it, so release reports whether the lock was still this process's, and a caller that lost it should discard what it measured. A parent killed outright (SIGKILL) also leaves any child it was waiting on running, with the lock looking stale.

The lock is shared on purpose, so any local user can block benchmarks by creating the file; the refusal names it. On Windows the default path is under the per-user temp directory, so there the lock is per user.

Node-only: it uses node:fs, node:os, and process.

view source

Declarations
#

7 declarations

benchmark_lock_acquire
#

benchmark_lock.ts view source

(label: string, options?: BenchmarkLockAcquireOptions | undefined): Result<{ lock: BenchmarkLock; }, BenchmarkLockRefusal> import {benchmark_lock_acquire} from '@fuzdev/fuz_util/benchmark_lock.js';

Takes the machine-wide benchmark lock, or reports who holds it. It never waits. A lock that can't be read is refused rather than broken, since there is no process to probe, and a stale lock that can't be removed (another user's, in a sticky temp directory) is refused too.

label

what this process is doing, shown to a run that is refused

type string

options?

optional

returns

Result

the lock, or the lock's path, its holder (null when the file names none), and whether that holder is gone but its lock couldn't be removed

throws

  • an - fs error other than the lock existing, or than EPERM removing a stale one

benchmark_lock_format_refusal
#

BENCHMARK_LOCK_PATH
#

benchmark_lock.ts view source

string import {BENCHMARK_LOCK_PATH} from '@fuzdev/fuz_util/benchmark_lock.js';

The default lock path: /tmp/fuz_benchmark.lock on POSIX, and fuz_benchmark.lock in the OS temp directory on Windows. POSIX deliberately doesn't use os.tmpdir(), which follows TMPDIR: two benchmarks run with different TMPDIRs would silently take different locks and measure at once. Windows has no shared temp directory, and its os.tmpdir() is per user, so there the lock is too. Tests pass a path of their own.

BenchmarkLock
#

benchmark_lock.ts view source

BenchmarkLock import type {BenchmarkLock} from '@fuzdev/fuz_util/benchmark_lock.js';

A held benchmark lock.

path

type string

release

Releases the lock. Safe to call again: later calls return the first call's answer and remove nothing.

type (): boolean

returns boolean

whether the lock was still this process's when first released

BenchmarkLockAcquireOptions
#

benchmark_lock.ts view source

BenchmarkLockAcquireOptions import type {BenchmarkLockAcquireOptions} from '@fuzdev/fuz_util/benchmark_lock.js';

path?

The lock file. Defaults to BENCHMARK_LOCK_PATH.

type string

remove?

Removes a lock file, as rmSync with force does. Injectable for tests.

type (path: string): void

path

type string
returns void

BenchmarkLockHolder
#

benchmark_lock.ts view source

BenchmarkLockHolder import type {BenchmarkLockHolder} from '@fuzdev/fuz_util/benchmark_lock.js';

What a lock file says about the process holding it.

pid

type number

label

What the holder is doing, shown to a run that is refused.

type string

cwd

The holder's working directory.

type string

started_at

When the lock was taken, as an ISO timestamp.

type string

BenchmarkLockRefusal
#

benchmark_lock.ts view source

BenchmarkLockRefusal import type {BenchmarkLockRefusal} from '@fuzdev/fuz_util/benchmark_lock.js';

Why benchmark_lock_acquire refused the lock.

path

type string

holder

The holder the lock file names, or null when it names none.

type BenchmarkLockHolder | null

stale

Whether the holder's process is gone but the lock couldn't be removed, as with another user's lock in a sticky temp directory.

type boolean

Depends on
#