extra-pool 0.1.3 → 0.1.5
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 +2 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -17
- package/lib/index.js.map +1 -1
- package/lib/instance.d.ts +13 -11
- package/lib/instance.js +153 -68
- package/lib/instance.js.map +1 -1
- package/lib/pool.d.ts +11 -11
- package/lib/pool.js +31 -38
- package/lib/pool.js.map +1 -1
- package/package.json +31 -31
- package/src/index.ts +1 -1
- package/src/instance.ts +171 -80
- package/src/pool.ts +35 -30
package/README.md
CHANGED
package/lib/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from './pool';
|
|
1
|
+
export * from './pool.js';
|
package/lib/index.js
CHANGED
|
@@ -1,18 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
-
};
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./pool"), exports);
|
|
1
|
+
export * from './pool.js';
|
|
18
2
|
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA"}
|
package/lib/instance.d.ts
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
import { Awaitable } from '@blackglory/prelude';
|
|
2
|
-
import { Deferred } from 'extra-promise';
|
|
1
|
+
import { Awaitable, NonEmptyArray } from '@blackglory/prelude';
|
|
3
2
|
export declare enum InstanceState {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
Created = 0,
|
|
4
|
+
Initializing = 1,
|
|
5
|
+
Idle = 2,
|
|
6
|
+
Busy = 3,
|
|
7
|
+
Destroying = 4,
|
|
8
|
+
Destroyed = 5
|
|
9
9
|
}
|
|
10
10
|
export declare class Instance<T> {
|
|
11
|
+
#private;
|
|
12
|
+
private createInstance;
|
|
11
13
|
private destroyInstance?;
|
|
12
14
|
private fsm;
|
|
13
|
-
private
|
|
14
|
-
|
|
15
|
+
private deferredCreateInstance;
|
|
16
|
+
private deferredDestroyInstance;
|
|
15
17
|
get users(): number;
|
|
18
|
+
get state(): InstanceState;
|
|
16
19
|
constructor(createInstance: () => Awaitable<T>, destroyInstance?: ((value: T) => Awaitable<void>) | undefined);
|
|
17
|
-
waitForCreated(): Promise<void>;
|
|
18
|
-
getState(): InstanceState;
|
|
19
20
|
use<U>(fn: (instance: T) => Awaitable<U>): Promise<U>;
|
|
20
21
|
destroy(): Promise<void>;
|
|
22
|
+
waitForState<States extends NonEmptyArray<InstanceState>>(...states: States): Promise<States[number]>;
|
|
21
23
|
}
|
package/lib/instance.js
CHANGED
|
@@ -1,28 +1,43 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
2
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
3
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
4
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
|
+
};
|
|
6
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
7
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
9
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
10
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
11
|
+
};
|
|
12
|
+
var _Instance_users;
|
|
13
|
+
import { pass } from '@blackglory/prelude';
|
|
14
|
+
import { ObservableFiniteStateMachine } from 'extra-fsm';
|
|
15
|
+
import { Deferred } from 'extra-promise';
|
|
16
|
+
import { firstValueFrom } from 'rxjs';
|
|
17
|
+
import { filter, map } from 'rxjs/operators';
|
|
18
|
+
export var InstanceState;
|
|
10
19
|
(function (InstanceState) {
|
|
11
|
-
InstanceState["
|
|
12
|
-
InstanceState["
|
|
13
|
-
InstanceState["
|
|
14
|
-
InstanceState["
|
|
15
|
-
InstanceState["
|
|
16
|
-
|
|
20
|
+
InstanceState[InstanceState["Created"] = 0] = "Created";
|
|
21
|
+
InstanceState[InstanceState["Initializing"] = 1] = "Initializing";
|
|
22
|
+
InstanceState[InstanceState["Idle"] = 2] = "Idle";
|
|
23
|
+
InstanceState[InstanceState["Busy"] = 3] = "Busy";
|
|
24
|
+
InstanceState[InstanceState["Destroying"] = 4] = "Destroying";
|
|
25
|
+
InstanceState[InstanceState["Destroyed"] = 5] = "Destroyed";
|
|
26
|
+
})(InstanceState || (InstanceState = {}));
|
|
17
27
|
const instanceSchema = {
|
|
18
|
-
[InstanceState.
|
|
19
|
-
|
|
28
|
+
[InstanceState.Created]: {
|
|
29
|
+
init: InstanceState.Initializing,
|
|
30
|
+
destroy: InstanceState.Destroyed
|
|
31
|
+
},
|
|
32
|
+
[InstanceState.Initializing]: {
|
|
33
|
+
inited: InstanceState.Idle,
|
|
34
|
+
fail: InstanceState.Created
|
|
20
35
|
},
|
|
21
36
|
[InstanceState.Idle]: {
|
|
22
|
-
use: InstanceState.
|
|
37
|
+
use: InstanceState.Busy,
|
|
23
38
|
destroy: InstanceState.Destroying
|
|
24
39
|
},
|
|
25
|
-
[InstanceState.
|
|
40
|
+
[InstanceState.Busy]: {
|
|
26
41
|
idle: InstanceState.Idle
|
|
27
42
|
},
|
|
28
43
|
[InstanceState.Destroying]: {
|
|
@@ -30,69 +45,139 @@ const instanceSchema = {
|
|
|
30
45
|
},
|
|
31
46
|
[InstanceState.Destroyed]: {}
|
|
32
47
|
};
|
|
33
|
-
class Instance {
|
|
48
|
+
export class Instance {
|
|
34
49
|
get users() {
|
|
35
|
-
return this
|
|
50
|
+
return __classPrivateFieldGet(this, _Instance_users, "f");
|
|
51
|
+
}
|
|
52
|
+
get state() {
|
|
53
|
+
return this.fsm.state;
|
|
36
54
|
}
|
|
37
55
|
constructor(createInstance, destroyInstance) {
|
|
56
|
+
this.createInstance = createInstance;
|
|
38
57
|
this.destroyInstance = destroyInstance;
|
|
39
|
-
this.
|
|
40
|
-
this.
|
|
41
|
-
this.
|
|
42
|
-
(
|
|
43
|
-
try {
|
|
44
|
-
const instance = await createInstance();
|
|
45
|
-
this.fsm.send('created');
|
|
46
|
-
this._instance.resolve(instance);
|
|
47
|
-
}
|
|
48
|
-
catch (e) {
|
|
49
|
-
this._instance.reject(e);
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
async waitForCreated() {
|
|
54
|
-
await this._instance;
|
|
55
|
-
}
|
|
56
|
-
getState() {
|
|
57
|
-
return this.fsm.state;
|
|
58
|
+
this.fsm = new ObservableFiniteStateMachine(instanceSchema, InstanceState.Created);
|
|
59
|
+
this.deferredCreateInstance = createDeferred();
|
|
60
|
+
this.deferredDestroyInstance = createDeferred();
|
|
61
|
+
_Instance_users.set(this, 0);
|
|
58
62
|
}
|
|
59
63
|
async use(fn) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
const self = this;
|
|
65
|
+
switch (this.fsm.state) {
|
|
66
|
+
case InstanceState.Created: {
|
|
67
|
+
addUser();
|
|
68
|
+
try {
|
|
69
|
+
this.fsm.send('init');
|
|
70
|
+
let instance;
|
|
71
|
+
try {
|
|
72
|
+
instance = await this.createInstance();
|
|
73
|
+
this.deferredCreateInstance.resolve(instance);
|
|
74
|
+
this.fsm.send('inited');
|
|
75
|
+
}
|
|
76
|
+
catch (e) {
|
|
77
|
+
this.deferredCreateInstance.reject(e);
|
|
78
|
+
this.deferredCreateInstance = createDeferred();
|
|
79
|
+
this.fsm.send('fail');
|
|
80
|
+
throw e;
|
|
81
|
+
}
|
|
82
|
+
this.fsm.send('use');
|
|
83
|
+
return await fn(instance);
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
removeUser();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
case InstanceState.Initializing: {
|
|
90
|
+
addUser();
|
|
91
|
+
try {
|
|
92
|
+
const instance = await this.deferredCreateInstance;
|
|
93
|
+
return await fn(instance);
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
removeUser();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
case InstanceState.Idle: {
|
|
100
|
+
addUser();
|
|
101
|
+
try {
|
|
102
|
+
this.fsm.send('use');
|
|
103
|
+
const instance = await this.deferredCreateInstance;
|
|
104
|
+
return await fn(instance);
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
removeUser();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
case InstanceState.Busy: {
|
|
111
|
+
addUser();
|
|
112
|
+
try {
|
|
113
|
+
const instance = await this.deferredCreateInstance;
|
|
114
|
+
return await fn(instance);
|
|
115
|
+
}
|
|
116
|
+
finally {
|
|
117
|
+
removeUser();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
case InstanceState.Destroying:
|
|
121
|
+
case InstanceState.Destroyed: throw new Error('The instance is not available');
|
|
122
|
+
default: throw new Error(`Unhandled state`);
|
|
68
123
|
}
|
|
69
|
-
(
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const result = await fn(instance);
|
|
73
|
-
return result;
|
|
124
|
+
function addUser() {
|
|
125
|
+
var _a, _b;
|
|
126
|
+
__classPrivateFieldSet(_a = self, _Instance_users, (_b = __classPrivateFieldGet(_a, _Instance_users, "f"), _b++, _b), "f");
|
|
74
127
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
128
|
+
function removeUser() {
|
|
129
|
+
var _a, _b;
|
|
130
|
+
if ((__classPrivateFieldSet(_a = self, _Instance_users, (_b = __classPrivateFieldGet(_a, _Instance_users, "f"), --_b), "f")) === 0) {
|
|
131
|
+
if (self.fsm.can('idle'))
|
|
132
|
+
self.fsm.send('idle');
|
|
78
133
|
}
|
|
79
134
|
}
|
|
80
135
|
}
|
|
81
136
|
async destroy() {
|
|
82
137
|
var _a;
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
138
|
+
switch (this.fsm.state) {
|
|
139
|
+
case InstanceState.Created: {
|
|
140
|
+
this.fsm.send('destroy');
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
case InstanceState.Initializing: {
|
|
144
|
+
await this.waitForState(InstanceState.Idle, InstanceState.Created);
|
|
145
|
+
return await this.destroy();
|
|
146
|
+
}
|
|
147
|
+
case InstanceState.Idle: {
|
|
148
|
+
this.fsm.send('destroy');
|
|
149
|
+
const instance = await this.deferredCreateInstance;
|
|
150
|
+
try {
|
|
151
|
+
await ((_a = this.destroyInstance) === null || _a === void 0 ? void 0 : _a.call(this, instance));
|
|
152
|
+
}
|
|
153
|
+
catch (e) {
|
|
154
|
+
this.deferredDestroyInstance.reject(e);
|
|
155
|
+
throw e;
|
|
156
|
+
}
|
|
157
|
+
this.fsm.send('destroyed');
|
|
158
|
+
this.deferredDestroyInstance.resolve();
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
case InstanceState.Busy: {
|
|
162
|
+
await this.waitForState(InstanceState.Idle);
|
|
163
|
+
return await this.destroy();
|
|
164
|
+
}
|
|
165
|
+
case InstanceState.Destroying: {
|
|
166
|
+
await this.deferredDestroyInstance;
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
case InstanceState.Destroyed: return;
|
|
170
|
+
default: throw new Error(`Unhandled state`);
|
|
94
171
|
}
|
|
95
172
|
}
|
|
173
|
+
async waitForState(...states) {
|
|
174
|
+
return await firstValueFrom(this.fsm.observeStateChanges().pipe(map(change => change.newState), filter(newState => states.includes(newState))));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
_Instance_users = new WeakMap();
|
|
178
|
+
function createDeferred() {
|
|
179
|
+
const deferred = new Deferred();
|
|
180
|
+
Promise.resolve(deferred).catch(pass);
|
|
181
|
+
return deferred;
|
|
96
182
|
}
|
|
97
|
-
exports.Instance = Instance;
|
|
98
183
|
//# sourceMappingURL=instance.js.map
|
package/lib/instance.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instance.js","sourceRoot":"","sources":["../src/instance.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"instance.js","sourceRoot":"","sources":["../src/instance.ts"],"names":[],"mappings":"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"}
|
package/lib/pool.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { CustomError } from '@blackglory/
|
|
2
|
-
import { Awaitable } from '@blackglory/prelude';
|
|
1
|
+
import { Awaitable, CustomError } from '@blackglory/prelude';
|
|
3
2
|
interface IPoolOptions<T> {
|
|
4
3
|
create: () => Awaitable<T>;
|
|
5
4
|
destroy?: (value: T) => Awaitable<void>;
|
|
@@ -9,15 +8,16 @@ interface IPoolOptions<T> {
|
|
|
9
8
|
concurrencyPerInstance?: number;
|
|
10
9
|
}
|
|
11
10
|
export declare class Pool<T> {
|
|
12
|
-
private createInstance;
|
|
13
|
-
private destroyInstance?;
|
|
14
|
-
private fsm;
|
|
15
|
-
private waitingUsers;
|
|
16
|
-
private items;
|
|
17
|
-
private maxInstances;
|
|
18
|
-
private minInstances;
|
|
19
|
-
private idleTimeout;
|
|
20
|
-
private concurrencyPerInstance;
|
|
11
|
+
private readonly createInstance;
|
|
12
|
+
private readonly destroyInstance?;
|
|
13
|
+
private readonly fsm;
|
|
14
|
+
private readonly waitingUsers;
|
|
15
|
+
private readonly items;
|
|
16
|
+
private readonly maxInstances;
|
|
17
|
+
private readonly minInstances;
|
|
18
|
+
private readonly idleTimeout;
|
|
19
|
+
private readonly concurrencyPerInstance;
|
|
20
|
+
get capacity(): number;
|
|
21
21
|
get size(): number;
|
|
22
22
|
constructor(options: IPoolOptions<T>);
|
|
23
23
|
use<U>(fn: (instance: T) => Awaitable<U>): Promise<U>;
|
package/lib/pool.js
CHANGED
|
@@ -1,19 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const extra_promise_1 = require("extra-promise");
|
|
9
|
-
const iterable_operator_1 = require("iterable-operator");
|
|
10
|
-
const extra_timers_1 = require("extra-timers");
|
|
11
|
-
const instance_1 = require("./instance");
|
|
1
|
+
import { go, CustomError, isntEmptyArray, assert } from '@blackglory/prelude';
|
|
2
|
+
import { Queue } from '@blackglory/structures';
|
|
3
|
+
import { FiniteStateMachine } from 'extra-fsm';
|
|
4
|
+
import { Deferred, each } from 'extra-promise';
|
|
5
|
+
import { toArray, filter } from 'iterable-operator';
|
|
6
|
+
import { setTimeout } from 'extra-timers';
|
|
7
|
+
import { Instance } from './instance.js';
|
|
12
8
|
var PoolState;
|
|
13
9
|
(function (PoolState) {
|
|
14
|
-
PoolState["Running"] = "
|
|
15
|
-
PoolState["Destroying"] = "
|
|
16
|
-
PoolState["Destroyed"] = "
|
|
10
|
+
PoolState[PoolState["Running"] = 0] = "Running";
|
|
11
|
+
PoolState[PoolState["Destroying"] = 1] = "Destroying";
|
|
12
|
+
PoolState[PoolState["Destroyed"] = 2] = "Destroyed";
|
|
17
13
|
})(PoolState || (PoolState = {}));
|
|
18
14
|
const poolSchema = {
|
|
19
15
|
[PoolState.Running]: {
|
|
@@ -24,14 +20,17 @@ const poolSchema = {
|
|
|
24
20
|
},
|
|
25
21
|
[PoolState.Destroyed]: {}
|
|
26
22
|
};
|
|
27
|
-
class Pool {
|
|
23
|
+
export class Pool {
|
|
24
|
+
get capacity() {
|
|
25
|
+
return this.maxInstances;
|
|
26
|
+
}
|
|
28
27
|
get size() {
|
|
29
28
|
return this.items.size;
|
|
30
29
|
}
|
|
31
30
|
constructor(options) {
|
|
32
31
|
var _a, _b, _c, _d;
|
|
33
|
-
this.fsm = new
|
|
34
|
-
this.waitingUsers = new
|
|
32
|
+
this.fsm = new FiniteStateMachine(poolSchema, PoolState.Running);
|
|
33
|
+
this.waitingUsers = new Queue();
|
|
35
34
|
this.items = new Set();
|
|
36
35
|
this.createInstance = options.create;
|
|
37
36
|
this.destroyInstance = options.destroy;
|
|
@@ -41,17 +40,15 @@ class Pool {
|
|
|
41
40
|
this.concurrencyPerInstance = (_d = options.concurrencyPerInstance) !== null && _d !== void 0 ? _d : 1;
|
|
42
41
|
}
|
|
43
42
|
async use(fn) {
|
|
43
|
+
assert(this.fsm.matches(PoolState.Running), 'The pool is not available');
|
|
44
44
|
const self = this;
|
|
45
|
-
const item =
|
|
46
|
-
const candidateItems =
|
|
47
|
-
if (candidateItems
|
|
45
|
+
const item = go(() => {
|
|
46
|
+
const candidateItems = toArray(filter(this.items, item => item.instance.users < this.concurrencyPerInstance));
|
|
47
|
+
if (isntEmptyArray(candidateItems)) {
|
|
48
48
|
return candidateItems.reduce((previous, current) => {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
else {
|
|
53
|
-
return previous;
|
|
54
|
-
}
|
|
49
|
+
return current.instance.users < previous.instance.users
|
|
50
|
+
? current
|
|
51
|
+
: previous;
|
|
55
52
|
});
|
|
56
53
|
}
|
|
57
54
|
});
|
|
@@ -60,13 +57,13 @@ class Pool {
|
|
|
60
57
|
}
|
|
61
58
|
else {
|
|
62
59
|
if (this.items.size < this.maxInstances) {
|
|
63
|
-
const instance = new
|
|
60
|
+
const instance = new Instance(this.createInstance, this.destroyInstance);
|
|
64
61
|
const item = { instance };
|
|
65
62
|
this.items.add(item);
|
|
66
63
|
return await useItem(item);
|
|
67
64
|
}
|
|
68
65
|
else {
|
|
69
|
-
const waitingUser = new
|
|
66
|
+
const waitingUser = new Deferred();
|
|
70
67
|
this.waitingUsers.enqueue(waitingUser);
|
|
71
68
|
const item = await waitingUser;
|
|
72
69
|
return await useItem(item);
|
|
@@ -89,7 +86,7 @@ class Pool {
|
|
|
89
86
|
if (item.instance.users === 0 &&
|
|
90
87
|
self.items.size > self.minInstances) {
|
|
91
88
|
if (self.idleTimeout > 0) {
|
|
92
|
-
item.cancelScheduledDeletion =
|
|
89
|
+
item.cancelScheduledDeletion = setTimeout(self.idleTimeout, deleteInstance);
|
|
93
90
|
}
|
|
94
91
|
else {
|
|
95
92
|
await deleteInstance();
|
|
@@ -105,19 +102,15 @@ class Pool {
|
|
|
105
102
|
}
|
|
106
103
|
async destroy() {
|
|
107
104
|
this.fsm.send('destroy');
|
|
108
|
-
|
|
109
|
-
await item.instance.destroy();
|
|
110
|
-
}
|
|
105
|
+
await each(this.items, item => item.instance.destroy());
|
|
111
106
|
this.items.clear();
|
|
112
|
-
let
|
|
113
|
-
while (
|
|
114
|
-
|
|
107
|
+
let waitingUser;
|
|
108
|
+
while (waitingUser = this.waitingUsers.dequeue()) {
|
|
109
|
+
waitingUser.reject(new UnavailablePool());
|
|
115
110
|
}
|
|
116
111
|
this.fsm.send('destroyed');
|
|
117
112
|
}
|
|
118
113
|
}
|
|
119
|
-
|
|
120
|
-
class UnavailablePool extends errors_1.CustomError {
|
|
114
|
+
export class UnavailablePool extends CustomError {
|
|
121
115
|
}
|
|
122
|
-
exports.UnavailablePool = UnavailablePool;
|
|
123
116
|
//# sourceMappingURL=pool.js.map
|
package/lib/pool.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pool.js","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pool.js","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "extra-pool",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "A library that helps you create object/thread/connection pools",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pool",
|
|
@@ -12,57 +12,57 @@
|
|
|
12
12
|
"lib",
|
|
13
13
|
"src"
|
|
14
14
|
],
|
|
15
|
+
"type": "module",
|
|
15
16
|
"main": "lib/index.js",
|
|
16
17
|
"types": "lib/index.d.ts",
|
|
17
18
|
"sideEffects": false,
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=22"
|
|
21
|
+
},
|
|
18
22
|
"repository": "git@github.com:BlackGlory/extra-pool.git",
|
|
19
23
|
"author": "BlackGlory <woshenmedoubuzhidao@blackglory.me>",
|
|
20
24
|
"license": "MIT",
|
|
21
25
|
"scripts": {
|
|
22
|
-
"
|
|
26
|
+
"prepare": "ts-patch install -s",
|
|
23
27
|
"lint": "eslint --ext .js,.jsx,.ts,.tsx --quiet src __tests__",
|
|
24
|
-
"test": "
|
|
25
|
-
"
|
|
26
|
-
"test:coverage": "jest --coverage --config jest.config.js",
|
|
27
|
-
"prepublishOnly": "run-s clean build",
|
|
28
|
+
"test": "vitest run",
|
|
29
|
+
"prepublishOnly": "run-s prepare clean build",
|
|
28
30
|
"clean": "rimraf lib",
|
|
29
|
-
"build": "
|
|
30
|
-
"build:compile": "tsc --project tsconfig.build.json --module commonjs --target es2018 --outDir lib",
|
|
31
|
-
"build:patch": "tscpaths -p tsconfig.build.json -s ./src -o ./lib",
|
|
31
|
+
"build": "tsc --project tsconfig.build.json",
|
|
32
32
|
"release": "standard-version"
|
|
33
33
|
},
|
|
34
34
|
"husky": {
|
|
35
35
|
"hooks": {
|
|
36
|
-
"pre-commit": "run-s lint build test",
|
|
36
|
+
"pre-commit": "run-s prepare lint build test",
|
|
37
37
|
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
|
|
38
38
|
}
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@commitlint/cli": "^
|
|
42
|
-
"@commitlint/config-conventional": "^
|
|
43
|
-
"@
|
|
44
|
-
"
|
|
45
|
-
"@typescript-eslint/parser": "^5.44.0",
|
|
46
|
-
"eslint": "^8.28.0",
|
|
41
|
+
"@commitlint/cli": "^20.4.2",
|
|
42
|
+
"@commitlint/config-conventional": "^20.4.2",
|
|
43
|
+
"@eslint/js": "^10.0.1",
|
|
44
|
+
"eslint": "^10.0.2",
|
|
47
45
|
"husky": "^4.3.8",
|
|
48
|
-
"jest": "^29.3.1",
|
|
49
46
|
"npm-run-all": "^4.1.5",
|
|
50
|
-
"
|
|
47
|
+
"return-style": "^4.0.0",
|
|
48
|
+
"rimraf": "^6.1.3",
|
|
51
49
|
"standard-version": "^9.5.0",
|
|
52
|
-
"ts-
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"typescript": "^
|
|
56
|
-
"
|
|
50
|
+
"ts-patch": "^3.3.0",
|
|
51
|
+
"tslib": "^2.8.1",
|
|
52
|
+
"typescript": "^5.9.3",
|
|
53
|
+
"typescript-eslint": "^8.56.1",
|
|
54
|
+
"typescript-transform-paths": "^3.5.6",
|
|
55
|
+
"vite": "^7.3.1",
|
|
56
|
+
"vite-tsconfig-paths": "^6.1.1",
|
|
57
|
+
"vitest": "^4.0.18"
|
|
57
58
|
},
|
|
58
59
|
"dependencies": {
|
|
59
|
-
"@blackglory/
|
|
60
|
-
"@blackglory/
|
|
61
|
-
"
|
|
62
|
-
"extra-
|
|
63
|
-
"extra-
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"rxjs": "^7.5.7"
|
|
60
|
+
"@blackglory/prelude": "^0.4.0",
|
|
61
|
+
"@blackglory/structures": "^0.14.12",
|
|
62
|
+
"extra-fsm": "^0.2.1",
|
|
63
|
+
"extra-promise": "^7.1.1",
|
|
64
|
+
"extra-timers": "^0.3.0",
|
|
65
|
+
"iterable-operator": "^6.0.0",
|
|
66
|
+
"rxjs": "^7.8.2"
|
|
67
67
|
}
|
|
68
68
|
}
|
package/src/index.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from './pool'
|
|
1
|
+
export * from './pool.js'
|
package/src/instance.ts
CHANGED
|
@@ -1,33 +1,41 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Awaitable, NonEmptyArray, pass } from '@blackglory/prelude'
|
|
2
|
+
import { ObservableFiniteStateMachine, IFiniteStateMachineSchema } from 'extra-fsm'
|
|
2
3
|
import { Deferred } from 'extra-promise'
|
|
3
|
-
import { ObservableFiniteStateMachine } from 'extra-fsm'
|
|
4
4
|
import { firstValueFrom } from 'rxjs'
|
|
5
|
-
import { filter } from 'rxjs/operators'
|
|
5
|
+
import { filter, map } from 'rxjs/operators'
|
|
6
6
|
|
|
7
7
|
export enum InstanceState {
|
|
8
|
-
|
|
9
|
-
,
|
|
10
|
-
,
|
|
11
|
-
,
|
|
12
|
-
,
|
|
8
|
+
Created
|
|
9
|
+
, Initializing
|
|
10
|
+
, Idle
|
|
11
|
+
, Busy
|
|
12
|
+
, Destroying
|
|
13
|
+
, Destroyed
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
type InstanceEvent =
|
|
16
|
-
| '
|
|
17
|
+
| 'init'
|
|
18
|
+
| 'inited'
|
|
19
|
+
| 'fail'
|
|
17
20
|
| 'use'
|
|
18
21
|
| 'idle'
|
|
19
22
|
| 'destroy'
|
|
20
23
|
| 'destroyed'
|
|
21
24
|
|
|
22
|
-
const instanceSchema = {
|
|
23
|
-
[InstanceState.
|
|
24
|
-
|
|
25
|
+
const instanceSchema: IFiniteStateMachineSchema<InstanceState, InstanceEvent> = {
|
|
26
|
+
[InstanceState.Created]: {
|
|
27
|
+
init: InstanceState.Initializing
|
|
28
|
+
, destroy: InstanceState.Destroyed
|
|
29
|
+
}
|
|
30
|
+
, [InstanceState.Initializing]: {
|
|
31
|
+
inited: InstanceState.Idle
|
|
32
|
+
, fail: InstanceState.Created
|
|
25
33
|
}
|
|
26
34
|
, [InstanceState.Idle]: {
|
|
27
|
-
use: InstanceState.
|
|
35
|
+
use: InstanceState.Busy
|
|
28
36
|
, destroy: InstanceState.Destroying
|
|
29
37
|
}
|
|
30
|
-
, [InstanceState.
|
|
38
|
+
, [InstanceState.Busy]: {
|
|
31
39
|
idle: InstanceState.Idle
|
|
32
40
|
}
|
|
33
41
|
, [InstanceState.Destroying]: {
|
|
@@ -37,97 +45,180 @@ const instanceSchema = {
|
|
|
37
45
|
}
|
|
38
46
|
|
|
39
47
|
export class Instance<T> {
|
|
40
|
-
private fsm: ObservableFiniteStateMachine<
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
48
|
+
private fsm: ObservableFiniteStateMachine<
|
|
49
|
+
InstanceState
|
|
50
|
+
, InstanceEvent
|
|
51
|
+
> = new ObservableFiniteStateMachine(
|
|
52
|
+
instanceSchema
|
|
53
|
+
, InstanceState.Created
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
private deferredCreateInstance: Deferred<T> = createDeferred()
|
|
57
|
+
private deferredDestroyInstance: Deferred<void> = createDeferred()
|
|
58
|
+
|
|
59
|
+
#users = 0
|
|
44
60
|
get users(): number {
|
|
45
|
-
return this
|
|
61
|
+
return this.#users
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
get state(): InstanceState {
|
|
65
|
+
return this.fsm.state
|
|
46
66
|
}
|
|
47
67
|
|
|
48
68
|
constructor(
|
|
49
|
-
createInstance: () => Awaitable<T>
|
|
69
|
+
private createInstance: () => Awaitable<T>
|
|
50
70
|
, private destroyInstance?: (value: T) => Awaitable<void>
|
|
51
|
-
) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
)
|
|
71
|
+
) {}
|
|
72
|
+
|
|
73
|
+
async use<U>(fn: (instance: T) => Awaitable<U>): Promise<U> {
|
|
74
|
+
const self = this
|
|
75
|
+
|
|
76
|
+
switch (this.fsm.state) {
|
|
77
|
+
case InstanceState.Created: {
|
|
78
|
+
addUser()
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
this.fsm.send('init')
|
|
82
|
+
|
|
83
|
+
let instance: T
|
|
84
|
+
try {
|
|
85
|
+
instance = await this.createInstance()
|
|
86
|
+
this.deferredCreateInstance.resolve(instance)
|
|
87
|
+
|
|
88
|
+
this.fsm.send('inited')
|
|
89
|
+
} catch (e) {
|
|
90
|
+
this.deferredCreateInstance.reject(e)
|
|
91
|
+
this.deferredCreateInstance = createDeferred()
|
|
57
92
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
93
|
+
this.fsm.send('fail')
|
|
94
|
+
|
|
95
|
+
throw e
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
this.fsm.send('use')
|
|
99
|
+
return await fn(instance)
|
|
100
|
+
} finally {
|
|
101
|
+
removeUser()
|
|
102
|
+
}
|
|
65
103
|
}
|
|
66
|
-
|
|
67
|
-
|
|
104
|
+
case InstanceState.Initializing: {
|
|
105
|
+
addUser()
|
|
68
106
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
107
|
+
try {
|
|
108
|
+
const instance = await this.deferredCreateInstance
|
|
72
109
|
|
|
73
|
-
|
|
74
|
-
return this.fsm.state
|
|
75
|
-
}
|
|
110
|
+
// 由导致初始化的调用将负责状态转换.
|
|
76
111
|
|
|
77
|
-
|
|
78
|
-
|
|
112
|
+
return await fn(instance)
|
|
113
|
+
} finally {
|
|
114
|
+
removeUser()
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
case InstanceState.Idle: {
|
|
118
|
+
addUser()
|
|
79
119
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
120
|
+
try {
|
|
121
|
+
this.fsm.send('use')
|
|
122
|
+
|
|
123
|
+
const instance = await this.deferredCreateInstance
|
|
124
|
+
|
|
125
|
+
return await fn(instance)
|
|
126
|
+
} finally {
|
|
127
|
+
removeUser()
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
case InstanceState.Busy: {
|
|
131
|
+
addUser()
|
|
85
132
|
|
|
86
|
-
|
|
133
|
+
try {
|
|
134
|
+
const instance = await this.deferredCreateInstance
|
|
87
135
|
|
|
88
|
-
|
|
89
|
-
|
|
136
|
+
return await fn(instance)
|
|
137
|
+
} finally {
|
|
138
|
+
removeUser()
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
case InstanceState.Destroying:
|
|
142
|
+
case InstanceState.Destroyed: throw new Error('The instance is not available')
|
|
143
|
+
default: throw new Error(`Unhandled state`)
|
|
90
144
|
}
|
|
91
145
|
|
|
92
|
-
|
|
93
|
-
|
|
146
|
+
function addUser(): void {
|
|
147
|
+
self.#users++
|
|
94
148
|
}
|
|
95
149
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const result = await fn(instance)
|
|
100
|
-
return result
|
|
101
|
-
} finally {
|
|
102
|
-
if ((--this._users) === 0) {
|
|
103
|
-
this.fsm.send('idle')
|
|
150
|
+
function removeUser(): void {
|
|
151
|
+
if ((--self.#users) === 0) {
|
|
152
|
+
if (self.fsm.can('idle')) self.fsm.send('idle')
|
|
104
153
|
}
|
|
105
154
|
}
|
|
106
155
|
}
|
|
107
156
|
|
|
108
157
|
async destroy(): Promise<void> {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
158
|
+
switch (this.fsm.state) {
|
|
159
|
+
case InstanceState.Created: {
|
|
160
|
+
this.fsm.send('destroy')
|
|
161
|
+
|
|
162
|
+
return
|
|
163
|
+
}
|
|
164
|
+
case InstanceState.Initializing: {
|
|
165
|
+
await this.waitForState(
|
|
166
|
+
InstanceState.Idle
|
|
167
|
+
, InstanceState.Created
|
|
116
168
|
)
|
|
117
|
-
|
|
169
|
+
|
|
170
|
+
return await this.destroy()
|
|
171
|
+
}
|
|
172
|
+
case InstanceState.Idle: {
|
|
173
|
+
this.fsm.send('destroy')
|
|
174
|
+
|
|
175
|
+
const instance = await this.deferredCreateInstance
|
|
176
|
+
|
|
177
|
+
try {
|
|
178
|
+
await this.destroyInstance?.(instance)
|
|
179
|
+
} catch (e) {
|
|
180
|
+
// 如果destroy过程中出错, 之后的所有destroy调用都会抛出相同错误.
|
|
181
|
+
// 此实例的状态将停留在Destroying, 这是预期行为.
|
|
182
|
+
this.deferredDestroyInstance.reject(e)
|
|
183
|
+
|
|
184
|
+
throw e
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
this.fsm.send('destroyed')
|
|
188
|
+
|
|
189
|
+
this.deferredDestroyInstance.resolve()
|
|
190
|
+
|
|
191
|
+
return
|
|
192
|
+
}
|
|
193
|
+
case InstanceState.Busy: {
|
|
194
|
+
await this.waitForState(InstanceState.Idle)
|
|
195
|
+
|
|
196
|
+
return await this.destroy()
|
|
197
|
+
}
|
|
198
|
+
case InstanceState.Destroying: {
|
|
199
|
+
await this.deferredDestroyInstance
|
|
200
|
+
|
|
201
|
+
return
|
|
202
|
+
}
|
|
203
|
+
case InstanceState.Destroyed: return
|
|
204
|
+
default: throw new Error(`Unhandled state`)
|
|
118
205
|
}
|
|
206
|
+
}
|
|
119
207
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
this.fsm.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
this.fsm.observeStateChanges().pipe(
|
|
128
|
-
filter(state => state.newState === InstanceState.Destroyed)
|
|
129
|
-
)
|
|
208
|
+
async waitForState<States extends NonEmptyArray<InstanceState>>(
|
|
209
|
+
...states: States
|
|
210
|
+
): Promise<States[number]> {
|
|
211
|
+
return await firstValueFrom(
|
|
212
|
+
this.fsm.observeStateChanges().pipe(
|
|
213
|
+
map(change => change.newState)
|
|
214
|
+
, filter(newState => states.includes(newState))
|
|
130
215
|
)
|
|
131
|
-
|
|
216
|
+
)
|
|
132
217
|
}
|
|
133
218
|
}
|
|
219
|
+
|
|
220
|
+
function createDeferred<T>(): Deferred<T> {
|
|
221
|
+
const deferred = new Deferred<T>()
|
|
222
|
+
Promise.resolve(deferred).catch(pass)
|
|
223
|
+
return deferred
|
|
224
|
+
}
|
package/src/pool.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { CustomError } from '@blackglory/
|
|
2
|
-
import { go, Awaitable } from '@blackglory/prelude'
|
|
1
|
+
import { go, Awaitable, CustomError, isntEmptyArray, assert } from '@blackglory/prelude'
|
|
3
2
|
import { Queue } from '@blackglory/structures'
|
|
4
|
-
import { FiniteStateMachine } from 'extra-fsm'
|
|
5
|
-
import { Deferred } from 'extra-promise'
|
|
3
|
+
import { FiniteStateMachine, IFiniteStateMachineSchema } from 'extra-fsm'
|
|
4
|
+
import { Deferred, each } from 'extra-promise'
|
|
6
5
|
import { toArray, filter } from 'iterable-operator'
|
|
7
6
|
import { setTimeout } from 'extra-timers'
|
|
8
|
-
import { Instance
|
|
7
|
+
import { Instance } from './instance.js'
|
|
9
8
|
|
|
10
9
|
interface IPoolOptions<T> {
|
|
11
10
|
create: () => Awaitable<T>
|
|
@@ -51,16 +50,16 @@ interface IPoolItem<T> {
|
|
|
51
50
|
}
|
|
52
51
|
|
|
53
52
|
enum PoolState {
|
|
54
|
-
Running
|
|
55
|
-
, Destroying
|
|
56
|
-
, Destroyed
|
|
53
|
+
Running
|
|
54
|
+
, Destroying
|
|
55
|
+
, Destroyed
|
|
57
56
|
}
|
|
58
57
|
|
|
59
58
|
type PoolEvent =
|
|
60
59
|
| 'destroy'
|
|
61
60
|
| 'destroyed'
|
|
62
61
|
|
|
63
|
-
const poolSchema = {
|
|
62
|
+
const poolSchema: IFiniteStateMachineSchema<PoolState, PoolEvent> = {
|
|
64
63
|
[PoolState.Running]: {
|
|
65
64
|
destroy: PoolState.Destroying
|
|
66
65
|
}
|
|
@@ -71,18 +70,25 @@ const poolSchema = {
|
|
|
71
70
|
}
|
|
72
71
|
|
|
73
72
|
export class Pool<T> {
|
|
74
|
-
private createInstance: () => Awaitable<T>
|
|
75
|
-
private destroyInstance?: (value: T) => Awaitable<void>
|
|
76
|
-
private fsm
|
|
73
|
+
private readonly createInstance: () => Awaitable<T>
|
|
74
|
+
private readonly destroyInstance?: (value: T) => Awaitable<void>
|
|
75
|
+
private readonly fsm: FiniteStateMachine<
|
|
76
|
+
PoolState
|
|
77
|
+
, PoolEvent
|
|
78
|
+
> = new FiniteStateMachine(
|
|
77
79
|
poolSchema
|
|
78
80
|
, PoolState.Running
|
|
79
81
|
)
|
|
80
|
-
private waitingUsers: Queue<Deferred<IPoolItem<T>>> = new Queue()
|
|
81
|
-
private items: Set<IPoolItem<T>> = new Set()
|
|
82
|
-
private maxInstances: number
|
|
83
|
-
private minInstances: number
|
|
84
|
-
private idleTimeout: number
|
|
85
|
-
private concurrencyPerInstance: number
|
|
82
|
+
private readonly waitingUsers: Queue<Deferred<IPoolItem<T>>> = new Queue()
|
|
83
|
+
private readonly items: Set<IPoolItem<T>> = new Set()
|
|
84
|
+
private readonly maxInstances: number
|
|
85
|
+
private readonly minInstances: number
|
|
86
|
+
private readonly idleTimeout: number
|
|
87
|
+
private readonly concurrencyPerInstance: number
|
|
88
|
+
|
|
89
|
+
get capacity(): number {
|
|
90
|
+
return this.maxInstances
|
|
91
|
+
}
|
|
86
92
|
|
|
87
93
|
get size(): number {
|
|
88
94
|
return this.items.size
|
|
@@ -102,6 +108,8 @@ export class Pool<T> {
|
|
|
102
108
|
* 函数的使用者应该尊重阻塞, 否则会意外制造大量非必要的Promise.
|
|
103
109
|
*/
|
|
104
110
|
async use<U>(fn: (instance: T) => Awaitable<U>): Promise<U> {
|
|
111
|
+
assert(this.fsm.matches(PoolState.Running), 'The pool is not available')
|
|
112
|
+
|
|
105
113
|
const self = this
|
|
106
114
|
|
|
107
115
|
const item = go(() => {
|
|
@@ -110,13 +118,12 @@ export class Pool<T> {
|
|
|
110
118
|
, item => item.instance.users < this.concurrencyPerInstance
|
|
111
119
|
))
|
|
112
120
|
|
|
113
|
-
if (candidateItems
|
|
121
|
+
if (isntEmptyArray(candidateItems)) {
|
|
122
|
+
// 找到负载最低(用户量最少)的项目.
|
|
114
123
|
return candidateItems.reduce((previous, current) => {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
return previous
|
|
119
|
-
}
|
|
124
|
+
return current.instance.users < previous.instance.users
|
|
125
|
+
? current
|
|
126
|
+
: previous
|
|
120
127
|
})
|
|
121
128
|
}
|
|
122
129
|
})
|
|
@@ -177,14 +184,12 @@ export class Pool<T> {
|
|
|
177
184
|
async destroy(): Promise<void> {
|
|
178
185
|
this.fsm.send('destroy')
|
|
179
186
|
|
|
180
|
-
|
|
181
|
-
await item.instance.destroy()
|
|
182
|
-
}
|
|
187
|
+
await each(this.items, item => item.instance.destroy())
|
|
183
188
|
this.items.clear()
|
|
184
189
|
|
|
185
|
-
let
|
|
186
|
-
while (
|
|
187
|
-
|
|
190
|
+
let waitingUser: Deferred<IPoolItem<T>> | undefined
|
|
191
|
+
while (waitingUser = this.waitingUsers.dequeue()) {
|
|
192
|
+
waitingUser.reject(new UnavailablePool())
|
|
188
193
|
}
|
|
189
194
|
|
|
190
195
|
this.fsm.send('destroyed')
|