kerium 1.4.4 → 1.5.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/dist/locks.d.ts +21 -0
- package/dist/locks.js +71 -0
- package/dist/refs.d.ts +7 -0
- package/dist/refs.js +17 -0
- package/package.json +1 -1
package/dist/locks.d.ts
CHANGED
|
@@ -49,4 +49,25 @@ export declare class RwLockable {
|
|
|
49
49
|
get isLocked(): boolean;
|
|
50
50
|
get lockMode(): LockMode | null;
|
|
51
51
|
}
|
|
52
|
+
/** A synchronization primitive that allows up to `max` concurrent holders. */
|
|
53
|
+
export declare class Semaphore {
|
|
54
|
+
#private;
|
|
55
|
+
readonly max: number;
|
|
56
|
+
constructor(max: number);
|
|
57
|
+
/** Number of slots currently held */
|
|
58
|
+
get active(): number;
|
|
59
|
+
/** Number of acquisitions waiting for a slot */
|
|
60
|
+
get waiting(): number;
|
|
61
|
+
/** Whether a slot can be acquired synchronously. This value may be invalid after an `await` */
|
|
62
|
+
get isAvailable(): boolean;
|
|
63
|
+
/** Acquire a slot, waiting for one to be freed if the semaphore is full */
|
|
64
|
+
get(): Promise<LockRelease>;
|
|
65
|
+
/**
|
|
66
|
+
* Acquire a slot synchronously. Does not support waiting for a slot to be freed.
|
|
67
|
+
* @throws EAGAIN if all slots are currently held
|
|
68
|
+
*/
|
|
69
|
+
getSync(): LockRelease;
|
|
70
|
+
/** Wait until every currently held slot has been released */
|
|
71
|
+
drain(): Promise<void>;
|
|
72
|
+
}
|
|
52
73
|
export {};
|
package/dist/locks.js
CHANGED
|
@@ -156,3 +156,74 @@ let RwLockable = (() => {
|
|
|
156
156
|
};
|
|
157
157
|
})();
|
|
158
158
|
export { RwLockable };
|
|
159
|
+
/** A synchronization primitive that allows up to `max` concurrent holders. */
|
|
160
|
+
export class Semaphore {
|
|
161
|
+
max;
|
|
162
|
+
/** Release promises for every currently held slot */
|
|
163
|
+
#active = new Set();
|
|
164
|
+
/** Callbacks that hand a freed slot to the next waiter, in order */
|
|
165
|
+
#pending = [];
|
|
166
|
+
constructor(max) {
|
|
167
|
+
this.max = max;
|
|
168
|
+
if (!Number.isInteger(max) || max < 1)
|
|
169
|
+
throw withErrno('EINVAL', 'Semaphore max must be a positive integer');
|
|
170
|
+
}
|
|
171
|
+
/** Number of slots currently held */
|
|
172
|
+
get active() {
|
|
173
|
+
return this.#active.size;
|
|
174
|
+
}
|
|
175
|
+
/** Number of acquisitions waiting for a slot */
|
|
176
|
+
get waiting() {
|
|
177
|
+
return this.#pending.length;
|
|
178
|
+
}
|
|
179
|
+
/** Whether a slot can be acquired synchronously. This value may be invalid after an `await` */
|
|
180
|
+
get isAvailable() {
|
|
181
|
+
return this.#active.size < this.max;
|
|
182
|
+
}
|
|
183
|
+
/** Actually take a slot. The caller must have already added `held` to `#active` */
|
|
184
|
+
#take(held, resolve) {
|
|
185
|
+
let released = false;
|
|
186
|
+
const release = () => {
|
|
187
|
+
if (released)
|
|
188
|
+
return;
|
|
189
|
+
released = true;
|
|
190
|
+
this.#active.delete(held);
|
|
191
|
+
resolve();
|
|
192
|
+
this.#pending.shift()?.();
|
|
193
|
+
};
|
|
194
|
+
release[Symbol.dispose] = release;
|
|
195
|
+
return release;
|
|
196
|
+
}
|
|
197
|
+
/** Acquire a slot, waiting for one to be freed if the semaphore is full */
|
|
198
|
+
async get() {
|
|
199
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
200
|
+
if (this.#active.size >= this.max) {
|
|
201
|
+
const grant = Promise.withResolvers();
|
|
202
|
+
this.#pending.push(() => {
|
|
203
|
+
this.#active.add(promise);
|
|
204
|
+
grant.resolve();
|
|
205
|
+
});
|
|
206
|
+
await grant.promise;
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
this.#active.add(promise);
|
|
210
|
+
}
|
|
211
|
+
return this.#take(promise, resolve);
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Acquire a slot synchronously. Does not support waiting for a slot to be freed.
|
|
215
|
+
* @throws EAGAIN if all slots are currently held
|
|
216
|
+
*/
|
|
217
|
+
getSync() {
|
|
218
|
+
if (this.#active.size >= this.max)
|
|
219
|
+
throw withErrno('EAGAIN');
|
|
220
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
221
|
+
this.#active.add(promise);
|
|
222
|
+
return this.#take(promise, resolve);
|
|
223
|
+
}
|
|
224
|
+
/** Wait until every currently held slot has been released */
|
|
225
|
+
async drain() {
|
|
226
|
+
while (this.#active.size)
|
|
227
|
+
await Promise.all(this.#active);
|
|
228
|
+
}
|
|
229
|
+
}
|
package/dist/refs.d.ts
ADDED
package/dist/refs.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export class RefCounted {
|
|
2
|
+
#refCount = 0;
|
|
3
|
+
get refCount() {
|
|
4
|
+
return this.#refCount;
|
|
5
|
+
}
|
|
6
|
+
ref() {
|
|
7
|
+
this.#refCount++;
|
|
8
|
+
return this;
|
|
9
|
+
}
|
|
10
|
+
unref() {
|
|
11
|
+
if (this.#refCount > 0)
|
|
12
|
+
this.#refCount--;
|
|
13
|
+
}
|
|
14
|
+
[Symbol.dispose]() {
|
|
15
|
+
this.unref();
|
|
16
|
+
}
|
|
17
|
+
}
|