kerium 1.4.3 → 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/error.d.ts +3 -0
- package/dist/error.js +3 -0
- 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/error.d.ts
CHANGED
|
@@ -51,6 +51,8 @@ export declare enum Errno {
|
|
|
51
51
|
ENFILE = 23,
|
|
52
52
|
/** Too many open files */
|
|
53
53
|
EMFILE = 24,
|
|
54
|
+
/** Inappropriate ioctl for device */
|
|
55
|
+
ENOTTY = 25,
|
|
54
56
|
/** Text file busy */
|
|
55
57
|
ETXTBSY = 26,
|
|
56
58
|
/** File is too big */
|
|
@@ -293,6 +295,7 @@ declare const errnoMessages: {
|
|
|
293
295
|
readonly 22: "invalid argument";
|
|
294
296
|
readonly 23: "file table overflow";
|
|
295
297
|
readonly 24: "too many open files";
|
|
298
|
+
readonly 25: "inappropriate ioctl for device";
|
|
296
299
|
readonly 26: "text file is busy";
|
|
297
300
|
readonly 27: "file too large";
|
|
298
301
|
readonly 28: "no space left on device";
|
package/dist/error.js
CHANGED
|
@@ -53,6 +53,8 @@ export var Errno;
|
|
|
53
53
|
Errno[Errno["ENFILE"] = 23] = "ENFILE";
|
|
54
54
|
/** Too many open files */
|
|
55
55
|
Errno[Errno["EMFILE"] = 24] = "EMFILE";
|
|
56
|
+
/** Inappropriate ioctl for device */
|
|
57
|
+
Errno[Errno["ENOTTY"] = 25] = "ENOTTY";
|
|
56
58
|
/** Text file busy */
|
|
57
59
|
Errno[Errno["ETXTBSY"] = 26] = "ETXTBSY";
|
|
58
60
|
/** File is too big */
|
|
@@ -295,6 +297,7 @@ const errnoMessages = {
|
|
|
295
297
|
[Errno.EINVAL]: 'invalid argument',
|
|
296
298
|
[Errno.ENFILE]: 'file table overflow',
|
|
297
299
|
[Errno.EMFILE]: 'too many open files',
|
|
300
|
+
[Errno.ENOTTY]: 'inappropriate ioctl for device',
|
|
298
301
|
[Errno.ETXTBSY]: 'text file is busy',
|
|
299
302
|
[Errno.EFBIG]: 'file too large',
|
|
300
303
|
[Errno.ENOSPC]: 'no space left on device',
|
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
|
+
}
|