@zukhruf/mutex 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/dist/fencing/file-token-source.js +2 -2
- package/dist/fencing/file-token-source.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/leader-election/index.d.ts +1 -0
- package/dist/leader-election/index.d.ts.map +1 -1
- package/dist/leader-election/index.js +1 -0
- package/dist/leader-election/index.js.map +1 -1
- package/dist/leader-election/leader-election.d.ts.map +1 -1
- package/dist/leader-election/leader-election.js +4 -2
- package/dist/leader-election/leader-election.js.map +1 -1
- package/dist/local-directory/local-directory.d.ts +7 -0
- package/dist/local-directory/local-directory.d.ts.map +1 -0
- package/dist/local-directory/local-directory.js +75 -0
- package/dist/local-directory/local-directory.js.map +1 -0
- package/dist/local-directory/network-directory-error.d.ts +8 -0
- package/dist/local-directory/network-directory-error.d.ts.map +1 -0
- package/dist/local-directory/network-directory-error.js +13 -0
- package/dist/local-directory/network-directory-error.js.map +1 -0
- package/dist/lock-stores/file-system/file-lock-store.d.ts.map +1 -1
- package/dist/lock-stores/file-system/file-lock-store.js +4 -1
- package/dist/lock-stores/file-system/file-lock-store.js.map +1 -1
- package/dist/lock-stores/file-system/lock-file-store.d.ts.map +1 -1
- package/dist/lock-stores/file-system/lock-file-store.js +9 -4
- package/dist/lock-stores/file-system/lock-file-store.js.map +1 -1
- package/dist/lock-stores/file-system/ticket-queue-file-store.d.ts.map +1 -1
- package/dist/lock-stores/file-system/ticket-queue-file-store.js +3 -2
- package/dist/lock-stores/file-system/ticket-queue-file-store.js.map +1 -1
- package/dist/lock-stores/ipc/child-process-connection.d.ts +3 -3
- package/dist/lock-stores/ipc/child-process-connection.d.ts.map +1 -1
- package/dist/lock-stores/ipc/child-process-connection.js +16 -20
- package/dist/lock-stores/ipc/child-process-connection.js.map +1 -1
- package/dist/lock-stores/ipc/ipc-store.d.ts.map +1 -1
- package/dist/lock-stores/ipc/ipc-store.js +7 -5
- package/dist/lock-stores/ipc/ipc-store.js.map +1 -1
- package/dist/lock-stores/ipc/process-channel-connection.d.ts +4 -3
- package/dist/lock-stores/ipc/process-channel-connection.d.ts.map +1 -1
- package/dist/lock-stores/ipc/process-channel-connection.js +6 -14
- package/dist/lock-stores/ipc/process-channel-connection.js.map +1 -1
- package/dist/lock-stores/memory/memory-store.d.ts +1 -1
- package/dist/lock-stores/memory/memory-store.d.ts.map +1 -1
- package/dist/lock-stores/memory/memory-store.js +13 -14
- package/dist/lock-stores/memory/memory-store.js.map +1 -1
- package/dist/lock-stores/remote/connection-supervisor.d.ts +39 -0
- package/dist/lock-stores/remote/connection-supervisor.d.ts.map +1 -0
- package/dist/lock-stores/remote/connection-supervisor.js +195 -0
- package/dist/lock-stores/remote/connection-supervisor.js.map +1 -0
- package/dist/lock-stores/remote/connection.d.ts +11 -7
- package/dist/lock-stores/remote/connection.d.ts.map +1 -1
- package/dist/lock-stores/remote/connector.d.ts +5 -2
- package/dist/lock-stores/remote/connector.d.ts.map +1 -1
- package/dist/lock-stores/remote/lock-coordinator.d.ts +2 -2
- package/dist/lock-stores/remote/lock-coordinator.d.ts.map +1 -1
- package/dist/lock-stores/remote/lock-coordinator.js +140 -69
- package/dist/lock-stores/remote/lock-coordinator.js.map +1 -1
- package/dist/lock-stores/remote/remote-lock-client.d.ts +8 -7
- package/dist/lock-stores/remote/remote-lock-client.d.ts.map +1 -1
- package/dist/lock-stores/remote/remote-lock-client.js +99 -99
- package/dist/lock-stores/remote/remote-lock-client.js.map +1 -1
- package/dist/lock-stores/socket/electing-connector.d.ts +7 -4
- package/dist/lock-stores/socket/electing-connector.d.ts.map +1 -1
- package/dist/lock-stores/socket/electing-connector.js +35 -8
- package/dist/lock-stores/socket/electing-connector.js.map +1 -1
- package/dist/lock-stores/socket/local-directory-connector.d.ts +14 -0
- package/dist/lock-stores/socket/local-directory-connector.d.ts.map +1 -0
- package/dist/lock-stores/socket/local-directory-connector.js +23 -0
- package/dist/lock-stores/socket/local-directory-connector.js.map +1 -0
- package/dist/lock-stores/socket/socket-connection.d.ts +3 -3
- package/dist/lock-stores/socket/socket-connection.d.ts.map +1 -1
- package/dist/lock-stores/socket/socket-connection.js +16 -18
- package/dist/lock-stores/socket/socket-connection.js.map +1 -1
- package/dist/lock-stores/socket/socket-store.d.ts +1 -2
- package/dist/lock-stores/socket/socket-store.d.ts.map +1 -1
- package/dist/lock-stores/socket/socket-store.js +16 -23
- package/dist/lock-stores/socket/socket-store.js.map +1 -1
- package/dist/lock-stores/thread/parent-port-connection.d.ts +4 -3
- package/dist/lock-stores/thread/parent-port-connection.d.ts.map +1 -1
- package/dist/lock-stores/thread/parent-port-connection.js +6 -13
- package/dist/lock-stores/thread/parent-port-connection.js.map +1 -1
- package/dist/lock-stores/thread/thread-store.d.ts.map +1 -1
- package/dist/lock-stores/thread/thread-store.js +5 -8
- package/dist/lock-stores/thread/thread-store.js.map +1 -1
- package/dist/lock-stores/thread/worker-connection.d.ts +3 -3
- package/dist/lock-stores/thread/worker-connection.d.ts.map +1 -1
- package/dist/lock-stores/thread/worker-connection.js +19 -23
- package/dist/lock-stores/thread/worker-connection.js.map +1 -1
- package/dist/mutex/acquire-mode.d.ts +6 -3
- package/dist/mutex/acquire-mode.d.ts.map +1 -1
- package/dist/mutex/acquire-modes/skip-if-busy-mode.d.ts +7 -3
- package/dist/mutex/acquire-modes/skip-if-busy-mode.d.ts.map +1 -1
- package/dist/mutex/acquire-modes/skip-if-busy-mode.js +7 -4
- package/dist/mutex/acquire-modes/skip-if-busy-mode.js.map +1 -1
- package/dist/mutex/acquire-modes/wait-mode.d.ts +2 -2
- package/dist/mutex/acquire-modes/wait-mode.d.ts.map +1 -1
- package/dist/mutex/acquire-modes/wait-mode.js +2 -2
- package/dist/mutex/acquire-modes/wait-mode.js.map +1 -1
- package/dist/mutex/key.d.ts +3 -2
- package/dist/mutex/key.d.ts.map +1 -1
- package/dist/mutex/key.js +3 -3
- package/dist/mutex/key.js.map +1 -1
- package/dist/mutex/mutex.d.ts +3 -3
- package/dist/mutex/mutex.d.ts.map +1 -1
- package/dist/mutex/mutex.js +28 -5
- package/dist/mutex/mutex.js.map +1 -1
- package/dist/shared/fs/atomic-write.d.ts.map +1 -1
- package/dist/shared/fs/atomic-write.js +3 -27
- package/dist/shared/fs/atomic-write.js.map +1 -1
- package/dist/shared/fs/create-exclusive.d.ts.map +1 -1
- package/dist/shared/fs/create-exclusive.js +7 -2
- package/dist/shared/fs/create-exclusive.js.map +1 -1
- package/dist/shared/fs/durable-write.d.ts +7 -0
- package/dist/shared/fs/durable-write.d.ts.map +1 -0
- package/dist/shared/fs/durable-write.js +46 -0
- package/dist/shared/fs/durable-write.js.map +1 -0
- package/dist/shared/fs/patiently.d.ts +3 -0
- package/dist/shared/fs/patiently.d.ts.map +1 -0
- package/dist/shared/fs/patiently.js +30 -0
- package/dist/shared/fs/patiently.js.map +1 -0
- package/dist/shared/fs/replace-file.d.ts +8 -0
- package/dist/shared/fs/replace-file.d.ts.map +1 -0
- package/dist/shared/fs/replace-file.js +12 -0
- package/dist/shared/fs/replace-file.js.map +1 -0
- package/dist/shared/latch.d.ts +10 -0
- package/dist/shared/latch.d.ts.map +1 -0
- package/dist/shared/latch.js +14 -0
- package/dist/shared/latch.js.map +1 -0
- package/package.json +1 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
1
2
|
import { unwrap, wrap } from "../remote/envelope.js";
|
|
2
3
|
import { isLockRequest, } from "../remote/protocol.js";
|
|
3
4
|
/**
|
|
@@ -5,41 +6,36 @@ import { isLockRequest, } from "../remote/protocol.js";
|
|
|
5
6
|
* emits `exit` however the thread stops, so a terminated or crashed holder
|
|
6
7
|
* is always noticed.
|
|
7
8
|
*/
|
|
8
|
-
export class WorkerConnection {
|
|
9
|
+
export class WorkerConnection extends EventEmitter {
|
|
9
10
|
#worker;
|
|
10
|
-
#
|
|
11
|
-
|
|
11
|
+
#onMessage = (envelope) => {
|
|
12
|
+
const request = unwrap(envelope);
|
|
13
|
+
if (isLockRequest(request))
|
|
14
|
+
this.emit('message', request);
|
|
15
|
+
};
|
|
16
|
+
#onExit = () => {
|
|
17
|
+
this.close();
|
|
18
|
+
this.emit('close');
|
|
19
|
+
};
|
|
12
20
|
constructor(worker) {
|
|
21
|
+
super();
|
|
13
22
|
this.#worker = worker;
|
|
23
|
+
worker.on('message', this.#onMessage);
|
|
24
|
+
worker.once('exit', this.#onExit);
|
|
14
25
|
}
|
|
15
26
|
async send(response) {
|
|
16
|
-
|
|
27
|
+
// A stopped worker drops messages without an error; its thread id is -1 from then on.
|
|
28
|
+
if (this.#worker.threadId === -1) {
|
|
17
29
|
throw new Error('The worker thread has exited.');
|
|
30
|
+
}
|
|
18
31
|
this.#worker.postMessage(wrap(response));
|
|
19
32
|
}
|
|
20
|
-
listen({ message, close }) {
|
|
21
|
-
const onMessage = (envelope) => {
|
|
22
|
-
const request = unwrap(envelope);
|
|
23
|
-
if (isLockRequest(request))
|
|
24
|
-
message(request);
|
|
25
|
-
};
|
|
26
|
-
const onExit = () => {
|
|
27
|
-
this.#exited = true;
|
|
28
|
-
this.close();
|
|
29
|
-
close();
|
|
30
|
-
};
|
|
31
|
-
this.#worker.on('message', onMessage);
|
|
32
|
-
this.#worker.once('exit', onExit);
|
|
33
|
-
this.#detach = () => {
|
|
34
|
-
this.#worker.off('message', onMessage);
|
|
35
|
-
this.#worker.off('exit', onExit);
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
33
|
/** The running worker already keeps its starting thread alive. */
|
|
39
34
|
ref() { }
|
|
40
35
|
unref() { }
|
|
41
36
|
close() {
|
|
42
|
-
this.#
|
|
37
|
+
this.#worker.off('message', this.#onMessage);
|
|
38
|
+
this.#worker.off('exit', this.#onExit);
|
|
43
39
|
}
|
|
44
40
|
}
|
|
45
41
|
//# sourceMappingURL=worker-connection.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker-connection.js","sourceRoot":"","sources":["../../../src/lock-stores/thread/worker-connection.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"worker-connection.js","sourceRoot":"","sources":["../../../src/lock-stores/thread/worker-connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAI3C,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAGL,aAAa,GACd,MAAM,uBAAuB,CAAC;AAE/B;;;;GAIG;AACH,MAAM,OAAO,gBACX,SAAQ,YAA2C;IAG1C,OAAO,CAAS;IAEhB,UAAU,GAAG,CAAC,QAAiB,EAAE,EAAE;QAC1C,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;QACjC,IAAI,aAAa,CAAC,OAAO,CAAC;YAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAC5D,CAAC,CAAC;IAEO,OAAO,GAAG,GAAG,EAAE;QACtB,IAAI,CAAC,KAAK,EAAE,CAAC;QACb,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACrB,CAAC,CAAC;IAEF,YAAY,MAAc;QACxB,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IACpC,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,QAAsB;QAC/B,sFAAsF;QACtF,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,KAAK,CAAC,CAAC,EAAE,CAAC;YACjC,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;QACnD,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC3C,CAAC;IAED,kEAAkE;IAClE,GAAG,KAAI,CAAC;IAER,KAAK,KAAI,CAAC;IAEV,KAAK;QACH,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QAC7C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IACzC,CAAC;CACF"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Lease } from './lease.ts';
|
|
2
|
-
import type { LockStore } from './lock-store.ts';
|
|
2
|
+
import type { AcquireOptions, LockStore } from './lock-store.ts';
|
|
3
3
|
/** Whether an acquire mode always ends with the key held, or may give up. */
|
|
4
4
|
export type Outcome = 'always' | 'maybe';
|
|
5
5
|
/**
|
|
@@ -8,8 +8,11 @@ export type Outcome = 'always' | 'maybe';
|
|
|
8
8
|
*/
|
|
9
9
|
export interface AcquireMode<O extends Outcome = Outcome> {
|
|
10
10
|
readonly outcome: O;
|
|
11
|
-
/**
|
|
12
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Resolves with a lease, or `undefined` when this caller gives up. Pass
|
|
13
|
+
* `signal` on to each wait, so that a cancel stops the wait at once.
|
|
14
|
+
*/
|
|
15
|
+
acquire(store: LockStore, key: string, options: AcquireOptions): Promise<Lease | undefined>;
|
|
13
16
|
}
|
|
14
17
|
export interface Acquired<T> {
|
|
15
18
|
readonly acquired: true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"acquire-mode.d.ts","sourceRoot":"","sources":["../../src/mutex/acquire-mode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"acquire-mode.d.ts","sourceRoot":"","sources":["../../src/mutex/acquire-mode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjE,6EAA6E;AAC7E,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEzC;;;GAGG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO;IACtD,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACpB;;;OAGG;IACH,OAAO,CACL,KAAK,EAAE,SAAS,EAChB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,QAAQ,CAAC,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC;CAC1B;AAED,+CAA+C;AAC/C,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,0DAA0D;IAC1D,MAAM,EAAE,CAAC,CAAC;IACV,wDAAwD;IACxD,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC;CAClC;AAED,yCAAyC;AACzC,MAAM,MAAM,UAAU,CACpB,CAAC,SAAS,WAAW,EACrB,CAAC,IACC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC"}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import type { AcquireMode } from '../acquire-mode.ts';
|
|
2
2
|
import type { Lease } from '../lease.ts';
|
|
3
|
-
import type { LockStore } from '../lock-store.ts';
|
|
3
|
+
import type { AcquireOptions, LockStore } from '../lock-store.ts';
|
|
4
4
|
export interface SkipIfBusyOptions {
|
|
5
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* Milliseconds to wait for another holder before giving up. The limit
|
|
7
|
+
* starts when the lock store answers that the key is busy: a slow answer
|
|
8
|
+
* does not show a busy key. Defaults to 0: one attempt only.
|
|
9
|
+
*/
|
|
6
10
|
waitAtMost?: number;
|
|
7
11
|
}
|
|
8
12
|
/** Gives up when the key stays busy, at once or after `waitAtMost`, so the task may not run. */
|
|
@@ -10,6 +14,6 @@ export declare class SkipIfBusyMode implements AcquireMode<'maybe'> {
|
|
|
10
14
|
#private;
|
|
11
15
|
readonly outcome: "maybe";
|
|
12
16
|
constructor({ waitAtMost }?: SkipIfBusyOptions);
|
|
13
|
-
acquire(store: LockStore, key: string): Promise<Lease | undefined>;
|
|
17
|
+
acquire(store: LockStore, key: string, { signal }: AcquireOptions): Promise<Lease | undefined>;
|
|
14
18
|
}
|
|
15
19
|
//# sourceMappingURL=skip-if-busy-mode.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skip-if-busy-mode.d.ts","sourceRoot":"","sources":["../../../src/mutex/acquire-modes/skip-if-busy-mode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"skip-if-busy-mode.d.ts","sourceRoot":"","sources":["../../../src/mutex/acquire-modes/skip-if-busy-mode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElE,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,gGAAgG;AAChG,qBAAa,cAAe,YAAW,WAAW,CAAC,OAAO,CAAC;;IACzD,QAAQ,CAAC,OAAO,EAAG,OAAO,CAAU;gBAGxB,EAAE,UAAc,EAAE,GAAE,iBAAsB;IAShD,OAAO,CACX,KAAK,EAAE,SAAS,EAChB,GAAG,EAAE,MAAM,EACX,EAAE,MAAM,EAAE,EAAE,cAAc,GACzB,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC;CAgB9B"}
|
|
@@ -8,17 +8,20 @@ export class SkipIfBusyMode {
|
|
|
8
8
|
}
|
|
9
9
|
this.#waitAtMost = waitAtMost;
|
|
10
10
|
}
|
|
11
|
-
async acquire(store, key) {
|
|
11
|
+
async acquire(store, key, { signal }) {
|
|
12
12
|
// Always one attempt first: a time limit must never skip a key that is free.
|
|
13
13
|
const lease = await store.tryAcquire(key);
|
|
14
14
|
if (lease || this.#waitAtMost === 0)
|
|
15
15
|
return lease;
|
|
16
|
-
|
|
16
|
+
// The caller comes first, so a caller that already cancelled is not read as giving up.
|
|
17
|
+
const timeout = AbortSignal.timeout(this.#waitAtMost);
|
|
17
18
|
try {
|
|
18
|
-
return await store.acquire(key, {
|
|
19
|
+
return await store.acquire(key, {
|
|
20
|
+
signal: signal ? AbortSignal.any([signal, timeout]) : timeout,
|
|
21
|
+
});
|
|
19
22
|
}
|
|
20
23
|
catch (error) {
|
|
21
|
-
if (error ===
|
|
24
|
+
if (timeout.aborted && error === timeout.reason)
|
|
22
25
|
return undefined;
|
|
23
26
|
throw error;
|
|
24
27
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skip-if-busy-mode.js","sourceRoot":"","sources":["../../../src/mutex/acquire-modes/skip-if-busy-mode.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"skip-if-busy-mode.js","sourceRoot":"","sources":["../../../src/mutex/acquire-modes/skip-if-busy-mode.ts"],"names":[],"mappings":"AAaA,gGAAgG;AAChG,MAAM,OAAO,cAAc;IAChB,OAAO,GAAG,OAAgB,CAAC;IAC3B,WAAW,CAAS;IAE7B,YAAY,EAAE,UAAU,GAAG,CAAC,KAAwB,EAAE;QACpD,IAAI,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;YACvD,MAAM,IAAI,UAAU,CAClB,gEAAgE,UAAU,GAAG,CAC9E,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC;IAChC,CAAC;IAED,KAAK,CAAC,OAAO,CACX,KAAgB,EAChB,GAAW,EACX,EAAE,MAAM,EAAkB;QAE1B,6EAA6E;QAC7E,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QAC1C,IAAI,KAAK,IAAI,IAAI,CAAC,WAAW,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QAElD,uFAAuF;QACvF,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACtD,IAAI,CAAC;YACH,OAAO,MAAM,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE;gBAC9B,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO;aAC9D,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,OAAO,CAAC,OAAO,IAAI,KAAK,KAAK,OAAO,CAAC,MAAM;gBAAE,OAAO,SAAS,CAAC;YAClE,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CACF"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { AcquireMode } from '../acquire-mode.ts';
|
|
2
2
|
import type { Lease } from '../lease.ts';
|
|
3
|
-
import type { LockStore } from '../lock-store.ts';
|
|
3
|
+
import type { AcquireOptions, LockStore } from '../lock-store.ts';
|
|
4
4
|
/** Waits until the key is granted, so the task always runs. */
|
|
5
5
|
export declare class WaitMode implements AcquireMode<'always'> {
|
|
6
6
|
readonly outcome: "always";
|
|
7
|
-
acquire(store: LockStore, key: string): Promise<Lease>;
|
|
7
|
+
acquire(store: LockStore, key: string, options: AcquireOptions): Promise<Lease>;
|
|
8
8
|
}
|
|
9
9
|
//# sourceMappingURL=wait-mode.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wait-mode.d.ts","sourceRoot":"","sources":["../../../src/mutex/acquire-modes/wait-mode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"wait-mode.d.ts","sourceRoot":"","sources":["../../../src/mutex/acquire-modes/wait-mode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElE,+DAA+D;AAC/D,qBAAa,QAAS,YAAW,WAAW,CAAC,QAAQ,CAAC;IACpD,QAAQ,CAAC,OAAO,EAAG,QAAQ,CAAU;IAErC,OAAO,CACL,KAAK,EAAE,SAAS,EAChB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,KAAK,CAAC;CAGlB"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/** Waits until the key is granted, so the task always runs. */
|
|
2
2
|
export class WaitMode {
|
|
3
3
|
outcome = 'always';
|
|
4
|
-
acquire(store, key) {
|
|
5
|
-
return store.acquire(key);
|
|
4
|
+
acquire(store, key, options) {
|
|
5
|
+
return store.acquire(key, options);
|
|
6
6
|
}
|
|
7
7
|
}
|
|
8
8
|
//# sourceMappingURL=wait-mode.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wait-mode.js","sourceRoot":"","sources":["../../../src/mutex/acquire-modes/wait-mode.ts"],"names":[],"mappings":"AAIA,+DAA+D;AAC/D,MAAM,OAAO,QAAQ;IACV,OAAO,GAAG,QAAiB,CAAC;IAErC,OAAO,
|
|
1
|
+
{"version":3,"file":"wait-mode.js","sourceRoot":"","sources":["../../../src/mutex/acquire-modes/wait-mode.ts"],"names":[],"mappings":"AAIA,+DAA+D;AAC/D,MAAM,OAAO,QAAQ;IACV,OAAO,GAAG,QAAiB,CAAC;IAErC,OAAO,CACL,KAAgB,EAChB,GAAW,EACX,OAAuB;QAEvB,OAAO,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACrC,CAAC;CACF"}
|
package/dist/mutex/key.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AcquireMode, Outcome, OutcomeResults } from './acquire-mode.ts';
|
|
2
2
|
import type { Lease } from './lease.ts';
|
|
3
|
+
import type { AcquireOptions } from './lock-store.ts';
|
|
3
4
|
import type { Mutex } from './mutex.ts';
|
|
4
5
|
/**
|
|
5
6
|
* A key with the acquire mode that its callers use unless one call says
|
|
@@ -11,11 +12,11 @@ export declare class Key<O extends Outcome> {
|
|
|
11
12
|
readonly name: string;
|
|
12
13
|
constructor(mutex: Mutex, name: string, mode: AcquireMode<O>);
|
|
13
14
|
/** Runs `task` with the key's own acquire mode. */
|
|
14
|
-
run<T>(task: (lease: Lease) => Promise<T>, options?: {
|
|
15
|
+
run<T>(task: (lease: Lease) => Promise<T>, options?: AcquireOptions & {
|
|
15
16
|
mode?: undefined;
|
|
16
17
|
}): Promise<OutcomeResults<T>[O]>;
|
|
17
18
|
/** Runs `task` with `mode` instead of the key's own acquire mode. */
|
|
18
|
-
run<T, P extends Outcome>(task: (lease: Lease) => Promise<T>, options: {
|
|
19
|
+
run<T, P extends Outcome>(task: (lease: Lease) => Promise<T>, options: AcquireOptions & {
|
|
19
20
|
mode: AcquireMode<P>;
|
|
20
21
|
}): Promise<OutcomeResults<T>[P]>;
|
|
21
22
|
}
|
package/dist/mutex/key.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"key.d.ts","sourceRoot":"","sources":["../../src/mutex/key.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAC9E,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC;;;;GAIG;AACH,qBAAa,GAAG,CAAC,CAAC,SAAS,OAAO;;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAIV,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC;IAM5D,mDAAmD;IACnD,GAAG,CAAC,CAAC,EACH,IAAI,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,EAClC,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,SAAS,CAAA;KAAE,
|
|
1
|
+
{"version":3,"file":"key.d.ts","sourceRoot":"","sources":["../../src/mutex/key.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAC9E,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC;;;;GAIG;AACH,qBAAa,GAAG,CAAC,CAAC,SAAS,OAAO;;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAIV,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC;IAM5D,mDAAmD;IACnD,GAAG,CAAC,CAAC,EACH,IAAI,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,EAClC,OAAO,CAAC,EAAE,cAAc,GAAG;QAAE,IAAI,CAAC,EAAE,SAAS,CAAA;KAAE,GAC9C,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAChC,qEAAqE;IACrE,GAAG,CAAC,CAAC,EAAE,CAAC,SAAS,OAAO,EACtB,IAAI,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,EAClC,OAAO,EAAE,cAAc,GAAG;QAAE,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,CAAA;KAAE,GACjD,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAYjC"}
|
package/dist/mutex/key.js
CHANGED
|
@@ -12,10 +12,10 @@ export class Key {
|
|
|
12
12
|
this.name = name;
|
|
13
13
|
this.#mode = mode;
|
|
14
14
|
}
|
|
15
|
-
run(task, { mode } = {}) {
|
|
15
|
+
run(task, { mode, signal, } = {}) {
|
|
16
16
|
return mode === undefined
|
|
17
|
-
? this.#mutex.acquire(this.name, task, { mode: this.#mode })
|
|
18
|
-
: this.#mutex.acquire(this.name, task, { mode });
|
|
17
|
+
? this.#mutex.acquire(this.name, task, { mode: this.#mode, signal })
|
|
18
|
+
: this.#mutex.acquire(this.name, task, { mode, signal });
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
//# sourceMappingURL=key.js.map
|
package/dist/mutex/key.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"key.js","sourceRoot":"","sources":["../../src/mutex/key.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"key.js","sourceRoot":"","sources":["../../src/mutex/key.ts"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,MAAM,OAAO,GAAG;IACL,IAAI,CAAS;IACb,MAAM,CAAQ;IACd,KAAK,CAAiB;IAE/B,YAAY,KAAY,EAAE,IAAY,EAAE,IAAoB;QAC1D,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IACpB,CAAC;IAYD,GAAG,CACD,IAAkC,EAClC,EACE,IAAI,EACJ,MAAM,MACoD,EAAE;QAE9D,OAAO,IAAI,KAAK,SAAS;YACvB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC;YACpE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IAC7D,CAAC;CACF"}
|
package/dist/mutex/mutex.d.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import type { AcquireMode, Outcome, OutcomeResults } from './acquire-mode.ts';
|
|
2
2
|
import { Key } from './key.ts';
|
|
3
3
|
import type { Lease } from './lease.ts';
|
|
4
|
-
import type { LockStore } from './lock-store.ts';
|
|
4
|
+
import type { AcquireOptions, LockStore } from './lock-store.ts';
|
|
5
5
|
export declare class Mutex {
|
|
6
6
|
#private;
|
|
7
7
|
constructor(store: LockStore);
|
|
8
8
|
/** Runs `task` while holding `key`, and waits while the key is busy. */
|
|
9
|
-
acquire<T>(key: string, task: (lease: Lease) => Promise<T>, options?: {
|
|
9
|
+
acquire<T>(key: string, task: (lease: Lease) => Promise<T>, options?: AcquireOptions & {
|
|
10
10
|
mode?: undefined;
|
|
11
11
|
}): Promise<T>;
|
|
12
12
|
/** Runs `task` while holding `key`. The acquire mode decides what happens while the key is busy. */
|
|
13
|
-
acquire<T, O extends Outcome>(key: string, task: (lease: Lease) => Promise<T>, options: {
|
|
13
|
+
acquire<T, O extends Outcome>(key: string, task: (lease: Lease) => Promise<T>, options: AcquireOptions & {
|
|
14
14
|
mode: AcquireMode<O>;
|
|
15
15
|
}): Promise<OutcomeResults<T>[O]>;
|
|
16
16
|
/** A key whose callers wait, unless one call says otherwise. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mutex.d.ts","sourceRoot":"","sources":["../../src/mutex/mutex.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"mutex.d.ts","sourceRoot":"","sources":["../../src/mutex/mutex.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAE9E,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAoBjE,qBAAa,KAAK;;gBAGJ,KAAK,EAAE,SAAS;IAI5B,wEAAwE;IACxE,OAAO,CAAC,CAAC,EACP,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,EAClC,OAAO,CAAC,EAAE,cAAc,GAAG;QAAE,IAAI,CAAC,EAAE,SAAS,CAAA;KAAE,GAC9C,OAAO,CAAC,CAAC,CAAC;IACb,oGAAoG;IACpG,OAAO,CAAC,CAAC,EAAE,CAAC,SAAS,OAAO,EAC1B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,EAClC,OAAO,EAAE,cAAc,GAAG;QAAE,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,CAAA;KAAE,GACjD,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAchC,gEAAgE;IAChE,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,SAAS,CAAA;KAAE,GAAG,GAAG,CAAC,QAAQ,CAAC;IAChE,sEAAsE;IACtE,GAAG,CAAC,CAAC,SAAS,OAAO,EACnB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;QAAE,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,CAAA;KAAE,GAChC,GAAG,CAAC,CAAC,CAAC;CAyCV"}
|
package/dist/mutex/mutex.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { untilAborted } from "../shared/until-aborted.js";
|
|
1
2
|
import { WaitMode } from "./acquire-modes/wait-mode.js";
|
|
2
3
|
import { Key } from "./key.js";
|
|
3
4
|
const wait = new WaitMode();
|
|
@@ -18,10 +19,10 @@ export class Mutex {
|
|
|
18
19
|
constructor(store) {
|
|
19
20
|
this.#store = store;
|
|
20
21
|
}
|
|
21
|
-
acquire(key, task, { mode } = {}) {
|
|
22
|
+
acquire(key, task, { mode, signal, } = {}) {
|
|
22
23
|
return mode === undefined
|
|
23
|
-
? this.#run(key, task, wait)
|
|
24
|
-
: this.#run(key, task, mode);
|
|
24
|
+
? this.#run(key, task, wait, signal)
|
|
25
|
+
: this.#run(key, task, mode, signal);
|
|
25
26
|
}
|
|
26
27
|
key(name, { mode } = {}) {
|
|
27
28
|
return mode === undefined
|
|
@@ -30,12 +31,34 @@ export class Mutex {
|
|
|
30
31
|
}
|
|
31
32
|
// `mode.outcome` has the type O, so TypeScript checks each result below
|
|
32
33
|
// against OutcomeResults<T>[O] and needs no type assertion.
|
|
33
|
-
async #run(key, task, mode) {
|
|
34
|
-
const lease = await
|
|
34
|
+
async #run(key, task, mode, signal) {
|
|
35
|
+
const lease = await this.#acquire(key, mode, signal);
|
|
35
36
|
if (!lease)
|
|
36
37
|
return gaveUp(key)[mode.outcome];
|
|
37
38
|
await using held = lease;
|
|
38
39
|
return ran(await task(held))[mode.outcome];
|
|
39
40
|
}
|
|
41
|
+
// A cancelled caller never takes a key, even a free one. A mode that does
|
|
42
|
+
// not pass the signal on still rejects at once, and gives its late lease back.
|
|
43
|
+
async #acquire(key, mode, signal) {
|
|
44
|
+
signal?.throwIfAborted();
|
|
45
|
+
const acquiring = mode.acquire(this.#store, key, { signal });
|
|
46
|
+
try {
|
|
47
|
+
return await untilAborted(acquiring, signal);
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
if (signal?.aborted)
|
|
51
|
+
void giveBack(acquiring);
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async function giveBack(acquiring) {
|
|
57
|
+
try {
|
|
58
|
+
await (await acquiring)?.[Symbol.asyncDispose]();
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// The caller already rejected with its reason; nobody is left to tell.
|
|
62
|
+
}
|
|
40
63
|
}
|
|
41
64
|
//# sourceMappingURL=mutex.js.map
|
package/dist/mutex/mutex.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mutex.js","sourceRoot":"","sources":["../../src/mutex/mutex.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"mutex.js","sourceRoot":"","sources":["../../src/mutex/mutex.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"atomic-write.d.ts","sourceRoot":"","sources":["../../../src/shared/fs/atomic-write.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"atomic-write.d.ts","sourceRoot":"","sources":["../../../src/shared/fs/atomic-write.ts"],"names":[],"mappings":"AAKA,qGAAqG;AACrG,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,iBAI9D"}
|
|
@@ -1,34 +1,10 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { isErrno } from "./errno.js";
|
|
5
|
-
/** How long to retry a rename that Windows refuses while another handle has the target open. */
|
|
6
|
-
const WINDOWS_RENAME_PATIENCE = 1000;
|
|
2
|
+
import { writeFile } from 'node:fs/promises';
|
|
3
|
+
import { replaceFile } from "./replace-file.js";
|
|
7
4
|
/** Replaces `path` in one step, so readers see the old or the new content, never a partial write. */
|
|
8
5
|
export async function atomicWrite(path, content) {
|
|
9
6
|
const draft = `${path}.${randomUUID()}.tmp`;
|
|
10
7
|
await writeFile(draft, content);
|
|
11
|
-
await
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* On Windows, a rename over a file fails while another process has that file
|
|
15
|
-
* open, for example a waiter that reads it at that moment. The refusal is
|
|
16
|
-
* brief, so the rename is tried again; it stays the single commit point.
|
|
17
|
-
*/
|
|
18
|
-
async function replaceWith(draft, path) {
|
|
19
|
-
const started = performance.now();
|
|
20
|
-
for (let attempt = 0;; attempt++) {
|
|
21
|
-
try {
|
|
22
|
-
return await rename(draft, path);
|
|
23
|
-
}
|
|
24
|
-
catch (error) {
|
|
25
|
-
const refusedForNow = process.platform === 'win32' &&
|
|
26
|
-
['EPERM', 'EACCES', 'EBUSY'].some((code) => isErrno(error, code));
|
|
27
|
-
if (!refusedForNow ||
|
|
28
|
-
performance.now() - started > WINDOWS_RENAME_PATIENCE)
|
|
29
|
-
throw error;
|
|
30
|
-
await delay(Math.min(5 * 2 ** attempt, 100));
|
|
31
|
-
}
|
|
32
|
-
}
|
|
8
|
+
await replaceFile(draft, path);
|
|
33
9
|
}
|
|
34
10
|
//# sourceMappingURL=atomic-write.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"atomic-write.js","sourceRoot":"","sources":["../../../src/shared/fs/atomic-write.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"atomic-write.js","sourceRoot":"","sources":["../../../src/shared/fs/atomic-write.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,qGAAqG;AACrG,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAY,EAAE,OAAe;IAC7D,MAAM,KAAK,GAAG,GAAG,IAAI,IAAI,UAAU,EAAE,MAAM,CAAC;IAC5C,MAAM,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAChC,MAAM,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;AACjC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-exclusive.d.ts","sourceRoot":"","sources":["../../../src/shared/fs/create-exclusive.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"create-exclusive.d.ts","sourceRoot":"","sources":["../../../src/shared/fs/create-exclusive.ts"],"names":[],"mappings":"AAMA;;;GAGG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,oBAYlE"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { link, unlink, writeFile } from 'node:fs/promises';
|
|
3
3
|
import { isErrno } from "./errno.js";
|
|
4
|
+
import { patiently } from "./patiently.js";
|
|
4
5
|
/**
|
|
5
6
|
* Creates `path` holding `content` unless it already exists. Linking a fully
|
|
6
7
|
* written file makes creation atomic, so the file is never seen empty.
|
|
@@ -9,7 +10,7 @@ export async function createExclusive(path, content) {
|
|
|
9
10
|
const draft = `${path}.${randomUUID()}.tmp`;
|
|
10
11
|
await writeFile(draft, content);
|
|
11
12
|
try {
|
|
12
|
-
await link(draft, path);
|
|
13
|
+
await patiently(() => link(draft, path));
|
|
13
14
|
return true;
|
|
14
15
|
}
|
|
15
16
|
catch (error) {
|
|
@@ -18,7 +19,11 @@ export async function createExclusive(path, content) {
|
|
|
18
19
|
throw error;
|
|
19
20
|
}
|
|
20
21
|
finally {
|
|
21
|
-
await
|
|
22
|
+
await removeDraft(draft);
|
|
22
23
|
}
|
|
23
24
|
}
|
|
25
|
+
/** A draft left behind has a unique name, so it never blocks a key; failing to remove it must not change whether `path` was created. */
|
|
26
|
+
async function removeDraft(draft) {
|
|
27
|
+
await patiently(() => unlink(draft)).catch(() => { });
|
|
28
|
+
}
|
|
24
29
|
//# sourceMappingURL=create-exclusive.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-exclusive.js","sourceRoot":"","sources":["../../../src/shared/fs/create-exclusive.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE3D,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"create-exclusive.js","sourceRoot":"","sources":["../../../src/shared/fs/create-exclusive.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE3D,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAY,EAAE,OAAe;IACjE,MAAM,KAAK,GAAG,GAAG,IAAI,IAAI,UAAU,EAAE,MAAM,CAAC;IAC5C,MAAM,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAChC,IAAI,CAAC;QACH,MAAM,SAAS,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;QACzC,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC;YAAE,OAAO,KAAK,CAAC;QAC3C,MAAM,KAAK,CAAC;IACd,CAAC;YAAS,CAAC;QACT,MAAM,WAAW,CAAC,KAAK,CAAC,CAAC;IAC3B,CAAC;AACH,CAAC;AAED,wIAAwI;AACxI,KAAK,UAAU,WAAW,CAAC,KAAa;IACtC,MAAM,SAAS,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;AACvD,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Replaces `path` in one step like `atomicWrite`, and returns only once both
|
|
3
|
+
* the new content and the replacement survive a power loss: a counter that
|
|
4
|
+
* goes back after a crash would hand out a token twice.
|
|
5
|
+
*/
|
|
6
|
+
export declare function durableWrite(path: string, content: string): Promise<void>;
|
|
7
|
+
//# sourceMappingURL=durable-write.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"durable-write.d.ts","sourceRoot":"","sources":["../../../src/shared/fs/durable-write.ts"],"names":[],"mappings":"AAOA;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,iBAW/D"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { open, rm } from 'node:fs/promises';
|
|
3
|
+
import { dirname } from 'node:path';
|
|
4
|
+
import { isErrno } from "./errno.js";
|
|
5
|
+
import { replaceFile } from "./replace-file.js";
|
|
6
|
+
/**
|
|
7
|
+
* Replaces `path` in one step like `atomicWrite`, and returns only once both
|
|
8
|
+
* the new content and the replacement survive a power loss: a counter that
|
|
9
|
+
* goes back after a crash would hand out a token twice.
|
|
10
|
+
*/
|
|
11
|
+
export async function durableWrite(path, content) {
|
|
12
|
+
const draft = `${path}.${randomUUID()}.tmp`;
|
|
13
|
+
try {
|
|
14
|
+
await writeSynced(draft, content);
|
|
15
|
+
await replaceFile(draft, path);
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
// A draft that never replaced `path` would only pile up, one per failed write.
|
|
19
|
+
await rm(draft, { force: true });
|
|
20
|
+
throw error;
|
|
21
|
+
}
|
|
22
|
+
await syncDirectory(dirname(path));
|
|
23
|
+
}
|
|
24
|
+
async function writeSynced(path, content) {
|
|
25
|
+
await using handle = await open(path, 'wx');
|
|
26
|
+
await handle.writeFile(content);
|
|
27
|
+
await handle.sync();
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A rename lives in its directory, so the directory must reach the disk too.
|
|
31
|
+
* Windows cannot open a directory to sync it, and some file systems refuse to
|
|
32
|
+
* sync one; there the rename is as durable as that system makes it.
|
|
33
|
+
*/
|
|
34
|
+
async function syncDirectory(directory) {
|
|
35
|
+
if (process.platform === 'win32')
|
|
36
|
+
return;
|
|
37
|
+
await using handle = await open(directory, 'r');
|
|
38
|
+
try {
|
|
39
|
+
await handle.sync();
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
if (!isErrno(error, 'EINVAL') && !isErrno(error, 'ENOTSUP'))
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=durable-write.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"durable-write.js","sourceRoot":"","sources":["../../../src/shared/fs/durable-write.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAY,EAAE,OAAe;IAC9D,MAAM,KAAK,GAAG,GAAG,IAAI,IAAI,UAAU,EAAE,MAAM,CAAC;IAC5C,IAAI,CAAC;QACH,MAAM,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAClC,MAAM,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACjC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,+EAA+E;QAC/E,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACjC,MAAM,KAAK,CAAC;IACd,CAAC;IACD,MAAM,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;AACrC,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,IAAY,EAAE,OAAe;IACtD,YAAY,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC5C,MAAM,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAChC,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;AACtB,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,aAAa,CAAC,SAAiB;IAC5C,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO;QAAE,OAAO;IACzC,YAAY,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;IAChD,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;IACtB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,SAAS,CAAC;YAAE,MAAM,KAAK,CAAC;IAC3E,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patiently.d.ts","sourceRoot":"","sources":["../../../src/shared/fs/patiently.ts"],"names":[],"mappings":"AAoBA,iIAAiI;AACjI,wBAAsB,SAAS,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAc1E"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { setTimeout as delay } from 'node:timers/promises';
|
|
2
|
+
import { isErrno } from "./errno.js";
|
|
3
|
+
/** How long to retry a file operation that Windows refuses for a moment. */
|
|
4
|
+
const WINDOWS_PATIENCE = 1000;
|
|
5
|
+
/**
|
|
6
|
+
* Windows refuses a file for a moment while another program holds it open
|
|
7
|
+
* with no sharing, as a virus scanner can, or while a delete of it is in
|
|
8
|
+
* progress. The error code is the same as for a real permission denial, so a
|
|
9
|
+
* refusal is retried only for a limited time.
|
|
10
|
+
*/
|
|
11
|
+
function isRefusedForNow(error) {
|
|
12
|
+
return (process.platform === 'win32' &&
|
|
13
|
+
['EPERM', 'EACCES', 'EBUSY'].some((code) => isErrno(error, code)));
|
|
14
|
+
}
|
|
15
|
+
/** Runs `operation`, and runs it again while Windows refuses the file for a moment; after that, the error reaches the caller. */
|
|
16
|
+
export async function patiently(operation) {
|
|
17
|
+
const started = performance.now();
|
|
18
|
+
for (let attempt = 0;; attempt++) {
|
|
19
|
+
try {
|
|
20
|
+
return await operation();
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
if (!isRefusedForNow(error) ||
|
|
24
|
+
performance.now() - started > WINDOWS_PATIENCE)
|
|
25
|
+
throw error;
|
|
26
|
+
await delay(Math.min(5 * 2 ** attempt, 100));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=patiently.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patiently.js","sourceRoot":"","sources":["../../../src/shared/fs/patiently.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,IAAI,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAE3D,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAErC,4EAA4E;AAC5E,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAE9B;;;;;GAKG;AACH,SAAS,eAAe,CAAC,KAAc;IACrC,OAAO,CACL,OAAO,CAAC,QAAQ,KAAK,OAAO;QAC5B,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAClE,CAAC;AACJ,CAAC;AAED,iIAAiI;AACjI,MAAM,CAAC,KAAK,UAAU,SAAS,CAAI,SAA2B;IAC5D,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;IAClC,KAAK,IAAI,OAAO,GAAG,CAAC,GAAI,OAAO,EAAE,EAAE,CAAC;QAClC,IAAI,CAAC;YACH,OAAO,MAAM,SAAS,EAAE,CAAC;QAC3B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IACE,CAAC,eAAe,CAAC,KAAK,CAAC;gBACvB,WAAW,CAAC,GAAG,EAAE,GAAG,OAAO,GAAG,gBAAgB;gBAE9C,MAAM,KAAK,CAAC;YACd,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Moves `draft` over `path` in one step. On Windows, a rename over a file
|
|
3
|
+
* fails while another process has that file open, for example a waiter that
|
|
4
|
+
* reads it at that moment. The refusal is brief, so the rename is tried again;
|
|
5
|
+
* it stays the single commit point.
|
|
6
|
+
*/
|
|
7
|
+
export declare function replaceFile(draft: string, path: string): Promise<void>;
|
|
8
|
+
//# sourceMappingURL=replace-file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replace-file.d.ts","sourceRoot":"","sources":["../../../src/shared/fs/replace-file.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,iBAEtD"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { rename } from 'node:fs/promises';
|
|
2
|
+
import { patiently } from "./patiently.js";
|
|
3
|
+
/**
|
|
4
|
+
* Moves `draft` over `path` in one step. On Windows, a rename over a file
|
|
5
|
+
* fails while another process has that file open, for example a waiter that
|
|
6
|
+
* reads it at that moment. The refusal is brief, so the rename is tried again;
|
|
7
|
+
* it stays the single commit point.
|
|
8
|
+
*/
|
|
9
|
+
export function replaceFile(draft, path) {
|
|
10
|
+
return patiently(() => rename(draft, path));
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=replace-file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replace-file.js","sourceRoot":"","sources":["../../../src/shared/fs/replace-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,KAAa,EAAE,IAAY;IACrD,OAAO,SAAS,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;AAC9C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"latch.d.ts","sourceRoot":"","sources":["../../src/shared/latch.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,KAAK;;IAGhB,IAAI,IAAI,IAAI;IAIZ,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAGtB"}
|