kerium 1.3.9 → 1.4.1

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.
@@ -0,0 +1,52 @@
1
+ export interface LockRelease extends Disposable {
2
+ (): void;
3
+ }
4
+ export type LockMode = 'ro' | 'rw';
5
+ declare const enum WriteState {
6
+ Free = 0,
7
+ Async = 1,
8
+ Sync = 2
9
+ }
10
+ /**
11
+ * A synchronization primitive for handling concurrent access to a resource that can have multiple readers or an exclusive writer.
12
+ * Modeled after Linux's `rw_semaphore`
13
+ */
14
+ export declare class RwLock {
15
+ /** Release promises for every currently held or queued acquisition */
16
+ protected pending: Set<Promise<void>>;
17
+ /** This is the *last queued* writer */
18
+ protected lastWriter?: Promise<void>;
19
+ /** The current accepted write state of the lock */
20
+ protected writeState: WriteState;
21
+ /** Number of currently accepted readers */
22
+ protected readers: number;
23
+ protected syncReaders: number;
24
+ /** Actually take the lock */
25
+ protected take(mode: LockMode, sync: boolean, resolve: () => void): LockRelease;
26
+ /** Queue the reader or writer */
27
+ protected queue(mode: LockMode): () => void;
28
+ /**
29
+ * Acquire a lock asynchronously
30
+ */
31
+ acquire(mode: LockMode): Promise<LockRelease>;
32
+ /**
33
+ * Acquire a lock synchronously. Does not support waiting for existing locks.
34
+ * @throws EDEADLK for rw if there is an existing synchronous holder (it is in the current call chain)
35
+ * @throws EAGAIN for rw if there is an existing asynchronous holder
36
+ */
37
+ acquireSync(mode: LockMode): LockRelease;
38
+ /** Whether a synchronous lock can be acquired for the given mode */
39
+ isAvailable(mode: LockMode): boolean;
40
+ /** Whether the lock is currently free. This value may be invalid after an `await` */
41
+ get isFree(): boolean;
42
+ /** How the lock is currently being used. This value may be invalid after an `await` */
43
+ get mode(): LockMode | null;
44
+ }
45
+ export declare class RwLockable {
46
+ protected _rwLock: RwLock;
47
+ get lock(): RwLock['acquire'];
48
+ get lockSync(): RwLock['acquireSync'];
49
+ get isLocked(): boolean;
50
+ get lockMode(): LockMode | null;
51
+ }
52
+ export {};
package/dist/locks.js ADDED
@@ -0,0 +1,158 @@
1
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
2
+ var useValue = arguments.length > 2;
3
+ for (var i = 0; i < initializers.length; i++) {
4
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
5
+ }
6
+ return useValue ? value : void 0;
7
+ };
8
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
9
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
10
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
11
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
12
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
13
+ var _, done = false;
14
+ for (var i = decorators.length - 1; i >= 0; i--) {
15
+ var context = {};
16
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
17
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
18
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
19
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
20
+ if (kind === "accessor") {
21
+ if (result === void 0) continue;
22
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
23
+ if (_ = accept(result.get)) descriptor.get = _;
24
+ if (_ = accept(result.set)) descriptor.set = _;
25
+ if (_ = accept(result.init)) initializers.unshift(_);
26
+ }
27
+ else if (_ = accept(result)) {
28
+ if (kind === "field") initializers.unshift(_);
29
+ else descriptor[key] = _;
30
+ }
31
+ }
32
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
33
+ done = true;
34
+ };
35
+ import { memoize } from 'utilium';
36
+ import { withErrno } from './error.js';
37
+ /**
38
+ * A synchronization primitive for handling concurrent access to a resource that can have multiple readers or an exclusive writer.
39
+ * Modeled after Linux's `rw_semaphore`
40
+ */
41
+ export class RwLock {
42
+ /** Release promises for every currently held or queued acquisition */
43
+ pending = new Set();
44
+ /** This is the *last queued* writer */
45
+ lastWriter;
46
+ /** The current accepted write state of the lock */
47
+ writeState = 0;
48
+ /** Number of currently accepted readers */
49
+ readers = 0;
50
+ syncReaders = 0;
51
+ /** Actually take the lock */
52
+ take(mode, sync, resolve) {
53
+ if (mode == 'rw') {
54
+ this.writeState = sync ? 2 /* WriteState.Sync */ : 1 /* WriteState.Async */;
55
+ }
56
+ else {
57
+ this.readers++;
58
+ if (sync)
59
+ this.syncReaders++;
60
+ }
61
+ let released = false;
62
+ const release = () => {
63
+ if (released)
64
+ return;
65
+ released = true;
66
+ if (mode == 'rw') {
67
+ this.writeState = 0;
68
+ }
69
+ else {
70
+ this.readers--;
71
+ if (sync)
72
+ this.syncReaders--;
73
+ }
74
+ resolve();
75
+ };
76
+ release[Symbol.dispose] = release;
77
+ return release;
78
+ }
79
+ /** Queue the reader or writer */
80
+ queue(mode) {
81
+ const { promise, resolve } = Promise.withResolvers();
82
+ this.pending.add(promise);
83
+ if (mode == 'rw')
84
+ this.lastWriter = promise;
85
+ void promise.then(() => {
86
+ this.pending.delete(promise);
87
+ if (this.lastWriter == promise)
88
+ delete this.lastWriter;
89
+ });
90
+ return resolve;
91
+ }
92
+ /**
93
+ * Acquire a lock asynchronously
94
+ */
95
+ async acquire(mode) {
96
+ const allReleased = mode == 'ro' ? this.lastWriter : Promise.all(this.pending);
97
+ const resolve = this.queue(mode);
98
+ await allReleased;
99
+ return this.take(mode, false, resolve);
100
+ }
101
+ /**
102
+ * Acquire a lock synchronously. Does not support waiting for existing locks.
103
+ * @throws EDEADLK for rw if there is an existing synchronous holder (it is in the current call chain)
104
+ * @throws EAGAIN for rw if there is an existing asynchronous holder
105
+ */
106
+ acquireSync(mode) {
107
+ if (this.writeState == 2 /* WriteState.Sync */ || (mode == 'rw' && this.syncReaders))
108
+ throw withErrno('EDEADLK');
109
+ if (this.writeState || (mode == 'rw' && this.readers))
110
+ throw withErrno('EAGAIN');
111
+ return this.take(mode, true, this.queue(mode));
112
+ }
113
+ /** Whether a synchronous lock can be acquired for the given mode */
114
+ isAvailable(mode) {
115
+ return (mode != 'rw' || !this.readers) && !this.writeState;
116
+ }
117
+ /** Whether the lock is currently free. This value may be invalid after an `await` */
118
+ get isFree() {
119
+ return !this.writeState && !this.readers;
120
+ }
121
+ /** How the lock is currently being used. This value may be invalid after an `await` */
122
+ get mode() {
123
+ if (this.writeState)
124
+ return 'rw';
125
+ if (this.readers)
126
+ return 'ro';
127
+ return null;
128
+ }
129
+ }
130
+ let RwLockable = (() => {
131
+ let _instanceExtraInitializers = [];
132
+ let _get_lock_decorators;
133
+ let _get_lockSync_decorators;
134
+ return class RwLockable {
135
+ static {
136
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
137
+ _get_lock_decorators = [memoize];
138
+ _get_lockSync_decorators = [memoize];
139
+ __esDecorate(this, null, _get_lock_decorators, { kind: "getter", name: "lock", static: false, private: false, access: { has: obj => "lock" in obj, get: obj => obj.lock }, metadata: _metadata }, null, _instanceExtraInitializers);
140
+ __esDecorate(this, null, _get_lockSync_decorators, { kind: "getter", name: "lockSync", static: false, private: false, access: { has: obj => "lockSync" in obj, get: obj => obj.lockSync }, metadata: _metadata }, null, _instanceExtraInitializers);
141
+ if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
142
+ }
143
+ _rwLock = (__runInitializers(this, _instanceExtraInitializers), new RwLock());
144
+ get lock() {
145
+ return this._rwLock.acquire.bind(this._rwLock);
146
+ }
147
+ get lockSync() {
148
+ return this._rwLock.acquireSync.bind(this._rwLock);
149
+ }
150
+ get isLocked() {
151
+ return !this._rwLock.isFree;
152
+ }
153
+ get lockMode() {
154
+ return this._rwLock.mode;
155
+ }
156
+ };
157
+ })();
158
+ export { RwLockable };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kerium",
3
- "version": "1.3.9",
3
+ "version": "1.4.1",
4
4
  "description": "POSIX-style errors, logging, and more",
5
5
  "author": "James Prevett <jp@jamespre.dev> (https://jamespre.dev)",
6
6
  "repository": {
@@ -21,7 +21,7 @@
21
21
  "types": "dist/index.d.ts",
22
22
  "exports": {
23
23
  ".": "./dist/index.js",
24
- "./log": "./dist/log.js"
24
+ "./*": "./dist/*.js"
25
25
  },
26
26
  "files": [
27
27
  "dist"
@@ -46,5 +46,17 @@
46
46
  },
47
47
  "dependencies": {
48
48
  "utilium": "^3.0.0"
49
+ },
50
+ "prettier": {
51
+ "singleQuote": true,
52
+ "useTabs": true,
53
+ "trailingComma": "es5",
54
+ "tabWidth": 4,
55
+ "printWidth": 120,
56
+ "arrowParens": "avoid",
57
+ "experimentalOperatorPosition": "start"
58
+ },
59
+ "allowScripts": {
60
+ "esbuild": true
49
61
  }
50
62
  }