@zukhruf/mutex 0.0.0-stage → 0.2.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.
Files changed (207) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +155 -2
  3. package/dist/fencing/counter-token-source.d.ts +11 -0
  4. package/dist/fencing/counter-token-source.d.ts.map +1 -0
  5. package/dist/fencing/counter-token-source.js +13 -0
  6. package/dist/fencing/counter-token-source.js.map +1 -0
  7. package/dist/fencing/epoch-token-source.d.ts +13 -0
  8. package/dist/fencing/epoch-token-source.d.ts.map +1 -0
  9. package/dist/fencing/epoch-token-source.js +27 -0
  10. package/dist/fencing/epoch-token-source.js.map +1 -0
  11. package/dist/fencing/fencing-token.d.ts +12 -0
  12. package/dist/fencing/fencing-token.d.ts.map +1 -0
  13. package/dist/fencing/fencing-token.js +18 -0
  14. package/dist/fencing/fencing-token.js.map +1 -0
  15. package/dist/fencing/file-token-source.d.ts +13 -0
  16. package/dist/fencing/file-token-source.d.ts.map +1 -0
  17. package/dist/fencing/file-token-source.js +35 -0
  18. package/dist/fencing/file-token-source.js.map +1 -0
  19. package/dist/fencing/monotonic-clock-token-source.d.ts +12 -0
  20. package/dist/fencing/monotonic-clock-token-source.d.ts.map +1 -0
  21. package/dist/fencing/monotonic-clock-token-source.js +15 -0
  22. package/dist/fencing/monotonic-clock-token-source.js.map +1 -0
  23. package/dist/fencing/token-source.d.ts +9 -0
  24. package/dist/fencing/token-source.d.ts.map +1 -0
  25. package/dist/fencing/token-source.js +2 -0
  26. package/dist/fencing/token-source.js.map +1 -0
  27. package/dist/index.d.ts +27 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +24 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/leader-election/index.d.ts +3 -0
  32. package/dist/leader-election/index.d.ts.map +1 -0
  33. package/dist/leader-election/index.js +3 -0
  34. package/dist/leader-election/index.js.map +1 -0
  35. package/dist/leader-election/leader-election.d.ts +23 -0
  36. package/dist/leader-election/leader-election.d.ts.map +1 -0
  37. package/dist/leader-election/leader-election.js +77 -0
  38. package/dist/leader-election/leader-election.js.map +1 -0
  39. package/dist/leader-election/leadership.d.ts +14 -0
  40. package/dist/leader-election/leadership.d.ts.map +1 -0
  41. package/dist/leader-election/leadership.js +23 -0
  42. package/dist/leader-election/leadership.js.map +1 -0
  43. package/dist/lock-stores/file-system/file-lock-store.d.ts +35 -0
  44. package/dist/lock-stores/file-system/file-lock-store.d.ts.map +1 -0
  45. package/dist/lock-stores/file-system/file-lock-store.js +64 -0
  46. package/dist/lock-stores/file-system/file-lock-store.js.map +1 -0
  47. package/dist/lock-stores/file-system/lock-file-store.d.ts +11 -0
  48. package/dist/lock-stores/file-system/lock-file-store.d.ts.map +1 -0
  49. package/dist/lock-stores/file-system/lock-file-store.js +44 -0
  50. package/dist/lock-stores/file-system/lock-file-store.js.map +1 -0
  51. package/dist/lock-stores/file-system/owner.d.ts +16 -0
  52. package/dist/lock-stores/file-system/owner.d.ts.map +1 -0
  53. package/dist/lock-stores/file-system/owner.js +47 -0
  54. package/dist/lock-stores/file-system/owner.js.map +1 -0
  55. package/dist/lock-stores/file-system/ticket-queue-file-store.d.ts +16 -0
  56. package/dist/lock-stores/file-system/ticket-queue-file-store.d.ts.map +1 -0
  57. package/dist/lock-stores/file-system/ticket-queue-file-store.js +111 -0
  58. package/dist/lock-stores/file-system/ticket-queue-file-store.js.map +1 -0
  59. package/dist/lock-stores/ipc/child-process-connection.d.ts +18 -0
  60. package/dist/lock-stores/ipc/child-process-connection.d.ts.map +1 -0
  61. package/dist/lock-stores/ipc/child-process-connection.js +46 -0
  62. package/dist/lock-stores/ipc/child-process-connection.js.map +1 -0
  63. package/dist/lock-stores/ipc/ipc-lock-coordinator.d.ts +24 -0
  64. package/dist/lock-stores/ipc/ipc-lock-coordinator.d.ts.map +1 -0
  65. package/dist/lock-stores/ipc/ipc-lock-coordinator.js +27 -0
  66. package/dist/lock-stores/ipc/ipc-lock-coordinator.js.map +1 -0
  67. package/dist/lock-stores/ipc/ipc-store.d.ts +15 -0
  68. package/dist/lock-stores/ipc/ipc-store.d.ts.map +1 -0
  69. package/dist/lock-stores/ipc/ipc-store.js +34 -0
  70. package/dist/lock-stores/ipc/ipc-store.js.map +1 -0
  71. package/dist/lock-stores/ipc/process-channel-connection.d.ts +18 -0
  72. package/dist/lock-stores/ipc/process-channel-connection.d.ts.map +1 -0
  73. package/dist/lock-stores/ipc/process-channel-connection.js +52 -0
  74. package/dist/lock-stores/ipc/process-channel-connection.js.map +1 -0
  75. package/dist/lock-stores/memory/memory-store.d.ts +14 -0
  76. package/dist/lock-stores/memory/memory-store.d.ts.map +1 -0
  77. package/dist/lock-stores/memory/memory-store.js +41 -0
  78. package/dist/lock-stores/memory/memory-store.js.map +1 -0
  79. package/dist/lock-stores/remote/connection.d.ts +17 -0
  80. package/dist/lock-stores/remote/connection.d.ts.map +1 -0
  81. package/dist/lock-stores/remote/connection.js +2 -0
  82. package/dist/lock-stores/remote/connection.js.map +1 -0
  83. package/dist/lock-stores/remote/connector.d.ts +12 -0
  84. package/dist/lock-stores/remote/connector.d.ts.map +1 -0
  85. package/dist/lock-stores/remote/connector.js +2 -0
  86. package/dist/lock-stores/remote/connector.js.map +1 -0
  87. package/dist/lock-stores/remote/coordinator-unavailable-error.d.ts +6 -0
  88. package/dist/lock-stores/remote/coordinator-unavailable-error.d.ts.map +1 -0
  89. package/dist/lock-stores/remote/coordinator-unavailable-error.js +10 -0
  90. package/dist/lock-stores/remote/coordinator-unavailable-error.js.map +1 -0
  91. package/dist/lock-stores/remote/envelope.d.ts +7 -0
  92. package/dist/lock-stores/remote/envelope.d.ts.map +1 -0
  93. package/dist/lock-stores/remote/envelope.js +11 -0
  94. package/dist/lock-stores/remote/envelope.js.map +1 -0
  95. package/dist/lock-stores/remote/lock-coordinator.d.ts +34 -0
  96. package/dist/lock-stores/remote/lock-coordinator.d.ts.map +1 -0
  97. package/dist/lock-stores/remote/lock-coordinator.js +150 -0
  98. package/dist/lock-stores/remote/lock-coordinator.js.map +1 -0
  99. package/dist/lock-stores/remote/protocol.d.ts +36 -0
  100. package/dist/lock-stores/remote/protocol.d.ts.map +1 -0
  101. package/dist/lock-stores/remote/protocol.js +33 -0
  102. package/dist/lock-stores/remote/protocol.js.map +1 -0
  103. package/dist/lock-stores/remote/remote-lock-client.d.ts +17 -0
  104. package/dist/lock-stores/remote/remote-lock-client.d.ts.map +1 -0
  105. package/dist/lock-stores/remote/remote-lock-client.js +172 -0
  106. package/dist/lock-stores/remote/remote-lock-client.js.map +1 -0
  107. package/dist/lock-stores/socket/electing-connector.d.ts +21 -0
  108. package/dist/lock-stores/socket/electing-connector.d.ts.map +1 -0
  109. package/dist/lock-stores/socket/electing-connector.js +45 -0
  110. package/dist/lock-stores/socket/electing-connector.js.map +1 -0
  111. package/dist/lock-stores/socket/lock-server.d.ts +23 -0
  112. package/dist/lock-stores/socket/lock-server.d.ts.map +1 -0
  113. package/dist/lock-stores/socket/lock-server.js +67 -0
  114. package/dist/lock-stores/socket/lock-server.js.map +1 -0
  115. package/dist/lock-stores/socket/socket-connection.d.ts +18 -0
  116. package/dist/lock-stores/socket/socket-connection.d.ts.map +1 -0
  117. package/dist/lock-stores/socket/socket-connection.js +54 -0
  118. package/dist/lock-stores/socket/socket-connection.js.map +1 -0
  119. package/dist/lock-stores/socket/socket-store.d.ts +35 -0
  120. package/dist/lock-stores/socket/socket-store.d.ts.map +1 -0
  121. package/dist/lock-stores/socket/socket-store.js +84 -0
  122. package/dist/lock-stores/socket/socket-store.js.map +1 -0
  123. package/dist/lock-stores/sqlite/sqlite-store.d.ts +21 -0
  124. package/dist/lock-stores/sqlite/sqlite-store.d.ts.map +1 -0
  125. package/dist/lock-stores/sqlite/sqlite-store.js +105 -0
  126. package/dist/lock-stores/sqlite/sqlite-store.js.map +1 -0
  127. package/dist/lock-stores/thread/parent-port-connection.d.ts +20 -0
  128. package/dist/lock-stores/thread/parent-port-connection.d.ts.map +1 -0
  129. package/dist/lock-stores/thread/parent-port-connection.js +44 -0
  130. package/dist/lock-stores/thread/parent-port-connection.js.map +1 -0
  131. package/dist/lock-stores/thread/thread-lock-coordinator.d.ts +22 -0
  132. package/dist/lock-stores/thread/thread-lock-coordinator.d.ts.map +1 -0
  133. package/dist/lock-stores/thread/thread-lock-coordinator.js +25 -0
  134. package/dist/lock-stores/thread/thread-lock-coordinator.js.map +1 -0
  135. package/dist/lock-stores/thread/thread-store.d.ts +13 -0
  136. package/dist/lock-stores/thread/thread-store.d.ts.map +1 -0
  137. package/dist/lock-stores/thread/thread-store.js +37 -0
  138. package/dist/lock-stores/thread/thread-store.js.map +1 -0
  139. package/dist/lock-stores/thread/worker-connection.d.ts +19 -0
  140. package/dist/lock-stores/thread/worker-connection.d.ts.map +1 -0
  141. package/dist/lock-stores/thread/worker-connection.js +45 -0
  142. package/dist/lock-stores/thread/worker-connection.js.map +1 -0
  143. package/dist/mutex/acquire-mode.d.ts +30 -0
  144. package/dist/mutex/acquire-mode.d.ts.map +1 -0
  145. package/dist/mutex/acquire-mode.js +2 -0
  146. package/dist/mutex/acquire-mode.js.map +1 -0
  147. package/dist/mutex/acquire-modes/modes.d.ts +9 -0
  148. package/dist/mutex/acquire-modes/modes.d.ts.map +1 -0
  149. package/dist/mutex/acquire-modes/modes.js +9 -0
  150. package/dist/mutex/acquire-modes/modes.js.map +1 -0
  151. package/dist/mutex/acquire-modes/skip-if-busy-mode.d.ts +15 -0
  152. package/dist/mutex/acquire-modes/skip-if-busy-mode.d.ts.map +1 -0
  153. package/dist/mutex/acquire-modes/skip-if-busy-mode.js +27 -0
  154. package/dist/mutex/acquire-modes/skip-if-busy-mode.js.map +1 -0
  155. package/dist/mutex/acquire-modes/wait-mode.d.ts +9 -0
  156. package/dist/mutex/acquire-modes/wait-mode.d.ts.map +1 -0
  157. package/dist/mutex/acquire-modes/wait-mode.js +8 -0
  158. package/dist/mutex/acquire-modes/wait-mode.js.map +1 -0
  159. package/dist/mutex/key.d.ts +22 -0
  160. package/dist/mutex/key.d.ts.map +1 -0
  161. package/dist/mutex/key.js +21 -0
  162. package/dist/mutex/key.js.map +1 -0
  163. package/dist/mutex/lease.d.ts +9 -0
  164. package/dist/mutex/lease.d.ts.map +1 -0
  165. package/dist/mutex/lease.js +12 -0
  166. package/dist/mutex/lease.js.map +1 -0
  167. package/dist/mutex/lock-lost-error.d.ts +6 -0
  168. package/dist/mutex/lock-lost-error.d.ts.map +1 -0
  169. package/dist/mutex/lock-lost-error.js +10 -0
  170. package/dist/mutex/lock-lost-error.js.map +1 -0
  171. package/dist/mutex/lock-store.d.ts +13 -0
  172. package/dist/mutex/lock-store.d.ts.map +1 -0
  173. package/dist/mutex/lock-store.js +2 -0
  174. package/dist/mutex/lock-store.js.map +1 -0
  175. package/dist/mutex/mutex.d.ts +25 -0
  176. package/dist/mutex/mutex.d.ts.map +1 -0
  177. package/dist/mutex/mutex.js +41 -0
  178. package/dist/mutex/mutex.js.map +1 -0
  179. package/dist/shared/fs/atomic-write.d.ts +3 -0
  180. package/dist/shared/fs/atomic-write.d.ts.map +1 -0
  181. package/dist/shared/fs/atomic-write.js +34 -0
  182. package/dist/shared/fs/atomic-write.js.map +1 -0
  183. package/dist/shared/fs/create-exclusive.d.ts +6 -0
  184. package/dist/shared/fs/create-exclusive.d.ts.map +1 -0
  185. package/dist/shared/fs/create-exclusive.js +24 -0
  186. package/dist/shared/fs/create-exclusive.js.map +1 -0
  187. package/dist/shared/fs/errno.d.ts +2 -0
  188. package/dist/shared/fs/errno.d.ts.map +1 -0
  189. package/dist/shared/fs/errno.js +4 -0
  190. package/dist/shared/fs/errno.js.map +1 -0
  191. package/dist/shared/fs/safe-file-name.d.ts +8 -0
  192. package/dist/shared/fs/safe-file-name.d.ts.map +1 -0
  193. package/dist/shared/fs/safe-file-name.js +10 -0
  194. package/dist/shared/fs/safe-file-name.js.map +1 -0
  195. package/dist/shared/is-record.d.ts +3 -0
  196. package/dist/shared/is-record.d.ts.map +1 -0
  197. package/dist/shared/is-record.js +3 -0
  198. package/dist/shared/is-record.js.map +1 -0
  199. package/dist/shared/sqlite/is-busy.d.ts +3 -0
  200. package/dist/shared/sqlite/is-busy.d.ts.map +1 -0
  201. package/dist/shared/sqlite/is-busy.js +10 -0
  202. package/dist/shared/sqlite/is-busy.js.map +1 -0
  203. package/dist/shared/until-aborted.d.ts +3 -0
  204. package/dist/shared/until-aborted.d.ts.map +1 -0
  205. package/dist/shared/until-aborted.js +19 -0
  206. package/dist/shared/until-aborted.js.map +1 -0
  207. package/package.json +50 -4
@@ -0,0 +1,105 @@
1
+ import { DatabaseSync } from 'node:sqlite';
2
+ import { isBusy } from "../../shared/sqlite/is-busy.js";
3
+ import { FileLockStore } from "../file-system/file-lock-store.js";
4
+ import { MemoryStore } from "../memory/memory-store.js";
5
+ /**
6
+ * Holds an exclusive SQLite transaction on the key's database file. SQLite
7
+ * locks through the kernel, which releases them when the holder dies, so a
8
+ * crash never leaves a key locked. Keep the default rollback journal: in WAL
9
+ * mode an exclusive transaction no longer blocks readers.
10
+ *
11
+ * Callers in this process first line up in a queue in memory, so only the
12
+ * first one opens a database connection: one open file per key per process,
13
+ * however many callers wait, and first-come order within the process.
14
+ */
15
+ export class SqliteStore extends FileLockStore {
16
+ #inProcess = new MemoryStore();
17
+ async acquire(key, options = {}) {
18
+ // Joining the in-process queue is synchronous, so the order is the order of the calls.
19
+ const turn = await this.#inProcess.acquire(key, options);
20
+ try {
21
+ return withTurn(await super.acquire(key, options), turn);
22
+ }
23
+ catch (error) {
24
+ await turn[Symbol.asyncDispose]();
25
+ throw error;
26
+ }
27
+ }
28
+ async tryAcquire(key) {
29
+ const turn = await this.#inProcess.tryAcquire(key);
30
+ if (!turn)
31
+ return undefined;
32
+ try {
33
+ const lease = await super.tryAcquire(key);
34
+ if (lease)
35
+ return withTurn(lease, turn);
36
+ }
37
+ catch (error) {
38
+ await turn[Symbol.asyncDispose]();
39
+ throw error;
40
+ }
41
+ await turn[Symbol.asyncDispose]();
42
+ return undefined;
43
+ }
44
+ async lock(path, signal) {
45
+ const database = new DatabaseSync(path, { timeout: 0 });
46
+ try {
47
+ await this.poll(async () => (begin(database) ? true : undefined), {
48
+ signal,
49
+ });
50
+ }
51
+ catch (error) {
52
+ database.close();
53
+ throw error;
54
+ }
55
+ return holding(database);
56
+ }
57
+ async tryLock(path) {
58
+ const database = new DatabaseSync(path, { timeout: 0 });
59
+ try {
60
+ if (begin(database))
61
+ return holding(database);
62
+ }
63
+ catch (error) {
64
+ database.close();
65
+ throw error;
66
+ }
67
+ database.close();
68
+ return undefined;
69
+ }
70
+ }
71
+ /** Starts the exclusive transaction, or reports that another connection holds it. */
72
+ function begin(database) {
73
+ try {
74
+ database.exec('BEGIN EXCLUSIVE');
75
+ return true;
76
+ }
77
+ catch (error) {
78
+ if (isBusy(error))
79
+ return false;
80
+ throw error;
81
+ }
82
+ }
83
+ function holding(database) {
84
+ return {
85
+ [Symbol.asyncDispose]: async () => {
86
+ database.exec('ROLLBACK');
87
+ database.close();
88
+ },
89
+ };
90
+ }
91
+ /** Releases the database lock first, then lets the next caller in this process try. */
92
+ function withTurn(lease, turn) {
93
+ return {
94
+ token: lease.token,
95
+ [Symbol.asyncDispose]: async () => {
96
+ try {
97
+ await lease[Symbol.asyncDispose]();
98
+ }
99
+ finally {
100
+ await turn[Symbol.asyncDispose]();
101
+ }
102
+ },
103
+ };
104
+ }
105
+ //# sourceMappingURL=sqlite-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite-store.js","sourceRoot":"","sources":["../../../src/lock-stores/sqlite/sqlite-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { MessagePort } from 'node:worker_threads';
2
+ import type { Connection, ConnectionHandlers } from '../remote/connection.ts';
3
+ import { type LockRequest, type LockResponse } from '../remote/protocol.ts';
4
+ /**
5
+ * A worker thread's end of the message port to the thread that started it.
6
+ * A `message` listener keeps the worker alive, so this adapter listens only
7
+ * while referenced; messages that arrive meanwhile wait in the port. The
8
+ * coordinator cannot stop without its workers stopping too, so `close` is
9
+ * never reported.
10
+ */
11
+ export declare class ParentPortConnection implements Connection<LockRequest, LockResponse> {
12
+ #private;
13
+ constructor(port: MessagePort);
14
+ send(request: LockRequest): Promise<void>;
15
+ listen(handlers: ConnectionHandlers<LockResponse>): void;
16
+ ref(): void;
17
+ unref(): void;
18
+ close(): void;
19
+ }
20
+ //# sourceMappingURL=parent-port-connection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parent-port-connection.d.ts","sourceRoot":"","sources":["../../../src/lock-stores/thread/parent-port-connection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAE9E,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,YAAY,EAElB,MAAM,uBAAuB,CAAC;AAE/B;;;;;;GAMG;AACH,qBAAa,oBAAqB,YAAW,UAAU,CACrD,WAAW,EACX,YAAY,CACb;;gBAUa,IAAI,EAAE,WAAW;IAIvB,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/C,MAAM,CAAC,QAAQ,EAAE,kBAAkB,CAAC,YAAY,CAAC;IAIjD,GAAG;IAMH,KAAK;IAML,KAAK;CAGN"}
@@ -0,0 +1,44 @@
1
+ import { unwrap, wrap } from "../remote/envelope.js";
2
+ import { isLockResponse, } from "../remote/protocol.js";
3
+ /**
4
+ * A worker thread's end of the message port to the thread that started it.
5
+ * A `message` listener keeps the worker alive, so this adapter listens only
6
+ * while referenced; messages that arrive meanwhile wait in the port. The
7
+ * coordinator cannot stop without its workers stopping too, so `close` is
8
+ * never reported.
9
+ */
10
+ export class ParentPortConnection {
11
+ #port;
12
+ #handlers;
13
+ #listening = false;
14
+ #onMessage = (envelope) => {
15
+ const response = unwrap(envelope);
16
+ if (isLockResponse(response))
17
+ this.#handlers?.message(response);
18
+ };
19
+ constructor(port) {
20
+ this.#port = port;
21
+ }
22
+ async send(request) {
23
+ this.#port.postMessage(wrap(request));
24
+ }
25
+ listen(handlers) {
26
+ this.#handlers = handlers;
27
+ }
28
+ ref() {
29
+ if (this.#listening)
30
+ return;
31
+ this.#listening = true;
32
+ this.#port.on('message', this.#onMessage);
33
+ }
34
+ unref() {
35
+ if (!this.#listening)
36
+ return;
37
+ this.#listening = false;
38
+ this.#port.off('message', this.#onMessage);
39
+ }
40
+ close() {
41
+ this.unref();
42
+ }
43
+ }
44
+ //# sourceMappingURL=parent-port-connection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parent-port-connection.js","sourceRoot":"","sources":["../../../src/lock-stores/thread/parent-port-connection.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAGL,cAAc,GACf,MAAM,uBAAuB,CAAC;AAE/B;;;;;;GAMG;AACH,MAAM,OAAO,oBAAoB;IAItB,KAAK,CAAc;IAC5B,SAAS,CAA+C;IACxD,UAAU,GAAG,KAAK,CAAC;IAEV,UAAU,GAAG,CAAC,QAAiB,EAAE,EAAE;QAC1C,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;QAClC,IAAI,cAAc,CAAC,QAAQ,CAAC;YAAE,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IAClE,CAAC,CAAC;IAEF,YAAY,IAAiB;QAC3B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IACpB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,OAAoB;QAC7B,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IACxC,CAAC;IAED,MAAM,CAAC,QAA0C;QAC/C,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC;IAC5B,CAAC;IAED,GAAG;QACD,IAAI,IAAI,CAAC,UAAU;YAAE,OAAO;QAC5B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IAC5C,CAAC;IAED,KAAK;QACH,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE,OAAO;QAC7B,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;QACxB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK;QACH,IAAI,CAAC,KAAK,EAAE,CAAC;IACf,CAAC;CACF"}
@@ -0,0 +1,22 @@
1
+ import type { Worker } from 'node:worker_threads';
2
+ import type { TokenSource } from '../../fencing/token-source.ts';
3
+ import type { Lease } from '../../mutex/lease.ts';
4
+ import type { AcquireOptions, LockStore } from '../../mutex/lock-store.ts';
5
+ export interface ThreadLockCoordinatorOptions {
6
+ /** Defaults to an in-memory counter, which lives as long as this process. */
7
+ tokens?: TokenSource;
8
+ }
9
+ /**
10
+ * Grants keys to the thread that owns it and to every worker thread it adopts,
11
+ * which use `ThreadStore`. One thread decides every grant, so two threads can
12
+ * never both be granted a key. Its reach is one process.
13
+ */
14
+ export declare class ThreadLockCoordinator implements LockStore {
15
+ #private;
16
+ constructor({ tokens, }?: ThreadLockCoordinatorOptions);
17
+ acquire(key: string, options?: AcquireOptions): Promise<Lease>;
18
+ tryAcquire(key: string): Promise<Lease | undefined>;
19
+ /** Serves `worker`. When the thread stops, everything it held or waited for is released. */
20
+ adopt(worker: Worker): void;
21
+ }
22
+ //# sourceMappingURL=thread-lock-coordinator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thread-lock-coordinator.d.ts","sourceRoot":"","sources":["../../../src/lock-stores/thread/thread-lock-coordinator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAGlD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAI3E,MAAM,WAAW,4BAA4B;IAC3C,6EAA6E;IAC7E,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;;;GAIG;AACH,qBAAa,qBAAsB,YAAW,SAAS;;gBAGzC,EACV,MAAiC,GAClC,GAAE,4BAAiC;IAIpC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,KAAK,CAAC;IAI9D,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC;IAInD,4FAA4F;IAC5F,KAAK,CAAC,MAAM,EAAE,MAAM;CAGrB"}
@@ -0,0 +1,25 @@
1
+ import { CounterTokenSource } from "../../fencing/counter-token-source.js";
2
+ import { LockCoordinator } from "../remote/lock-coordinator.js";
3
+ import { WorkerConnection } from "./worker-connection.js";
4
+ /**
5
+ * Grants keys to the thread that owns it and to every worker thread it adopts,
6
+ * which use `ThreadStore`. One thread decides every grant, so two threads can
7
+ * never both be granted a key. Its reach is one process.
8
+ */
9
+ export class ThreadLockCoordinator {
10
+ #coordinator;
11
+ constructor({ tokens = new CounterTokenSource(), } = {}) {
12
+ this.#coordinator = new LockCoordinator({ tokens });
13
+ }
14
+ acquire(key, options) {
15
+ return this.#coordinator.acquire(key, options);
16
+ }
17
+ tryAcquire(key) {
18
+ return this.#coordinator.tryAcquire(key);
19
+ }
20
+ /** Serves `worker`. When the thread stops, everything it held or waited for is released. */
21
+ adopt(worker) {
22
+ this.#coordinator.serve(new WorkerConnection(worker));
23
+ }
24
+ }
25
+ //# sourceMappingURL=thread-lock-coordinator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thread-lock-coordinator.js","sourceRoot":"","sources":["../../../src/lock-stores/thread/thread-lock-coordinator.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAI3E,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAO1D;;;;GAIG;AACH,MAAM,OAAO,qBAAqB;IACvB,YAAY,CAAkB;IAEvC,YAAY,EACV,MAAM,GAAG,IAAI,kBAAkB,EAAE,MACD,EAAE;QAClC,IAAI,CAAC,YAAY,GAAG,IAAI,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,OAAO,CAAC,GAAW,EAAE,OAAwB;QAC3C,OAAO,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACjD,CAAC;IAED,UAAU,CAAC,GAAW;QACpB,OAAO,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IAC3C,CAAC;IAED,4FAA4F;IAC5F,KAAK,CAAC,MAAc;QAClB,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC;IACxD,CAAC;CACF"}
@@ -0,0 +1,13 @@
1
+ import type { Lease } from '../../mutex/lease.ts';
2
+ import type { AcquireOptions, LockStore } from '../../mutex/lock-store.ts';
3
+ /**
4
+ * The worker-thread side of `ThreadLockCoordinator`: asks the thread that
5
+ * started this worker for keys, through the worker's message port.
6
+ */
7
+ export declare class ThreadStore implements LockStore {
8
+ #private;
9
+ constructor();
10
+ acquire(key: string, options?: AcquireOptions): Promise<Lease>;
11
+ tryAcquire(key: string): Promise<Lease | undefined>;
12
+ }
13
+ //# sourceMappingURL=thread-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thread-store.d.ts","sourceRoot":"","sources":["../../../src/lock-stores/thread/thread-store.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAqB3E;;;GAGG;AACH,qBAAa,WAAY,YAAW,SAAS;;;IAY3C,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,KAAK,CAAC;IAI9D,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC;CAGpD"}
@@ -0,0 +1,37 @@
1
+ import { parentPort } from 'node:worker_threads';
2
+ import { RemoteLockClient } from "../remote/remote-lock-client.js";
3
+ import { ParentPortConnection } from "./parent-port-connection.js";
4
+ /** A worker has one port to its parent thread for its whole life. */
5
+ class ParentPortConnector {
6
+ #port;
7
+ #handedOut = false;
8
+ constructor(port) {
9
+ this.#port = port;
10
+ }
11
+ async connect() {
12
+ if (this.#handedOut)
13
+ return undefined;
14
+ this.#handedOut = true;
15
+ return new ParentPortConnection(this.#port);
16
+ }
17
+ }
18
+ /**
19
+ * The worker-thread side of `ThreadLockCoordinator`: asks the thread that
20
+ * started this worker for keys, through the worker's message port.
21
+ */
22
+ export class ThreadStore {
23
+ #client;
24
+ constructor() {
25
+ if (!parentPort) {
26
+ throw new Error('ThreadStore runs in a worker thread; use ThreadLockCoordinator in the thread that starts the workers.');
27
+ }
28
+ this.#client = new RemoteLockClient(new ParentPortConnector(parentPort));
29
+ }
30
+ acquire(key, options) {
31
+ return this.#client.acquire(key, options);
32
+ }
33
+ tryAcquire(key) {
34
+ return this.#client.tryAcquire(key);
35
+ }
36
+ }
37
+ //# sourceMappingURL=thread-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thread-store.js","sourceRoot":"","sources":["../../../src/lock-stores/thread/thread-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAKnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAEnE,qEAAqE;AACrE,MAAM,mBAAmB;IACd,KAAK,CAAc;IAC5B,UAAU,GAAG,KAAK,CAAC;IAEnB,YAAY,IAAiB;QAC3B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IACpB,CAAC;IAED,KAAK,CAAC,OAAO;QACX,IAAI,IAAI,CAAC,UAAU;YAAE,OAAO,SAAS,CAAC;QACtC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,OAAO,IAAI,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9C,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,OAAO,WAAW;IACb,OAAO,CAAmB;IAEnC;QACE,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CACb,uGAAuG,CACxG,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,OAAO,GAAG,IAAI,gBAAgB,CAAC,IAAI,mBAAmB,CAAC,UAAU,CAAC,CAAC,CAAC;IAC3E,CAAC;IAED,OAAO,CAAC,GAAW,EAAE,OAAwB;QAC3C,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC5C,CAAC;IAED,UAAU,CAAC,GAAW;QACpB,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IACtC,CAAC;CACF"}
@@ -0,0 +1,19 @@
1
+ import type { Worker } from 'node:worker_threads';
2
+ import type { Connection, ConnectionHandlers } from '../remote/connection.ts';
3
+ import { type LockRequest, type LockResponse } from '../remote/protocol.ts';
4
+ /**
5
+ * The coordinator's end of the message port to one worker thread. `Worker`
6
+ * emits `exit` however the thread stops, so a terminated or crashed holder
7
+ * is always noticed.
8
+ */
9
+ export declare class WorkerConnection implements Connection<LockResponse, LockRequest> {
10
+ #private;
11
+ constructor(worker: Worker);
12
+ send(response: LockResponse): Promise<void>;
13
+ listen({ message, close }: ConnectionHandlers<LockRequest>): void;
14
+ /** The running worker already keeps its starting thread alive. */
15
+ ref(): void;
16
+ unref(): void;
17
+ close(): void;
18
+ }
19
+ //# sourceMappingURL=worker-connection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-connection.d.ts","sourceRoot":"","sources":["../../../src/lock-stores/thread/worker-connection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAElD,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAE9E,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,YAAY,EAElB,MAAM,uBAAuB,CAAC;AAE/B;;;;GAIG;AACH,qBAAa,gBAAiB,YAAW,UAAU,CAAC,YAAY,EAAE,WAAW,CAAC;;gBAKhE,MAAM,EAAE,MAAM;IAIpB,IAAI,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAKjD,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,kBAAkB,CAAC,WAAW,CAAC;IAkB1D,kEAAkE;IAClE,GAAG;IAEH,KAAK;IAEL,KAAK;CAGN"}
@@ -0,0 +1,45 @@
1
+ import { unwrap, wrap } from "../remote/envelope.js";
2
+ import { isLockRequest, } from "../remote/protocol.js";
3
+ /**
4
+ * The coordinator's end of the message port to one worker thread. `Worker`
5
+ * emits `exit` however the thread stops, so a terminated or crashed holder
6
+ * is always noticed.
7
+ */
8
+ export class WorkerConnection {
9
+ #worker;
10
+ #exited = false;
11
+ #detach = () => { };
12
+ constructor(worker) {
13
+ this.#worker = worker;
14
+ }
15
+ async send(response) {
16
+ if (this.#exited)
17
+ throw new Error('The worker thread has exited.');
18
+ this.#worker.postMessage(wrap(response));
19
+ }
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
+ /** The running worker already keeps its starting thread alive. */
39
+ ref() { }
40
+ unref() { }
41
+ close() {
42
+ this.#detach();
43
+ }
44
+ }
45
+ //# sourceMappingURL=worker-connection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-connection.js","sourceRoot":"","sources":["../../../src/lock-stores/thread/worker-connection.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAGL,aAAa,GACd,MAAM,uBAAuB,CAAC;AAE/B;;;;GAIG;AACH,MAAM,OAAO,gBAAgB;IAClB,OAAO,CAAS;IACzB,OAAO,GAAG,KAAK,CAAC;IAChB,OAAO,GAAG,GAAG,EAAE,GAAE,CAAC,CAAC;IAEnB,YAAY,MAAc;QACxB,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;IACxB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,QAAsB;QAC/B,IAAI,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;QACnE,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC3C,CAAC;IAED,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,EAAmC;QACxD,MAAM,SAAS,GAAG,CAAC,QAAiB,EAAE,EAAE;YACtC,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;YACjC,IAAI,aAAa,CAAC,OAAO,CAAC;gBAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QAC/C,CAAC,CAAC;QACF,MAAM,MAAM,GAAG,GAAG,EAAE;YAClB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;YACpB,IAAI,CAAC,KAAK,EAAE,CAAC;YACb,KAAK,EAAE,CAAC;QACV,CAAC,CAAC;QACF,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QACtC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAClC,IAAI,CAAC,OAAO,GAAG,GAAG,EAAE;YAClB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;YACvC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACnC,CAAC,CAAC;IACJ,CAAC;IAED,kEAAkE;IAClE,GAAG,KAAI,CAAC;IAER,KAAK,KAAI,CAAC;IAEV,KAAK;QACH,IAAI,CAAC,OAAO,EAAE,CAAC;IACjB,CAAC;CACF"}
@@ -0,0 +1,30 @@
1
+ import type { Lease } from './lease.ts';
2
+ import type { LockStore } from './lock-store.ts';
3
+ /** Whether an acquire mode always ends with the key held, or may give up. */
4
+ export type Outcome = 'always' | 'maybe';
5
+ /**
6
+ * What one caller does while its key is busy. Exclusivity never depends on
7
+ * the acquire mode, so callers of one key can use different modes.
8
+ */
9
+ export interface AcquireMode<O extends Outcome = Outcome> {
10
+ readonly outcome: O;
11
+ /** Resolves with a lease, or `undefined` when this caller gives up. */
12
+ acquire(store: LockStore, key: string): Promise<Lease | undefined>;
13
+ }
14
+ export interface Acquired<T> {
15
+ readonly acquired: true;
16
+ readonly value: T;
17
+ }
18
+ export interface NotAcquired {
19
+ readonly acquired: false;
20
+ }
21
+ /** What a run gives back, for each outcome. */
22
+ export interface OutcomeResults<T> {
23
+ /** A mode that always acquires gives the task's value. */
24
+ always: T;
25
+ /** A mode that may give up says whether it acquired. */
26
+ maybe: Acquired<T> | NotAcquired;
27
+ }
28
+ /** The result of a run with mode `M`. */
29
+ export type ModeResult<M extends AcquireMode, T> = OutcomeResults<T>[M['outcome']];
30
+ //# sourceMappingURL=acquire-mode.d.ts.map
@@ -0,0 +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;AAEjD,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,uEAAuE;IACvE,OAAO,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC;CACpE;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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=acquire-mode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acquire-mode.js","sourceRoot":"","sources":["../../src/mutex/acquire-mode.ts"],"names":[],"mappings":""}
@@ -0,0 +1,9 @@
1
+ import { SkipIfBusyMode, type SkipIfBusyOptions } from './skip-if-busy-mode.ts';
2
+ import { WaitMode } from './wait-mode.ts';
3
+ export declare const Modes: {
4
+ /** Wait until the key is granted. The task always runs. */
5
+ wait: () => WaitMode;
6
+ /** Give up when the key stays busy, at once or after `waitAtMost` ms. The task may not run. */
7
+ skipIfBusy: (options?: SkipIfBusyOptions) => SkipIfBusyMode;
8
+ };
9
+ //# sourceMappingURL=modes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modes.d.ts","sourceRoot":"","sources":["../../../src/mutex/acquire-modes/modes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChF,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,eAAO,MAAM,KAAK;IAChB,2DAA2D;;IAE3D,+FAA+F;2BACxE,iBAAiB;CACzC,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { SkipIfBusyMode } from "./skip-if-busy-mode.js";
2
+ import { WaitMode } from "./wait-mode.js";
3
+ export const Modes = {
4
+ /** Wait until the key is granted. The task always runs. */
5
+ wait: () => new WaitMode(),
6
+ /** Give up when the key stays busy, at once or after `waitAtMost` ms. The task may not run. */
7
+ skipIfBusy: (options) => new SkipIfBusyMode(options),
8
+ };
9
+ //# sourceMappingURL=modes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modes.js","sourceRoot":"","sources":["../../../src/mutex/acquire-modes/modes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAA0B,MAAM,wBAAwB,CAAC;AAChF,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,2DAA2D;IAC3D,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,QAAQ,EAAE;IAC1B,+FAA+F;IAC/F,UAAU,EAAE,CAAC,OAA2B,EAAE,EAAE,CAAC,IAAI,cAAc,CAAC,OAAO,CAAC;CACzE,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { AcquireMode } from '../acquire-mode.ts';
2
+ import type { Lease } from '../lease.ts';
3
+ import type { LockStore } from '../lock-store.ts';
4
+ export interface SkipIfBusyOptions {
5
+ /** Milliseconds to wait for a busy key before giving up. Defaults to 0: one attempt only. */
6
+ waitAtMost?: number;
7
+ }
8
+ /** Gives up when the key stays busy, at once or after `waitAtMost`, so the task may not run. */
9
+ export declare class SkipIfBusyMode implements AcquireMode<'maybe'> {
10
+ #private;
11
+ readonly outcome: "maybe";
12
+ constructor({ waitAtMost }?: SkipIfBusyOptions);
13
+ acquire(store: LockStore, key: string): Promise<Lease | undefined>;
14
+ }
15
+ //# sourceMappingURL=skip-if-busy-mode.d.ts.map
@@ -0,0 +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;AAElD,MAAM,WAAW,iBAAiB;IAChC,6FAA6F;IAC7F,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,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC;CAazE"}
@@ -0,0 +1,27 @@
1
+ /** Gives up when the key stays busy, at once or after `waitAtMost`, so the task may not run. */
2
+ export class SkipIfBusyMode {
3
+ outcome = 'maybe';
4
+ #waitAtMost;
5
+ constructor({ waitAtMost = 0 } = {}) {
6
+ if (!(waitAtMost >= 0) || !Number.isFinite(waitAtMost)) {
7
+ throw new RangeError(`waitAtMost must be a finite number of milliseconds >= 0, got ${waitAtMost}.`);
8
+ }
9
+ this.#waitAtMost = waitAtMost;
10
+ }
11
+ async acquire(store, key) {
12
+ // Always one attempt first: a time limit must never skip a key that is free.
13
+ const lease = await store.tryAcquire(key);
14
+ if (lease || this.#waitAtMost === 0)
15
+ return lease;
16
+ const signal = AbortSignal.timeout(this.#waitAtMost);
17
+ try {
18
+ return await store.acquire(key, { signal });
19
+ }
20
+ catch (error) {
21
+ if (error === signal.reason)
22
+ return undefined;
23
+ throw error;
24
+ }
25
+ }
26
+ }
27
+ //# sourceMappingURL=skip-if-busy-mode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skip-if-busy-mode.js","sourceRoot":"","sources":["../../../src/mutex/acquire-modes/skip-if-busy-mode.ts"],"names":[],"mappings":"AASA,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,CAAC,KAAgB,EAAE,GAAW;QACzC,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,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACrD,IAAI,CAAC;YACH,OAAO,MAAM,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QAC9C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,KAAK,MAAM,CAAC,MAAM;gBAAE,OAAO,SAAS,CAAC;YAC9C,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,9 @@
1
+ import type { AcquireMode } from '../acquire-mode.ts';
2
+ import type { Lease } from '../lease.ts';
3
+ import type { LockStore } from '../lock-store.ts';
4
+ /** Waits until the key is granted, so the task always runs. */
5
+ export declare class WaitMode implements AcquireMode<'always'> {
6
+ readonly outcome: "always";
7
+ acquire(store: LockStore, key: string): Promise<Lease>;
8
+ }
9
+ //# sourceMappingURL=wait-mode.d.ts.map
@@ -0,0 +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;AAElD,+DAA+D;AAC/D,qBAAa,QAAS,YAAW,WAAW,CAAC,QAAQ,CAAC;IACpD,QAAQ,CAAC,OAAO,EAAG,QAAQ,CAAU;IAErC,OAAO,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;CAGvD"}
@@ -0,0 +1,8 @@
1
+ /** Waits until the key is granted, so the task always runs. */
2
+ export class WaitMode {
3
+ outcome = 'always';
4
+ acquire(store, key) {
5
+ return store.acquire(key);
6
+ }
7
+ }
8
+ //# sourceMappingURL=wait-mode.js.map
@@ -0,0 +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,CAAC,KAAgB,EAAE,GAAW;QACnC,OAAO,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC;CACF"}
@@ -0,0 +1,22 @@
1
+ import type { AcquireMode, Outcome, OutcomeResults } from './acquire-mode.ts';
2
+ import type { Lease } from './lease.ts';
3
+ import type { Mutex } from './mutex.ts';
4
+ /**
5
+ * A key with the acquire mode that its callers use unless one call says
6
+ * otherwise. Build it with `mutex.key(name, { mode })`. `O` is the outcome of
7
+ * that mode.
8
+ */
9
+ export declare class Key<O extends Outcome> {
10
+ #private;
11
+ readonly name: string;
12
+ constructor(mutex: Mutex, name: string, mode: AcquireMode<O>);
13
+ /** Runs `task` with the key's own acquire mode. */
14
+ run<T>(task: (lease: Lease) => Promise<T>, options?: {
15
+ mode?: undefined;
16
+ }): Promise<OutcomeResults<T>[O]>;
17
+ /** 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
+ mode: AcquireMode<P>;
20
+ }): Promise<OutcomeResults<T>[P]>;
21
+ }
22
+ //# sourceMappingURL=key.d.ts.map
@@ -0,0 +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,GAC7B,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;QAAE,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,CAAA;KAAE,GAChC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CASjC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * A key with the acquire mode that its callers use unless one call says
3
+ * otherwise. Build it with `mutex.key(name, { mode })`. `O` is the outcome of
4
+ * that mode.
5
+ */
6
+ export class Key {
7
+ name;
8
+ #mutex;
9
+ #mode;
10
+ constructor(mutex, name, mode) {
11
+ this.#mutex = mutex;
12
+ this.name = name;
13
+ this.#mode = mode;
14
+ }
15
+ run(task, { mode } = {}) {
16
+ return mode === undefined
17
+ ? this.#mutex.acquire(this.name, task, { mode: this.#mode })
18
+ : this.#mutex.acquire(this.name, task, { mode });
19
+ }
20
+ }
21
+ //# sourceMappingURL=key.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"key.js","sourceRoot":"","sources":["../../src/mutex/key.ts"],"names":[],"mappings":"AAIA;;;;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,EAAE,IAAI,KAA4C,EAAE;QAEpD,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,CAAC;YAC5D,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,CAAC;CACF"}
@@ -0,0 +1,9 @@
1
+ import type { FencingToken } from '../fencing/fencing-token.ts';
2
+ import type { TokenSource } from '../fencing/token-source.ts';
3
+ /** Proof of holding a key: pass `token` to fenced resources; dispose to release. */
4
+ export interface Lease extends AsyncDisposable {
5
+ readonly token: FencingToken;
6
+ }
7
+ /** Mints the token for a lock that is already held, releasing the lock if minting fails. */
8
+ export declare function leaseFor(key: string, held: AsyncDisposable, tokens: TokenSource): Promise<Lease>;
9
+ //# sourceMappingURL=lease.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lease.d.ts","sourceRoot":"","sources":["../../src/mutex/lease.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAE9D,oFAAoF;AACpF,MAAM,WAAW,KAAM,SAAQ,eAAe;IAC5C,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;CAC9B;AAED,4FAA4F;AAC5F,wBAAsB,QAAQ,CAC5B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,eAAe,EACrB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,KAAK,CAAC,CAQhB"}
@@ -0,0 +1,12 @@
1
+ /** Mints the token for a lock that is already held, releasing the lock if minting fails. */
2
+ export async function leaseFor(key, held, tokens) {
3
+ try {
4
+ const token = await tokens.next(key);
5
+ return { token, [Symbol.asyncDispose]: () => held[Symbol.asyncDispose]() };
6
+ }
7
+ catch (error) {
8
+ await held[Symbol.asyncDispose]();
9
+ throw error;
10
+ }
11
+ }
12
+ //# sourceMappingURL=lease.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lease.js","sourceRoot":"","sources":["../../src/mutex/lease.ts"],"names":[],"mappings":"AAQA,4FAA4F;AAC5F,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,GAAW,EACX,IAAqB,EACrB,MAAmB;IAEnB,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACrC,OAAO,EAAE,KAAK,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,EAAE,CAAC;IAC7E,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC;QAClC,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ /** The key may have been granted to another holder while this lease was still in use. */
2
+ export declare class LockLostError extends Error {
3
+ readonly key: string;
4
+ constructor(key: string);
5
+ }
6
+ //# sourceMappingURL=lock-lost-error.d.ts.map