mini-semaphore 1.3.4 → 1.3.10
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/README.md +11 -1
- package/cjs/class.js +55 -1
- package/cjs/core.js +33 -2
- package/cjs/deque.js +40 -2
- package/cjs/extras.js +1 -1
- package/cjs/flow-restrictor.js +66 -2
- package/cjs/index.d.ts +0 -2
- package/cjs/index.js +9 -1
- package/cjs/object.js +36 -1
- package/esm/class.mjs +85 -0
- package/esm/core.mjs +60 -0
- package/esm/{deque.js → deque.mjs} +40 -2
- package/esm/{extras.js → extras.mjs} +2 -2
- package/esm/{flow-restrictor.js → flow-restrictor.mjs} +67 -3
- package/esm/index.d.ts +0 -2
- package/esm/index.mjs +12 -0
- package/esm/object.mjs +67 -0
- package/index.d.ts +0 -2
- package/package.json +5 -4
- package/umd/index.d.ts +0 -2
- package/umd/index.js +7 -158
- package/umd/index.js.LICENSE.txt +7 -0
- package/webpack/index.d.ts +0 -2
- package/webpack/index.js +6 -154
- package/webpack/index.js.LICENSE.txt +7 -0
- package/webpack-esm/index.d.ts +210 -0
- package/webpack-esm/index.mjs +6 -0
- package/webpack-esm/index.mjs.LICENSE.txt +7 -0
- package/esm/class.js +0 -31
- package/esm/core.js +0 -29
- package/esm/index.js +0 -4
- package/esm/object.js +0 -32
- package/umd/index.js.map +0 -1
- package/webpack/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
|
-
[](https://circleci.com/gh/jeffy-g/mini-semaphore/tree/master)
|
|
2
|
+

|
|
3
|
+
[](https://badge.fury.io/js/mini-semaphore)
|
|
4
|
+

|
|
5
|
+
[](https://lgtm.com/projects/g/jeffy-g/mini-semaphore/alerts/)
|
|
6
|
+
[](https://lgtm.com/projects/g/jeffy-g/mini-semaphore/context:javascript)
|
|
7
|
+
|
|
8
|
+

|
|
9
|
+

|
|
10
|
+

|
|
11
|
+

|
|
2
12
|
|
|
3
13
|
# Mini Semaphore (mini-semaphore
|
|
4
14
|
|
package/cjs/class.js
CHANGED
|
@@ -1,15 +1,65 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MiniSemaphore = void 0;
|
|
4
|
+
/*!
|
|
5
|
+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
6
|
+
Copyright (C) 2020 jeffy-g <hirotom1107@gmail.com>
|
|
7
|
+
Released under the MIT license
|
|
8
|
+
https://opensource.org/licenses/mit-license.php
|
|
9
|
+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* @file minimal implementation of semaphore (class implementation
|
|
13
|
+
* @author jeffy-g <hirotom1107@gmail.com>
|
|
14
|
+
* @version 1.0
|
|
15
|
+
*/
|
|
4
16
|
const core = require("./core");
|
|
5
17
|
const deque_1 = require("./deque");
|
|
6
18
|
const a = core.acquire;
|
|
7
19
|
const r = core.release;
|
|
20
|
+
/**
|
|
21
|
+
* #### Mini Semaphore
|
|
22
|
+
*
|
|
23
|
+
* + minimal implementation of semaphore
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* import { MiniSemaphore } from "mini-semaphore";
|
|
27
|
+
*
|
|
28
|
+
* const s = new MiniSemaphore(10);
|
|
29
|
+
* async function fetchTypeData(type_id) {
|
|
30
|
+
* await s.acquire();
|
|
31
|
+
* try {
|
|
32
|
+
* return fetch(`https://esi.evetech.net/latest/universe/types/${type_id}/`);
|
|
33
|
+
* } finally {
|
|
34
|
+
* s.release();
|
|
35
|
+
* }
|
|
36
|
+
* }
|
|
37
|
+
*
|
|
38
|
+
* //
|
|
39
|
+
* // or automatic acquire/release
|
|
40
|
+
* //
|
|
41
|
+
* async function fetchTypeData(type_id) {
|
|
42
|
+
* return s.flow(async () => fetch(`https://esi.evetech.net/latest/universe/types/${type_id}/`));
|
|
43
|
+
* }
|
|
44
|
+
*
|
|
45
|
+
* @date 2020/2/7
|
|
46
|
+
* @version 1.0
|
|
47
|
+
*/
|
|
8
48
|
class MiniSemaphore {
|
|
49
|
+
/**
|
|
50
|
+
* constructs a semaphore instance limited at `capacity`
|
|
51
|
+
*
|
|
52
|
+
* @param capacity limitation of concurrent async by `capacity`
|
|
53
|
+
*/
|
|
9
54
|
constructor(capacity) {
|
|
10
55
|
this.limit = this.capacity = capacity;
|
|
11
56
|
this.q = new deque_1.Deque(capacity);
|
|
12
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* If there is enough capacity, execute the `resolve` immediately
|
|
60
|
+
*
|
|
61
|
+
* If not, put it in a queue and wait for the currently pending process to execute `release`
|
|
62
|
+
*/
|
|
13
63
|
acquire(lazy) {
|
|
14
64
|
return a(this, lazy);
|
|
15
65
|
}
|
|
@@ -22,6 +72,10 @@ class MiniSemaphore {
|
|
|
22
72
|
get pending() {
|
|
23
73
|
return this.q.length;
|
|
24
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* automatic acquire/release
|
|
77
|
+
* @param process
|
|
78
|
+
*/
|
|
25
79
|
async flow(process, lazy) {
|
|
26
80
|
await a(this, lazy);
|
|
27
81
|
try {
|
|
@@ -32,4 +86,4 @@ class MiniSemaphore {
|
|
|
32
86
|
}
|
|
33
87
|
}
|
|
34
88
|
}
|
|
35
|
-
exports.MiniSemaphore = MiniSemaphore;
|
|
89
|
+
exports.MiniSemaphore = MiniSemaphore;
|
package/cjs/core.js
CHANGED
|
@@ -2,6 +2,27 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.release = exports.acquire = void 0;
|
|
4
4
|
const extras_1 = require("./extras");
|
|
5
|
+
/**
|
|
6
|
+
* @typedef ISimplifiedLock
|
|
7
|
+
* @prop {(lazy?: boolean) => Promise<void>} acquire acquire the process rights@param lazy Whether the privilege acquisition process is deffer. default `true`
|
|
8
|
+
* @prop {() => void} release release the pending of one
|
|
9
|
+
* @prop {(restriction: number) => void} setRestriction Change sharing restrictions to the value of `restriction`@param {number} restriction
|
|
10
|
+
* @prop {number} pending Get the number of currently pending processes@type {number}
|
|
11
|
+
* @prop {number} limit limitation
|
|
12
|
+
* @prop {number} capacity capacity
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {<T>(f: () => Promise<T>, lazy?: boolean) => Promise<T>} TFlow
|
|
16
|
+
* @typedef {ISimplifiedLock & { flow: TFlow }} IFlowableLock
|
|
17
|
+
* @typedef {() => void} TVoidFunction
|
|
18
|
+
* @typedef {import("./deque").Deque} Deque
|
|
19
|
+
* @typedef {IFlowableLock & { readonly q: Deque }} TFlowableLock
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
*
|
|
23
|
+
* @param {TFlowableLock} z
|
|
24
|
+
* @param {TVoidFunction} r
|
|
25
|
+
*/
|
|
5
26
|
const box = (z, r) => {
|
|
6
27
|
if (z.capacity > 0) {
|
|
7
28
|
z.capacity--, r();
|
|
@@ -10,6 +31,12 @@ const box = (z, r) => {
|
|
|
10
31
|
z.q.push(r);
|
|
11
32
|
}
|
|
12
33
|
};
|
|
34
|
+
/**
|
|
35
|
+
*
|
|
36
|
+
* @param {TFlowableLock} dis
|
|
37
|
+
* @param {boolean} [lazy] default: true
|
|
38
|
+
* @returns {Promise<void>}
|
|
39
|
+
*/
|
|
13
40
|
const acquire = (dis, lazy = true) => {
|
|
14
41
|
return new Promise(r => {
|
|
15
42
|
if (!lazy) {
|
|
@@ -21,14 +48,18 @@ const acquire = (dis, lazy = true) => {
|
|
|
21
48
|
});
|
|
22
49
|
};
|
|
23
50
|
exports.acquire = acquire;
|
|
51
|
+
/**
|
|
52
|
+
* @param {TFlowableLock} dis
|
|
53
|
+
* @returns {void}
|
|
54
|
+
*/
|
|
24
55
|
const release = (dis) => {
|
|
25
56
|
dis.capacity++;
|
|
26
57
|
if (dis.q.length) {
|
|
27
|
-
dis.capacity -= 1, (dis.q.shift() || extras_1.THROW)();
|
|
58
|
+
dis.capacity -= 1, (dis.q.shift() || /* istanbul ignore next */ extras_1.THROW)();
|
|
28
59
|
}
|
|
29
60
|
if (dis.capacity > dis.limit) {
|
|
30
61
|
console.warn("inconsistent release!");
|
|
31
62
|
dis.capacity = dis.limit;
|
|
32
63
|
}
|
|
33
64
|
};
|
|
34
|
-
exports.release = release;
|
|
65
|
+
exports.release = release;
|
package/cjs/deque.js
CHANGED
|
@@ -1,12 +1,25 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Deque = void 0;
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* arrayMove
|
|
6
|
+
*
|
|
7
|
+
* @param src
|
|
8
|
+
* @param si
|
|
9
|
+
* @param dst
|
|
10
|
+
* @param di
|
|
11
|
+
* @param len
|
|
12
|
+
*/
|
|
13
|
+
const am = /* istanbul ignore next */ (src, si, dst, di, len) => {
|
|
5
14
|
for (let j = 0; j < len; ++j) {
|
|
6
15
|
dst[j + di] = src[j + si];
|
|
7
16
|
src[j + si] = void 0;
|
|
8
17
|
}
|
|
9
18
|
};
|
|
19
|
+
/**
|
|
20
|
+
* pow2AtLeast
|
|
21
|
+
* @param n
|
|
22
|
+
*/
|
|
10
23
|
const p2l = (n) => {
|
|
11
24
|
n = n >>> 0;
|
|
12
25
|
n = n - 1;
|
|
@@ -17,16 +30,34 @@ const p2l = (n) => {
|
|
|
17
30
|
n = n | (n >> 16);
|
|
18
31
|
return n + 1;
|
|
19
32
|
};
|
|
33
|
+
/**
|
|
34
|
+
* getCapacity
|
|
35
|
+
* @param n
|
|
36
|
+
*/
|
|
20
37
|
const gc = (n) => {
|
|
38
|
+
// @ts-ignore typescript cannot allow (undefined | 0) expression
|
|
21
39
|
return p2l(Math.min(Math.max(16, n | 0), 1073741824));
|
|
22
40
|
};
|
|
41
|
+
/**
|
|
42
|
+
* ### Implementation restricted to FIFO
|
|
43
|
+
*
|
|
44
|
+
* this class is based on https://github.com/petkaantonov/deque/blob/master/js/deque.js
|
|
45
|
+
* Released under the MIT License: https://github.com/petkaantonov/deque/blob/master/LICENSE
|
|
46
|
+
*/
|
|
23
47
|
class Deque {
|
|
48
|
+
/**
|
|
49
|
+
* default capacity `16`
|
|
50
|
+
* @param ic initial capacity
|
|
51
|
+
*/
|
|
24
52
|
constructor(ic) {
|
|
25
53
|
this._c = gc(ic);
|
|
26
54
|
this._l = 0;
|
|
27
55
|
this._f = 0;
|
|
28
56
|
this._a = [];
|
|
29
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* @param s subject
|
|
60
|
+
*/
|
|
30
61
|
push(s) {
|
|
31
62
|
const l = this._l;
|
|
32
63
|
if (this._c < l + 1) {
|
|
@@ -38,6 +69,7 @@ class Deque {
|
|
|
38
69
|
}
|
|
39
70
|
shift() {
|
|
40
71
|
const l = this._l;
|
|
72
|
+
/* istanbul ignore if */
|
|
41
73
|
if (l === 0) {
|
|
42
74
|
return void 0;
|
|
43
75
|
}
|
|
@@ -53,13 +85,19 @@ class Deque {
|
|
|
53
85
|
}
|
|
54
86
|
}
|
|
55
87
|
exports.Deque = Deque;
|
|
88
|
+
/**
|
|
89
|
+
* resize to
|
|
90
|
+
*
|
|
91
|
+
* @param n expected capacity
|
|
92
|
+
*/
|
|
56
93
|
const rt = (dis, n) => {
|
|
57
94
|
const oc = dis._c;
|
|
58
95
|
dis._c = n;
|
|
59
96
|
const f = dis._f;
|
|
60
97
|
const l = dis._l;
|
|
98
|
+
/* istanbul ignore next */
|
|
61
99
|
if (f + l > oc) {
|
|
62
100
|
const mc = (f + l) & (oc - 1);
|
|
63
101
|
am(dis._a, 0, dis._a, oc, mc);
|
|
64
102
|
}
|
|
65
|
-
};
|
|
103
|
+
};
|
package/cjs/extras.js
CHANGED
package/cjs/flow-restrictor.js
CHANGED
|
@@ -1,12 +1,42 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.restrictor = void 0;
|
|
4
|
+
/*!
|
|
5
|
+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
6
|
+
Copyright (C) 2020 jeffy-g <hirotom1107@gmail.com>
|
|
7
|
+
Released under the MIT license
|
|
8
|
+
https://opensource.org/licenses/mit-license.php
|
|
9
|
+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* @file Utility module using `MiniSemaphore`
|
|
13
|
+
* @author jeffy-g <hirotom1107@gmail.com>
|
|
14
|
+
* @version 1.0
|
|
15
|
+
*/
|
|
4
16
|
const c = require("./class");
|
|
17
|
+
/**
|
|
18
|
+
* @typedef {string | number} TLockRecordKey
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Flow Restriction
|
|
22
|
+
*/
|
|
5
23
|
var restrictor;
|
|
6
24
|
(function (restrictor) {
|
|
7
25
|
const { MiniSemaphore: MS } = c;
|
|
26
|
+
/**
|
|
27
|
+
* @internal
|
|
28
|
+
*/
|
|
8
29
|
const internalLock = new MS(1);
|
|
30
|
+
/**
|
|
31
|
+
*
|
|
32
|
+
*/
|
|
9
33
|
let locks = Object.create(null);
|
|
34
|
+
/**
|
|
35
|
+
*
|
|
36
|
+
* @param {TLockRecordKey} key
|
|
37
|
+
* @param {number} restriction
|
|
38
|
+
* @throws when different restriction
|
|
39
|
+
*/
|
|
10
40
|
const get = async (key, restriction) => {
|
|
11
41
|
await internalLock.acquire(false);
|
|
12
42
|
let lock = locks[key];
|
|
@@ -20,12 +50,28 @@ var restrictor;
|
|
|
20
50
|
internalLock.release();
|
|
21
51
|
return lock;
|
|
22
52
|
};
|
|
53
|
+
/**
|
|
54
|
+
* get the semaphore associated with the value of `key`
|
|
55
|
+
*
|
|
56
|
+
* + ⚠️ The object to be retrieved with `key` must already be created with `multi` ore `one`
|
|
57
|
+
*
|
|
58
|
+
* @param {TLockRecordKey} key
|
|
59
|
+
* @returns `IFlowableLock` instance or `undefined`
|
|
60
|
+
*/
|
|
23
61
|
restrictor.getLockByKey = async (key) => {
|
|
24
62
|
await internalLock.acquire(false);
|
|
25
63
|
const l = locks[key];
|
|
26
64
|
internalLock.release();
|
|
27
65
|
return l;
|
|
28
66
|
};
|
|
67
|
+
/**
|
|
68
|
+
* Eliminate unused instances for the `timeSpan` seconds
|
|
69
|
+
*
|
|
70
|
+
* @param {number} timeSpan specify unit as seconds
|
|
71
|
+
* @param {true} [debug] enable debug
|
|
72
|
+
* @returns {Promise<number>} eliminated count
|
|
73
|
+
* @date 2020/6/19
|
|
74
|
+
*/
|
|
29
75
|
restrictor.cleanup = async (timeSpan, debug) => {
|
|
30
76
|
await internalLock.acquire(false);
|
|
31
77
|
const currentLocks = locks;
|
|
@@ -33,7 +79,7 @@ var restrictor;
|
|
|
33
79
|
const keys = Object.keys(currentLocks);
|
|
34
80
|
let eliminatedCount = 0;
|
|
35
81
|
let eliminatedKeys;
|
|
36
|
-
!timeSpan && (timeSpan = 1);
|
|
82
|
+
!timeSpan && /* istanbul ignore next */ (timeSpan = 1);
|
|
37
83
|
timeSpan *= 1000;
|
|
38
84
|
if (debug) {
|
|
39
85
|
eliminatedKeys = [];
|
|
@@ -59,6 +105,14 @@ var restrictor;
|
|
|
59
105
|
}
|
|
60
106
|
return eliminatedCount;
|
|
61
107
|
};
|
|
108
|
+
/**
|
|
109
|
+
* Allocate a semaphore for each `key`, and limit the number of shares with the value of `restriction`
|
|
110
|
+
*
|
|
111
|
+
* @template {any} T
|
|
112
|
+
* @param {TLockRecordKey} key number or string as tag
|
|
113
|
+
* @param {number} restriction number of process restriction
|
|
114
|
+
* @param {() => Promise<T>} pb the process body
|
|
115
|
+
*/
|
|
62
116
|
async function multi(key, restriction, pb) {
|
|
63
117
|
const s = await get(key, restriction);
|
|
64
118
|
const result = s.flow(pb);
|
|
@@ -66,8 +120,18 @@ var restrictor;
|
|
|
66
120
|
return result;
|
|
67
121
|
}
|
|
68
122
|
restrictor.multi = multi;
|
|
123
|
+
/**
|
|
124
|
+
* synonym of `multi(key, 1, pb)`
|
|
125
|
+
*
|
|
126
|
+
* + use case
|
|
127
|
+
* * Avoid concurrent requests to the same url
|
|
128
|
+
*
|
|
129
|
+
* @template {any} T
|
|
130
|
+
* @param {TLockRecordKey} key number or string as tag
|
|
131
|
+
* @param {() => Promise<T>} pb the process body
|
|
132
|
+
*/
|
|
69
133
|
async function one(key, pb) {
|
|
70
134
|
return multi(key, 1, pb);
|
|
71
135
|
}
|
|
72
136
|
restrictor.one = one;
|
|
73
|
-
})(restrictor = exports.restrictor || (exports.restrictor = {}));
|
|
137
|
+
})(restrictor = exports.restrictor || (exports.restrictor = {}));
|
package/cjs/index.d.ts
CHANGED
|
@@ -94,8 +94,6 @@ export declare type TFlowableLock<T = TVoidFunction> = IFlowableLock & {
|
|
|
94
94
|
readonly q: Deque<T>;
|
|
95
95
|
};
|
|
96
96
|
export declare type TVoidFunction = () => void;
|
|
97
|
-
// export declare const acquire: (dis: TFlowableLock<TVoidFunction>, lazy?: boolean) => Promise<void>;
|
|
98
|
-
// export declare const release: (dis: TFlowableLock<TVoidFunction>) => void;
|
|
99
97
|
|
|
100
98
|
|
|
101
99
|
/**
|
package/cjs/index.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/*!
|
|
3
|
+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
4
|
+
Copyright (C) 2020 jeffy-g <hirotom1107@gmail.com>
|
|
5
|
+
Released under the MIT license
|
|
6
|
+
https://opensource.org/licenses/mit-license.php
|
|
7
|
+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
8
|
+
*/
|
|
2
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.restrictor = exports.Deque = exports.create = exports.MiniSemaphore = void 0;
|
|
10
|
+
exports.version = exports.restrictor = exports.Deque = exports.create = exports.MiniSemaphore = void 0;
|
|
4
11
|
var class_1 = require("./class");
|
|
5
12
|
Object.defineProperty(exports, "MiniSemaphore", { enumerable: true, get: function () { return class_1.MiniSemaphore; } });
|
|
6
13
|
var object_1 = require("./object");
|
|
@@ -9,3 +16,4 @@ var deque_1 = require("./deque");
|
|
|
9
16
|
Object.defineProperty(exports, "Deque", { enumerable: true, get: function () { return deque_1.Deque; } });
|
|
10
17
|
var flow_restrictor_1 = require("./flow-restrictor");
|
|
11
18
|
Object.defineProperty(exports, "restrictor", { enumerable: true, get: function () { return flow_restrictor_1.restrictor; } });
|
|
19
|
+
exports.version = "v1.3.10";
|
package/cjs/object.js
CHANGED
|
@@ -1,27 +1,62 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.create = void 0;
|
|
4
|
+
/*!
|
|
5
|
+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
6
|
+
Copyright (C) 2020 jeffy-g <hirotom1107@gmail.com>
|
|
7
|
+
Released under the MIT license
|
|
8
|
+
https://opensource.org/licenses/mit-license.php
|
|
9
|
+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* @file minimal implementation of semaphore (object implementation
|
|
13
|
+
* @author jeffy-g <hirotom1107@gmail.com>
|
|
14
|
+
* @version 1.0
|
|
15
|
+
*/
|
|
4
16
|
const core = require("./core");
|
|
5
17
|
const deque_1 = require("./deque");
|
|
6
18
|
const a = core.acquire;
|
|
7
19
|
const r = core.release;
|
|
20
|
+
/**
|
|
21
|
+
* object implementation of `IFlowableLock`
|
|
22
|
+
*
|
|
23
|
+
* + constructs a semaphore object limited at `capacity`
|
|
24
|
+
*
|
|
25
|
+
* @param {number} capacity limitation of concurrent async by `capacity`
|
|
26
|
+
* @date 2020/2/7
|
|
27
|
+
* @version 1.0
|
|
28
|
+
*/
|
|
8
29
|
const create = (capacity) => {
|
|
9
30
|
return {
|
|
10
31
|
capacity,
|
|
11
32
|
limit: capacity,
|
|
12
33
|
q: new deque_1.Deque(capacity),
|
|
34
|
+
/**
|
|
35
|
+
*
|
|
36
|
+
* @param {boolean} [lazy]
|
|
37
|
+
* @returns {Promise<void>}
|
|
38
|
+
*/
|
|
13
39
|
acquire(lazy) {
|
|
14
40
|
return a(this, lazy);
|
|
15
41
|
},
|
|
16
42
|
release() {
|
|
17
43
|
r(this);
|
|
18
44
|
},
|
|
45
|
+
/**
|
|
46
|
+
* @param {number} restriction
|
|
47
|
+
*/
|
|
19
48
|
setRestriction(restriction) {
|
|
20
49
|
this.limit = this.capacity = restriction;
|
|
21
50
|
},
|
|
22
51
|
get pending() {
|
|
23
52
|
return this.q.length;
|
|
24
53
|
},
|
|
54
|
+
/**
|
|
55
|
+
* @template {any} T
|
|
56
|
+
* @param {() => Promise<T>} process
|
|
57
|
+
* @param {boolean} [lazy]
|
|
58
|
+
* @returns {Promise<T>}
|
|
59
|
+
*/
|
|
25
60
|
async flow(process, lazy) {
|
|
26
61
|
await a(this, lazy);
|
|
27
62
|
try {
|
|
@@ -33,4 +68,4 @@ const create = (capacity) => {
|
|
|
33
68
|
}
|
|
34
69
|
};
|
|
35
70
|
};
|
|
36
|
-
exports.create = create;
|
|
71
|
+
exports.create = create;
|
package/esm/class.mjs
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
3
|
+
Copyright (C) 2020 jeffy-g <hirotom1107@gmail.com>
|
|
4
|
+
Released under the MIT license
|
|
5
|
+
https://opensource.org/licenses/mit-license.php
|
|
6
|
+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* @file minimal implementation of semaphore (class implementation
|
|
10
|
+
* @author jeffy-g <hirotom1107@gmail.com>
|
|
11
|
+
* @version 1.0
|
|
12
|
+
*/
|
|
13
|
+
import * as core from "./core.mjs";
|
|
14
|
+
import { Deque } from "./deque.mjs";
|
|
15
|
+
const a = core.acquire;
|
|
16
|
+
const r = core.release;
|
|
17
|
+
/**
|
|
18
|
+
* #### Mini Semaphore
|
|
19
|
+
*
|
|
20
|
+
* + minimal implementation of semaphore
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* import { MiniSemaphore } from "mini-semaphore";
|
|
24
|
+
*
|
|
25
|
+
* const s = new MiniSemaphore(10);
|
|
26
|
+
* async function fetchTypeData(type_id) {
|
|
27
|
+
* await s.acquire();
|
|
28
|
+
* try {
|
|
29
|
+
* return fetch(`https://esi.evetech.net/latest/universe/types/${type_id}/`);
|
|
30
|
+
* } finally {
|
|
31
|
+
* s.release();
|
|
32
|
+
* }
|
|
33
|
+
* }
|
|
34
|
+
*
|
|
35
|
+
* //
|
|
36
|
+
* // or automatic acquire/release
|
|
37
|
+
* //
|
|
38
|
+
* async function fetchTypeData(type_id) {
|
|
39
|
+
* return s.flow(async () => fetch(`https://esi.evetech.net/latest/universe/types/${type_id}/`));
|
|
40
|
+
* }
|
|
41
|
+
*
|
|
42
|
+
* @date 2020/2/7
|
|
43
|
+
* @version 1.0
|
|
44
|
+
*/
|
|
45
|
+
export class MiniSemaphore {
|
|
46
|
+
/**
|
|
47
|
+
* constructs a semaphore instance limited at `capacity`
|
|
48
|
+
*
|
|
49
|
+
* @param capacity limitation of concurrent async by `capacity`
|
|
50
|
+
*/
|
|
51
|
+
constructor(capacity) {
|
|
52
|
+
this.limit = this.capacity = capacity;
|
|
53
|
+
this.q = new Deque(capacity);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* If there is enough capacity, execute the `resolve` immediately
|
|
57
|
+
*
|
|
58
|
+
* If not, put it in a queue and wait for the currently pending process to execute `release`
|
|
59
|
+
*/
|
|
60
|
+
acquire(lazy) {
|
|
61
|
+
return a(this, lazy);
|
|
62
|
+
}
|
|
63
|
+
release() {
|
|
64
|
+
r(this);
|
|
65
|
+
}
|
|
66
|
+
setRestriction(restriction) {
|
|
67
|
+
this.limit = this.capacity = restriction;
|
|
68
|
+
}
|
|
69
|
+
get pending() {
|
|
70
|
+
return this.q.length;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* automatic acquire/release
|
|
74
|
+
* @param process
|
|
75
|
+
*/
|
|
76
|
+
async flow(process, lazy) {
|
|
77
|
+
await a(this, lazy);
|
|
78
|
+
try {
|
|
79
|
+
return await process();
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
r(this);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
package/esm/core.mjs
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { THROW } from "./extras.mjs";
|
|
2
|
+
/**
|
|
3
|
+
* @typedef ISimplifiedLock
|
|
4
|
+
* @prop {(lazy?: boolean) => Promise<void>} acquire acquire the process rights@param lazy Whether the privilege acquisition process is deffer. default `true`
|
|
5
|
+
* @prop {() => void} release release the pending of one
|
|
6
|
+
* @prop {(restriction: number) => void} setRestriction Change sharing restrictions to the value of `restriction`@param {number} restriction
|
|
7
|
+
* @prop {number} pending Get the number of currently pending processes@type {number}
|
|
8
|
+
* @prop {number} limit limitation
|
|
9
|
+
* @prop {number} capacity capacity
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* @typedef {<T>(f: () => Promise<T>, lazy?: boolean) => Promise<T>} TFlow
|
|
13
|
+
* @typedef {ISimplifiedLock & { flow: TFlow }} IFlowableLock
|
|
14
|
+
* @typedef {() => void} TVoidFunction
|
|
15
|
+
* @typedef {import("./deque").Deque} Deque
|
|
16
|
+
* @typedef {IFlowableLock & { readonly q: Deque }} TFlowableLock
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
*
|
|
20
|
+
* @param {TFlowableLock} z
|
|
21
|
+
* @param {TVoidFunction} r
|
|
22
|
+
*/
|
|
23
|
+
const box = (z, r) => {
|
|
24
|
+
if (z.capacity > 0) {
|
|
25
|
+
z.capacity--, r();
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
z.q.push(r);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
*
|
|
33
|
+
* @param {TFlowableLock} dis
|
|
34
|
+
* @param {boolean} [lazy] default: true
|
|
35
|
+
* @returns {Promise<void>}
|
|
36
|
+
*/
|
|
37
|
+
export const acquire = (dis, lazy = true) => {
|
|
38
|
+
return new Promise(r => {
|
|
39
|
+
if (!lazy) {
|
|
40
|
+
box(dis, r);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
setTimeout(() => box(dis, r), 4);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* @param {TFlowableLock} dis
|
|
49
|
+
* @returns {void}
|
|
50
|
+
*/
|
|
51
|
+
export const release = (dis) => {
|
|
52
|
+
dis.capacity++;
|
|
53
|
+
if (dis.q.length) {
|
|
54
|
+
dis.capacity -= 1, (dis.q.shift() || /* istanbul ignore next */ THROW)();
|
|
55
|
+
}
|
|
56
|
+
if (dis.capacity > dis.limit) {
|
|
57
|
+
console.warn("inconsistent release!");
|
|
58
|
+
dis.capacity = dis.limit;
|
|
59
|
+
}
|
|
60
|
+
};
|