@matter/general 0.16.0-alpha.0-20251108-514b3f69e → 0.16.0-alpha.0-20251111-11cc8c3bd

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.
@@ -3,12 +3,20 @@
3
3
  * Copyright 2022-2025 Matter.js Authors
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
+ import { MatterError } from "#MatterError.js";
7
+ export declare class MutexClosedError extends MatterError {
8
+ constructor();
9
+ }
6
10
  /**
7
11
  * A mutex is a task queue where at most one task is active at a time.
8
12
  */
9
13
  export declare class Mutex implements PromiseLike<unknown> {
10
14
  #private;
11
15
  constructor(owner: {}, initial?: PromiseLike<unknown>);
16
+ /**
17
+ * Prevent new tasks and wait for remaining tasks to complete.
18
+ */
19
+ close(): Promise<void>;
12
20
  /**
13
21
  * As a PromiseLike, you can await the Mutex. This promise resolves when current activity completes but the mutex
14
22
  * may engage in another activity immediately thereafter. So the mutex is not guaranteed to be available after an
@@ -21,15 +29,23 @@ export declare class Mutex implements PromiseLike<unknown> {
21
29
  * If {@link task} is a function it runs when current activity completes. If it is a promise then the mutex will
22
30
  * not clear until {@link task} resolves.
23
31
  */
24
- run(task: PromiseLike<unknown> | (() => PromiseLike<unknown>), cancel?: () => void): void;
32
+ run(task: PromiseLike<unknown> | (() => PromiseLike<unknown>)): void;
25
33
  /**
26
34
  * Enqueue work with an awaitable result.
27
35
  */
28
- produce<T>(task: () => PromiseLike<T>, cancel?: () => void): Promise<T>;
36
+ produce<T>(task: () => PromiseLike<T>): Promise<T>;
29
37
  /**
30
- * Cancel remaining work and perform one last task with the Mutex held.
38
+ * Acquire the lock.
39
+ *
40
+ * This offers more natural mutex handling via a disposable. The returned object must be disposed to unlock the
41
+ * mutex.
42
+ *
43
+ * Note that acquiring the lock is async but releasing is not, so you must use `using _lock = await mutex.lock()`
44
+ * rather than `await using _lock = mutex.lock()`.
45
+ *
46
+ * TODO - add abort support
31
47
  */
32
- terminate(cleanup?: () => PromiseLike<void>): void;
48
+ lock(): Promise<Disposable>;
33
49
  /**
34
50
  * Activate a task.
35
51
  */
@@ -1 +1 @@
1
- {"version":3,"file":"Mutex.d.ts","sourceRoot":"","sources":["../../../src/util/Mutex.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH;;GAEG;AACH,qBAAa,KAAM,YAAW,WAAW,CAAC,OAAO,CAAC;;gBAMlC,KAAK,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC;IAOrD;;;;OAIG;IACH,IAAI,CAAC,QAAQ,GAAG,IAAI,EAAE,QAAQ,GAAG,KAAK,EAClC,WAAW,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,GAAG,IAAI,EAC3E,UAAU,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,GAAG,KAAK,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,GAAG,IAAI,GACxE,WAAW,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAInC;;;;;OAKG;IACH,GAAG,CAAC,IAAI,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,WAAW,CAAC,OAAO,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,IAAI;IAuBlF;;OAEG;IACH,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC;IAYvE;;OAEG;IACH,SAAS,CAAC,OAAO,CAAC,EAAE,MAAM,WAAW,CAAC,IAAI,CAAC;IAoB3C;;OAEG;cACa,YAAY,CAAC,IAAI,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,WAAW,CAAC,OAAO,CAAC,CAAC;CAMzF"}
1
+ {"version":3,"file":"Mutex.d.ts","sourceRoot":"","sources":["../../../src/util/Mutex.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAM9C,qBAAa,gBAAiB,SAAQ,WAAW;;CAIhD;AAED;;GAEG;AACH,qBAAa,KAAM,YAAW,WAAW,CAAC,OAAO,CAAC;;gBAKlC,KAAK,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC;IAOrD;;OAEG;IACG,KAAK;IAKX;;;;OAIG;IACH,IAAI,CAAC,QAAQ,GAAG,IAAI,EAAE,QAAQ,GAAG,KAAK,EAClC,WAAW,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,GAAG,IAAI,EAC3E,UAAU,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,GAAG,KAAK,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,GAAG,IAAI,GACxE,WAAW,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAInC;;;;;OAKG;IACH,GAAG,CAAC,IAAI,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,WAAW,CAAC,OAAO,CAAC,CAAC;IAc7D;;OAEG;IACH,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAgBlD;;;;;;;;;;OAUG;IACG,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC;IAgBjC;;OAEG;cACa,YAAY,CAAC,IAAI,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,WAAW,CAAC,OAAO,CAAC,CAAC;CAMzF"}
@@ -18,9 +18,11 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
  var Mutex_exports = {};
20
20
  __export(Mutex_exports, {
21
- Mutex: () => Mutex
21
+ Mutex: () => Mutex,
22
+ MutexClosedError: () => MutexClosedError
22
23
  });
23
24
  module.exports = __toCommonJS(Mutex_exports);
25
+ var import_MatterError = require("#MatterError.js");
24
26
  var import_Logger = require("../log/Logger.js");
25
27
  var import_Error = require("./Error.js");
26
28
  /**
@@ -29,10 +31,14 @@ var import_Error = require("./Error.js");
29
31
  * SPDX-License-Identifier: Apache-2.0
30
32
  */
31
33
  const logger = import_Logger.Logger.get("Mutex");
34
+ class MutexClosedError extends import_MatterError.MatterError {
35
+ constructor() {
36
+ super("Cannot schedule task because mutex is closed");
37
+ }
38
+ }
32
39
  class Mutex {
33
40
  #owner;
34
- #cancel;
35
- #canceled = false;
41
+ #closed = false;
36
42
  #promise;
37
43
  constructor(owner, initial) {
38
44
  this.#owner = owner;
@@ -40,6 +46,13 @@ class Mutex {
40
46
  this.run(() => initial);
41
47
  }
42
48
  }
49
+ /**
50
+ * Prevent new tasks and wait for remaining tasks to complete.
51
+ */
52
+ async close() {
53
+ this.#closed = true;
54
+ await this.#promise;
55
+ }
43
56
  /**
44
57
  * As a PromiseLike, you can await the Mutex. This promise resolves when current activity completes but the mutex
45
58
  * may engage in another activity immediately thereafter. So the mutex is not guaranteed to be available after an
@@ -54,30 +67,25 @@ class Mutex {
54
67
  * If {@link task} is a function it runs when current activity completes. If it is a promise then the mutex will
55
68
  * not clear until {@link task} resolves.
56
69
  */
57
- run(task, cancel) {
58
- if (this.#canceled) {
59
- cancel?.();
60
- return;
70
+ run(task) {
71
+ if (this.#closed) {
72
+ throw new MutexClosedError();
61
73
  }
62
74
  if (!this.#promise) {
63
75
  this.#promise = this.initiateTask(task);
64
76
  } else {
65
77
  this.#promise = this.#promise.then(() => {
66
- if (this.#canceled) {
67
- cancel?.();
68
- return;
69
- }
70
- this.#cancel = cancel;
71
- return this.initiateTask(task).finally(() => {
72
- this.#cancel = void 0;
73
- });
78
+ return this.initiateTask(task);
74
79
  });
75
80
  }
76
81
  }
77
82
  /**
78
83
  * Enqueue work with an awaitable result.
79
84
  */
80
- produce(task, cancel) {
85
+ produce(task) {
86
+ if (this.#closed) {
87
+ throw new MutexClosedError();
88
+ }
81
89
  return new Promise((resolve, reject) => {
82
90
  this.run(async () => {
83
91
  try {
@@ -85,27 +93,33 @@ class Mutex {
85
93
  } catch (e) {
86
94
  reject((0, import_Error.asError)(e));
87
95
  }
88
- }, cancel);
96
+ });
89
97
  });
90
98
  }
91
99
  /**
92
- * Cancel remaining work and perform one last task with the Mutex held.
100
+ * Acquire the lock.
101
+ *
102
+ * This offers more natural mutex handling via a disposable. The returned object must be disposed to unlock the
103
+ * mutex.
104
+ *
105
+ * Note that acquiring the lock is async but releasing is not, so you must use `using _lock = await mutex.lock()`
106
+ * rather than `await using _lock = mutex.lock()`.
107
+ *
108
+ * TODO - add abort support
93
109
  */
94
- terminate(cleanup) {
95
- if (this.#canceled) {
96
- return;
97
- }
98
- this.#canceled = true;
99
- if (this.#cancel) {
100
- this.#cancel();
101
- }
102
- if (cleanup) {
103
- if (!this.#promise) {
104
- this.#promise = this.initiateTask(cleanup);
105
- } else {
106
- this.#promise = this.#promise.then(() => this.initiateTask(cleanup));
107
- }
110
+ async lock() {
111
+ if (this.#closed) {
112
+ throw new MutexClosedError();
108
113
  }
114
+ return new Promise((lockObtained) => {
115
+ this.run(async () => {
116
+ await new Promise((lockReleased) => {
117
+ lockObtained({
118
+ [Symbol.dispose]: lockReleased
119
+ });
120
+ });
121
+ });
122
+ });
109
123
  }
110
124
  /**
111
125
  * Activate a task.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/util/Mutex.ts"],
4
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAMA,oBAAuB;AACvB,mBAAwB;AAPxB;AAAA;AAAA;AAAA;AAAA;AASA,MAAM,SAAS,qBAAO,IAAI,OAAO;AAK1B,MAAM,MAAsC;AAAA,EAC/C;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EAEA,YAAY,OAAW,SAAgC;AACnD,SAAK,SAAS;AACd,QAAI,SAAS;AACT,WAAK,IAAI,MAAM,OAAO;AAAA,IAC1B;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,KACI,aACA,YACgC;AAChC,YAAQ,KAAK,YAAY,QAAQ,QAAQ,GAAG,KAAK,aAAa,UAAU;AAAA,EAC5E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,IAAI,MAA2D,QAAqB;AAChF,QAAI,KAAK,WAAW;AAChB,eAAS;AACT;AAAA,IACJ;AAEA,QAAI,CAAC,KAAK,UAAU;AAChB,WAAK,WAAW,KAAK,aAAa,IAAI;AAAA,IAC1C,OAAO;AACH,WAAK,WAAW,KAAK,SAAS,KAAK,MAAM;AACrC,YAAI,KAAK,WAAW;AAChB,mBAAS;AACT;AAAA,QACJ;AAEA,aAAK,UAAU;AACf,eAAO,KAAK,aAAa,IAAI,EAAE,QAAQ,MAAM;AACzC,eAAK,UAAU;AAAA,QACnB,CAAC;AAAA,MACL,CAAC;AAAA,IACL;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKA,QAAW,MAA4B,QAAiC;AACpE,WAAO,IAAI,QAAW,CAAC,SAAS,WAAW;AACvC,WAAK,IAAI,YAAY;AACjB,YAAI;AACA,kBAAQ,MAAM,KAAK,CAAC;AAAA,QACxB,SAAS,GAAG;AACR,qBAAO,sBAAQ,CAAC,CAAC;AAAA,QACrB;AAAA,MACJ,GAAG,MAAM;AAAA,IACb,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA,EAKA,UAAU,SAAmC;AACzC,QAAI,KAAK,WAAW;AAChB;AAAA,IACJ;AAEA,SAAK,YAAY;AAEjB,QAAI,KAAK,SAAS;AACd,WAAK,QAAQ;AAAA,IACjB;AAEA,QAAI,SAAS;AACT,UAAI,CAAC,KAAK,UAAU;AAChB,aAAK,WAAW,KAAK,aAAa,OAAO;AAAA,MAC7C,OAAO;AACH,aAAK,WAAW,KAAK,SAAS,KAAK,MAAM,KAAK,aAAa,OAAO,CAAC;AAAA,MACvE;AAAA,IACJ;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKA,MAAgB,aAAa,MAA2D;AACpF,QAAI,OAAO,SAAS,YAAY;AAC5B,aAAO,KAAK;AAAA,IAChB;AACA,WAAO,QAAQ,QAAQ,IAAI,EAAE,MAAM,WAAS,OAAO,MAAM,sBAAsB,KAAK,MAAM,YAAY,KAAK,CAAC;AAAA,EAChH;AACJ;",
4
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAMA,yBAA4B;AAC5B,oBAAuB;AACvB,mBAAwB;AARxB;AAAA;AAAA;AAAA;AAAA;AAUA,MAAM,SAAS,qBAAO,IAAI,OAAO;AAE1B,MAAM,yBAAyB,+BAAY;AAAA,EAC9C,cAAc;AACV,UAAM,8CAA8C;AAAA,EACxD;AACJ;AAKO,MAAM,MAAsC;AAAA,EAC/C;AAAA,EACA,UAAU;AAAA,EACV;AAAA,EAEA,YAAY,OAAW,SAAgC;AACnD,SAAK,SAAS;AACd,QAAI,SAAS;AACT,WAAK,IAAI,MAAM,OAAO;AAAA,IAC1B;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAQ;AACV,SAAK,UAAU;AACf,UAAM,KAAK;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,KACI,aACA,YACgC;AAChC,YAAQ,KAAK,YAAY,QAAQ,QAAQ,GAAG,KAAK,aAAa,UAAU;AAAA,EAC5E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,IAAI,MAA2D;AAC3D,QAAI,KAAK,SAAS;AACd,YAAM,IAAI,iBAAiB;AAAA,IAC/B;AAEA,QAAI,CAAC,KAAK,UAAU;AAChB,WAAK,WAAW,KAAK,aAAa,IAAI;AAAA,IAC1C,OAAO;AACH,WAAK,WAAW,KAAK,SAAS,KAAK,MAAM;AACrC,eAAO,KAAK,aAAa,IAAI;AAAA,MACjC,CAAC;AAAA,IACL;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKA,QAAW,MAAwC;AAC/C,QAAI,KAAK,SAAS;AACd,YAAM,IAAI,iBAAiB;AAAA,IAC/B;AAEA,WAAO,IAAI,QAAW,CAAC,SAAS,WAAW;AACvC,WAAK,IAAI,YAAY;AACjB,YAAI;AACA,kBAAQ,MAAM,KAAK,CAAC;AAAA,QACxB,SAAS,GAAG;AACR,qBAAO,sBAAQ,CAAC,CAAC;AAAA,QACrB;AAAA,MACJ,CAAC;AAAA,IACL,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,OAA4B;AAC9B,QAAI,KAAK,SAAS;AACd,YAAM,IAAI,iBAAiB;AAAA,IAC/B;AAEA,WAAO,IAAI,QAAQ,kBAAgB;AAC/B,WAAK,IAAI,YAAY;AACjB,cAAM,IAAI,QAAc,kBAAgB;AACpC,uBAAa;AAAA,YACT,CAAC,OAAO,OAAO,GAAG;AAAA,UACtB,CAAC;AAAA,QACL,CAAC;AAAA,MACL,CAAC;AAAA,IACL,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA,EAKA,MAAgB,aAAa,MAA2D;AACpF,QAAI,OAAO,SAAS,YAAY;AAC5B,aAAO,KAAK;AAAA,IAChB;AACA,WAAO,QAAQ,QAAQ,IAAI,EAAE,MAAM,WAAS,OAAO,MAAM,sBAAsB,KAAK,MAAM,YAAY,KAAK,CAAC;AAAA,EAChH;AACJ;",
5
5
  "names": []
6
6
  }
@@ -3,12 +3,20 @@
3
3
  * Copyright 2022-2025 Matter.js Authors
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
+ import { MatterError } from "#MatterError.js";
7
+ export declare class MutexClosedError extends MatterError {
8
+ constructor();
9
+ }
6
10
  /**
7
11
  * A mutex is a task queue where at most one task is active at a time.
8
12
  */
9
13
  export declare class Mutex implements PromiseLike<unknown> {
10
14
  #private;
11
15
  constructor(owner: {}, initial?: PromiseLike<unknown>);
16
+ /**
17
+ * Prevent new tasks and wait for remaining tasks to complete.
18
+ */
19
+ close(): Promise<void>;
12
20
  /**
13
21
  * As a PromiseLike, you can await the Mutex. This promise resolves when current activity completes but the mutex
14
22
  * may engage in another activity immediately thereafter. So the mutex is not guaranteed to be available after an
@@ -21,15 +29,23 @@ export declare class Mutex implements PromiseLike<unknown> {
21
29
  * If {@link task} is a function it runs when current activity completes. If it is a promise then the mutex will
22
30
  * not clear until {@link task} resolves.
23
31
  */
24
- run(task: PromiseLike<unknown> | (() => PromiseLike<unknown>), cancel?: () => void): void;
32
+ run(task: PromiseLike<unknown> | (() => PromiseLike<unknown>)): void;
25
33
  /**
26
34
  * Enqueue work with an awaitable result.
27
35
  */
28
- produce<T>(task: () => PromiseLike<T>, cancel?: () => void): Promise<T>;
36
+ produce<T>(task: () => PromiseLike<T>): Promise<T>;
29
37
  /**
30
- * Cancel remaining work and perform one last task with the Mutex held.
38
+ * Acquire the lock.
39
+ *
40
+ * This offers more natural mutex handling via a disposable. The returned object must be disposed to unlock the
41
+ * mutex.
42
+ *
43
+ * Note that acquiring the lock is async but releasing is not, so you must use `using _lock = await mutex.lock()`
44
+ * rather than `await using _lock = mutex.lock()`.
45
+ *
46
+ * TODO - add abort support
31
47
  */
32
- terminate(cleanup?: () => PromiseLike<void>): void;
48
+ lock(): Promise<Disposable>;
33
49
  /**
34
50
  * Activate a task.
35
51
  */
@@ -1 +1 @@
1
- {"version":3,"file":"Mutex.d.ts","sourceRoot":"","sources":["../../../src/util/Mutex.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH;;GAEG;AACH,qBAAa,KAAM,YAAW,WAAW,CAAC,OAAO,CAAC;;gBAMlC,KAAK,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC;IAOrD;;;;OAIG;IACH,IAAI,CAAC,QAAQ,GAAG,IAAI,EAAE,QAAQ,GAAG,KAAK,EAClC,WAAW,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,GAAG,IAAI,EAC3E,UAAU,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,GAAG,KAAK,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,GAAG,IAAI,GACxE,WAAW,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAInC;;;;;OAKG;IACH,GAAG,CAAC,IAAI,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,WAAW,CAAC,OAAO,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,IAAI;IAuBlF;;OAEG;IACH,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC;IAYvE;;OAEG;IACH,SAAS,CAAC,OAAO,CAAC,EAAE,MAAM,WAAW,CAAC,IAAI,CAAC;IAoB3C;;OAEG;cACa,YAAY,CAAC,IAAI,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,WAAW,CAAC,OAAO,CAAC,CAAC;CAMzF"}
1
+ {"version":3,"file":"Mutex.d.ts","sourceRoot":"","sources":["../../../src/util/Mutex.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAM9C,qBAAa,gBAAiB,SAAQ,WAAW;;CAIhD;AAED;;GAEG;AACH,qBAAa,KAAM,YAAW,WAAW,CAAC,OAAO,CAAC;;gBAKlC,KAAK,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC;IAOrD;;OAEG;IACG,KAAK;IAKX;;;;OAIG;IACH,IAAI,CAAC,QAAQ,GAAG,IAAI,EAAE,QAAQ,GAAG,KAAK,EAClC,WAAW,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,GAAG,IAAI,EAC3E,UAAU,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,GAAG,KAAK,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,GAAG,IAAI,GACxE,WAAW,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAInC;;;;;OAKG;IACH,GAAG,CAAC,IAAI,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,WAAW,CAAC,OAAO,CAAC,CAAC;IAc7D;;OAEG;IACH,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAgBlD;;;;;;;;;;OAUG;IACG,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC;IAgBjC;;OAEG;cACa,YAAY,CAAC,IAAI,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,WAAW,CAAC,OAAO,CAAC,CAAC;CAMzF"}
@@ -3,13 +3,18 @@
3
3
  * Copyright 2022-2025 Matter.js Authors
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
+ import { MatterError } from "#MatterError.js";
6
7
  import { Logger } from "../log/Logger.js";
7
8
  import { asError } from "./Error.js";
8
9
  const logger = Logger.get("Mutex");
10
+ class MutexClosedError extends MatterError {
11
+ constructor() {
12
+ super("Cannot schedule task because mutex is closed");
13
+ }
14
+ }
9
15
  class Mutex {
10
16
  #owner;
11
- #cancel;
12
- #canceled = false;
17
+ #closed = false;
13
18
  #promise;
14
19
  constructor(owner, initial) {
15
20
  this.#owner = owner;
@@ -17,6 +22,13 @@ class Mutex {
17
22
  this.run(() => initial);
18
23
  }
19
24
  }
25
+ /**
26
+ * Prevent new tasks and wait for remaining tasks to complete.
27
+ */
28
+ async close() {
29
+ this.#closed = true;
30
+ await this.#promise;
31
+ }
20
32
  /**
21
33
  * As a PromiseLike, you can await the Mutex. This promise resolves when current activity completes but the mutex
22
34
  * may engage in another activity immediately thereafter. So the mutex is not guaranteed to be available after an
@@ -31,30 +43,25 @@ class Mutex {
31
43
  * If {@link task} is a function it runs when current activity completes. If it is a promise then the mutex will
32
44
  * not clear until {@link task} resolves.
33
45
  */
34
- run(task, cancel) {
35
- if (this.#canceled) {
36
- cancel?.();
37
- return;
46
+ run(task) {
47
+ if (this.#closed) {
48
+ throw new MutexClosedError();
38
49
  }
39
50
  if (!this.#promise) {
40
51
  this.#promise = this.initiateTask(task);
41
52
  } else {
42
53
  this.#promise = this.#promise.then(() => {
43
- if (this.#canceled) {
44
- cancel?.();
45
- return;
46
- }
47
- this.#cancel = cancel;
48
- return this.initiateTask(task).finally(() => {
49
- this.#cancel = void 0;
50
- });
54
+ return this.initiateTask(task);
51
55
  });
52
56
  }
53
57
  }
54
58
  /**
55
59
  * Enqueue work with an awaitable result.
56
60
  */
57
- produce(task, cancel) {
61
+ produce(task) {
62
+ if (this.#closed) {
63
+ throw new MutexClosedError();
64
+ }
58
65
  return new Promise((resolve, reject) => {
59
66
  this.run(async () => {
60
67
  try {
@@ -62,27 +69,33 @@ class Mutex {
62
69
  } catch (e) {
63
70
  reject(asError(e));
64
71
  }
65
- }, cancel);
72
+ });
66
73
  });
67
74
  }
68
75
  /**
69
- * Cancel remaining work and perform one last task with the Mutex held.
76
+ * Acquire the lock.
77
+ *
78
+ * This offers more natural mutex handling via a disposable. The returned object must be disposed to unlock the
79
+ * mutex.
80
+ *
81
+ * Note that acquiring the lock is async but releasing is not, so you must use `using _lock = await mutex.lock()`
82
+ * rather than `await using _lock = mutex.lock()`.
83
+ *
84
+ * TODO - add abort support
70
85
  */
71
- terminate(cleanup) {
72
- if (this.#canceled) {
73
- return;
74
- }
75
- this.#canceled = true;
76
- if (this.#cancel) {
77
- this.#cancel();
78
- }
79
- if (cleanup) {
80
- if (!this.#promise) {
81
- this.#promise = this.initiateTask(cleanup);
82
- } else {
83
- this.#promise = this.#promise.then(() => this.initiateTask(cleanup));
84
- }
86
+ async lock() {
87
+ if (this.#closed) {
88
+ throw new MutexClosedError();
85
89
  }
90
+ return new Promise((lockObtained) => {
91
+ this.run(async () => {
92
+ await new Promise((lockReleased) => {
93
+ lockObtained({
94
+ [Symbol.dispose]: lockReleased
95
+ });
96
+ });
97
+ });
98
+ });
86
99
  }
87
100
  /**
88
101
  * Activate a task.
@@ -95,6 +108,7 @@ class Mutex {
95
108
  }
96
109
  }
97
110
  export {
98
- Mutex
111
+ Mutex,
112
+ MutexClosedError
99
113
  };
100
114
  //# sourceMappingURL=Mutex.js.map
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/util/Mutex.ts"],
4
- "mappings": "AAAA;AAAA;AAAA;AAAA;AAAA;AAMA,SAAS,cAAc;AACvB,SAAS,eAAe;AAExB,MAAM,SAAS,OAAO,IAAI,OAAO;AAK1B,MAAM,MAAsC;AAAA,EAC/C;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EAEA,YAAY,OAAW,SAAgC;AACnD,SAAK,SAAS;AACd,QAAI,SAAS;AACT,WAAK,IAAI,MAAM,OAAO;AAAA,IAC1B;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,KACI,aACA,YACgC;AAChC,YAAQ,KAAK,YAAY,QAAQ,QAAQ,GAAG,KAAK,aAAa,UAAU;AAAA,EAC5E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,IAAI,MAA2D,QAAqB;AAChF,QAAI,KAAK,WAAW;AAChB,eAAS;AACT;AAAA,IACJ;AAEA,QAAI,CAAC,KAAK,UAAU;AAChB,WAAK,WAAW,KAAK,aAAa,IAAI;AAAA,IAC1C,OAAO;AACH,WAAK,WAAW,KAAK,SAAS,KAAK,MAAM;AACrC,YAAI,KAAK,WAAW;AAChB,mBAAS;AACT;AAAA,QACJ;AAEA,aAAK,UAAU;AACf,eAAO,KAAK,aAAa,IAAI,EAAE,QAAQ,MAAM;AACzC,eAAK,UAAU;AAAA,QACnB,CAAC;AAAA,MACL,CAAC;AAAA,IACL;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKA,QAAW,MAA4B,QAAiC;AACpE,WAAO,IAAI,QAAW,CAAC,SAAS,WAAW;AACvC,WAAK,IAAI,YAAY;AACjB,YAAI;AACA,kBAAQ,MAAM,KAAK,CAAC;AAAA,QACxB,SAAS,GAAG;AACR,iBAAO,QAAQ,CAAC,CAAC;AAAA,QACrB;AAAA,MACJ,GAAG,MAAM;AAAA,IACb,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA,EAKA,UAAU,SAAmC;AACzC,QAAI,KAAK,WAAW;AAChB;AAAA,IACJ;AAEA,SAAK,YAAY;AAEjB,QAAI,KAAK,SAAS;AACd,WAAK,QAAQ;AAAA,IACjB;AAEA,QAAI,SAAS;AACT,UAAI,CAAC,KAAK,UAAU;AAChB,aAAK,WAAW,KAAK,aAAa,OAAO;AAAA,MAC7C,OAAO;AACH,aAAK,WAAW,KAAK,SAAS,KAAK,MAAM,KAAK,aAAa,OAAO,CAAC;AAAA,MACvE;AAAA,IACJ;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKA,MAAgB,aAAa,MAA2D;AACpF,QAAI,OAAO,SAAS,YAAY;AAC5B,aAAO,KAAK;AAAA,IAChB;AACA,WAAO,QAAQ,QAAQ,IAAI,EAAE,MAAM,WAAS,OAAO,MAAM,sBAAsB,KAAK,MAAM,YAAY,KAAK,CAAC;AAAA,EAChH;AACJ;",
4
+ "mappings": "AAAA;AAAA;AAAA;AAAA;AAAA;AAMA,SAAS,mBAAmB;AAC5B,SAAS,cAAc;AACvB,SAAS,eAAe;AAExB,MAAM,SAAS,OAAO,IAAI,OAAO;AAE1B,MAAM,yBAAyB,YAAY;AAAA,EAC9C,cAAc;AACV,UAAM,8CAA8C;AAAA,EACxD;AACJ;AAKO,MAAM,MAAsC;AAAA,EAC/C;AAAA,EACA,UAAU;AAAA,EACV;AAAA,EAEA,YAAY,OAAW,SAAgC;AACnD,SAAK,SAAS;AACd,QAAI,SAAS;AACT,WAAK,IAAI,MAAM,OAAO;AAAA,IAC1B;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAQ;AACV,SAAK,UAAU;AACf,UAAM,KAAK;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,KACI,aACA,YACgC;AAChC,YAAQ,KAAK,YAAY,QAAQ,QAAQ,GAAG,KAAK,aAAa,UAAU;AAAA,EAC5E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,IAAI,MAA2D;AAC3D,QAAI,KAAK,SAAS;AACd,YAAM,IAAI,iBAAiB;AAAA,IAC/B;AAEA,QAAI,CAAC,KAAK,UAAU;AAChB,WAAK,WAAW,KAAK,aAAa,IAAI;AAAA,IAC1C,OAAO;AACH,WAAK,WAAW,KAAK,SAAS,KAAK,MAAM;AACrC,eAAO,KAAK,aAAa,IAAI;AAAA,MACjC,CAAC;AAAA,IACL;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKA,QAAW,MAAwC;AAC/C,QAAI,KAAK,SAAS;AACd,YAAM,IAAI,iBAAiB;AAAA,IAC/B;AAEA,WAAO,IAAI,QAAW,CAAC,SAAS,WAAW;AACvC,WAAK,IAAI,YAAY;AACjB,YAAI;AACA,kBAAQ,MAAM,KAAK,CAAC;AAAA,QACxB,SAAS,GAAG;AACR,iBAAO,QAAQ,CAAC,CAAC;AAAA,QACrB;AAAA,MACJ,CAAC;AAAA,IACL,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,OAA4B;AAC9B,QAAI,KAAK,SAAS;AACd,YAAM,IAAI,iBAAiB;AAAA,IAC/B;AAEA,WAAO,IAAI,QAAQ,kBAAgB;AAC/B,WAAK,IAAI,YAAY;AACjB,cAAM,IAAI,QAAc,kBAAgB;AACpC,uBAAa;AAAA,YACT,CAAC,OAAO,OAAO,GAAG;AAAA,UACtB,CAAC;AAAA,QACL,CAAC;AAAA,MACL,CAAC;AAAA,IACL,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA,EAKA,MAAgB,aAAa,MAA2D;AACpF,QAAI,OAAO,SAAS,YAAY;AAC5B,aAAO,KAAK;AAAA,IAChB;AACA,WAAO,QAAQ,QAAQ,IAAI,EAAE,MAAM,WAAS,OAAO,MAAM,sBAAsB,KAAK,MAAM,YAAY,KAAK,CAAC;AAAA,EAChH;AACJ;",
5
5
  "names": []
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@matter/general",
3
- "version": "0.16.0-alpha.0-20251108-514b3f69e",
3
+ "version": "0.16.0-alpha.0-20251111-11cc8c3bd",
4
4
  "description": "Non-Matter support for Matter.js",
5
5
  "keywords": [
6
6
  "iot",
@@ -36,7 +36,7 @@
36
36
  "@noble/curves": "^2.0.1"
37
37
  },
38
38
  "devDependencies": {
39
- "@matter/testing": "0.16.0-alpha.0-20251108-514b3f69e"
39
+ "@matter/testing": "0.16.0-alpha.0-20251111-11cc8c3bd"
40
40
  },
41
41
  "files": [
42
42
  "dist/**/*",
package/src/util/Mutex.ts CHANGED
@@ -4,18 +4,24 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
+ import { MatterError } from "#MatterError.js";
7
8
  import { Logger } from "../log/Logger.js";
8
9
  import { asError } from "./Error.js";
9
10
 
10
11
  const logger = Logger.get("Mutex");
11
12
 
13
+ export class MutexClosedError extends MatterError {
14
+ constructor() {
15
+ super("Cannot schedule task because mutex is closed");
16
+ }
17
+ }
18
+
12
19
  /**
13
20
  * A mutex is a task queue where at most one task is active at a time.
14
21
  */
15
22
  export class Mutex implements PromiseLike<unknown> {
16
23
  #owner: {};
17
- #cancel?: () => void;
18
- #canceled = false;
24
+ #closed = false;
19
25
  #promise?: Promise<unknown>;
20
26
 
21
27
  constructor(owner: {}, initial?: PromiseLike<unknown>) {
@@ -25,6 +31,14 @@ export class Mutex implements PromiseLike<unknown> {
25
31
  }
26
32
  }
27
33
 
34
+ /**
35
+ * Prevent new tasks and wait for remaining tasks to complete.
36
+ */
37
+ async close() {
38
+ this.#closed = true;
39
+ await this.#promise;
40
+ }
41
+
28
42
  /**
29
43
  * As a PromiseLike, you can await the Mutex. This promise resolves when current activity completes but the mutex
30
44
  * may engage in another activity immediately thereafter. So the mutex is not guaranteed to be available after an
@@ -43,25 +57,16 @@ export class Mutex implements PromiseLike<unknown> {
43
57
  * If {@link task} is a function it runs when current activity completes. If it is a promise then the mutex will
44
58
  * not clear until {@link task} resolves.
45
59
  */
46
- run(task: PromiseLike<unknown> | (() => PromiseLike<unknown>), cancel?: () => void) {
47
- if (this.#canceled) {
48
- cancel?.();
49
- return;
60
+ run(task: PromiseLike<unknown> | (() => PromiseLike<unknown>)) {
61
+ if (this.#closed) {
62
+ throw new MutexClosedError();
50
63
  }
51
64
 
52
65
  if (!this.#promise) {
53
66
  this.#promise = this.initiateTask(task);
54
67
  } else {
55
68
  this.#promise = this.#promise.then(() => {
56
- if (this.#canceled) {
57
- cancel?.();
58
- return;
59
- }
60
-
61
- this.#cancel = cancel;
62
- return this.initiateTask(task).finally(() => {
63
- this.#cancel = undefined;
64
- });
69
+ return this.initiateTask(task);
65
70
  });
66
71
  }
67
72
  }
@@ -69,7 +74,11 @@ export class Mutex implements PromiseLike<unknown> {
69
74
  /**
70
75
  * Enqueue work with an awaitable result.
71
76
  */
72
- produce<T>(task: () => PromiseLike<T>, cancel?: () => void): Promise<T> {
77
+ produce<T>(task: () => PromiseLike<T>): Promise<T> {
78
+ if (this.#closed) {
79
+ throw new MutexClosedError();
80
+ }
81
+
73
82
  return new Promise<T>((resolve, reject) => {
74
83
  this.run(async () => {
75
84
  try {
@@ -77,31 +86,35 @@ export class Mutex implements PromiseLike<unknown> {
77
86
  } catch (e) {
78
87
  reject(asError(e));
79
88
  }
80
- }, cancel);
89
+ });
81
90
  });
82
91
  }
83
92
 
84
93
  /**
85
- * Cancel remaining work and perform one last task with the Mutex held.
94
+ * Acquire the lock.
95
+ *
96
+ * This offers more natural mutex handling via a disposable. The returned object must be disposed to unlock the
97
+ * mutex.
98
+ *
99
+ * Note that acquiring the lock is async but releasing is not, so you must use `using _lock = await mutex.lock()`
100
+ * rather than `await using _lock = mutex.lock()`.
101
+ *
102
+ * TODO - add abort support
86
103
  */
87
- terminate(cleanup?: () => PromiseLike<void>) {
88
- if (this.#canceled) {
89
- return;
90
- }
91
-
92
- this.#canceled = true;
93
-
94
- if (this.#cancel) {
95
- this.#cancel();
104
+ async lock(): Promise<Disposable> {
105
+ if (this.#closed) {
106
+ throw new MutexClosedError();
96
107
  }
97
108
 
98
- if (cleanup) {
99
- if (!this.#promise) {
100
- this.#promise = this.initiateTask(cleanup);
101
- } else {
102
- this.#promise = this.#promise.then(() => this.initiateTask(cleanup));
103
- }
104
- }
109
+ return new Promise(lockObtained => {
110
+ this.run(async () => {
111
+ await new Promise<void>(lockReleased => {
112
+ lockObtained({
113
+ [Symbol.dispose]: lockReleased,
114
+ });
115
+ });
116
+ });
117
+ });
105
118
  }
106
119
 
107
120
  /**