fib-pool 1.6.2 → 1.7.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/Readme.md +48 -4
- package/package.json +3 -33
- package/src/index.ts +258 -129
- package/tsconfig.json +6 -10
- package/.travis.yml +0 -99
- package/appveyor.yml +0 -26
- package/lib/index.js +0 -163
- package/typings/index.d.ts +0 -43
package/Readme.md
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
## Generic resource pooling for fibjs
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.org/package/fib-pool)
|
|
4
|
-
[](https://travis-ci.org/fibjs/fib-pool)
|
|
5
|
-
[](https://ci.appveyor.com/project/richardo2016/fib-pool)
|
|
6
4
|
|
|
7
5
|
## Install
|
|
8
6
|
|
|
@@ -13,7 +11,8 @@ npm install fib-pool [--save]
|
|
|
13
11
|
## Test
|
|
14
12
|
|
|
15
13
|
```sh
|
|
16
|
-
npm
|
|
14
|
+
npm test # fibjs test
|
|
15
|
+
npm run typecheck # fibjs --check
|
|
17
16
|
```
|
|
18
17
|
|
|
19
18
|
## Creating a pool
|
|
@@ -103,6 +102,47 @@ var res = p("test", (conn) => {
|
|
|
103
102
|
|
|
104
103
|
```
|
|
105
104
|
|
|
105
|
+
## Options
|
|
106
|
+
|
|
107
|
+
| option | type | default | notes |
|
|
108
|
+
|---|---|---|---|
|
|
109
|
+
| `create` | `(name) => item` | — | required; throwing is a failure, retried up to `retry` times |
|
|
110
|
+
| `destroy` | `(item) => void` | close/destroy/dispose | defaults to calling whichever of those three the item provides |
|
|
111
|
+
| `maxsize` | positive integer | `10` | caps both concurrency and the number of idle items |
|
|
112
|
+
| `timeout` | positive number (ms) | `60000` | how long an idle item is kept |
|
|
113
|
+
| `retry` | positive integer | `1` | how many times `create` is attempted before giving up |
|
|
114
|
+
| `strict` | boolean | `true` | see below |
|
|
115
|
+
|
|
116
|
+
Invalid `maxsize`, `timeout` and `retry` values throw a `TypeError` when the
|
|
117
|
+
pool is created rather than being silently coerced:
|
|
118
|
+
|
|
119
|
+
```js
|
|
120
|
+
Pool({ create: () => ({}), maxsize: 0 }); // TypeError: maxsize must be a positive integer, received 0
|
|
121
|
+
Pool({ create: () => ({}), timeout: -5 }); // TypeError: timeout must be a positive number, received -5
|
|
122
|
+
Pool({ create: () => ({}), retry: true }); // TypeError: retry must be a positive integer, received true
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
The idle list is capped by `maxsize` as well: a pool used with more names than
|
|
126
|
+
that evicts the least recently used item (and destroys it) instead of keeping
|
|
127
|
+
one idle item per name.
|
|
128
|
+
|
|
129
|
+
## Reentrancy
|
|
130
|
+
|
|
131
|
+
A pool cannot be acquired twice from the same fiber. The inner call would wait
|
|
132
|
+
for a permit that the outer call is holding, which never arrives, so it is
|
|
133
|
+
rejected instead of deadlocking:
|
|
134
|
+
|
|
135
|
+
```js
|
|
136
|
+
var p = Pool({ create: () => ({}), maxsize: 1 });
|
|
137
|
+
|
|
138
|
+
p(() => {
|
|
139
|
+
p(() => {}); // Error: Cannot pool again from the same fiber
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Nesting different pools, or acquiring the same pool from another fiber, works as
|
|
144
|
+
expected.
|
|
145
|
+
|
|
106
146
|
## Strict mode (default: enabled)
|
|
107
147
|
|
|
108
148
|
By default, the pool uses strict mode to prevent accessing pooled objects outside of the callback function. This helps prevent resource leaks and ensures proper resource management.
|
|
@@ -127,9 +167,13 @@ p((obj) => {
|
|
|
127
167
|
leaked.getValue(); // Error!
|
|
128
168
|
```
|
|
129
169
|
|
|
130
|
-
|
|
170
|
+
The guarded object behaves like the item for reading and inspection —
|
|
171
|
+
`key in obj`, `Object.keys(obj)`, `for...in`, `JSON.stringify(obj)` and
|
|
172
|
+
`util.inspect(obj)` all report the item's own members — while the guards remain:
|
|
173
|
+
|
|
131
174
|
- Setting properties on pooled objects
|
|
132
175
|
- Calling `close()`, `destroy()`, or `dispose()` methods directly
|
|
176
|
+
- Extracting a method and calling it after the callback returned
|
|
133
177
|
|
|
134
178
|
To disable strict mode:
|
|
135
179
|
|
package/package.json
CHANGED
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fib-pool",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "Generic resource pooling for fibjs",
|
|
5
|
-
"main": "./src",
|
|
6
|
-
"types": "typings/index.d.ts",
|
|
5
|
+
"main": "./src/index.ts",
|
|
7
6
|
"type": "module",
|
|
8
7
|
"repository": "git://github.com/fibjs/fib-pool.git",
|
|
9
8
|
"author": "Liu Hu <lion@9465.net>",
|
|
10
|
-
"contributors": [
|
|
11
|
-
{
|
|
12
|
-
"name": "Richard",
|
|
13
|
-
"email": "richardo2016@gmail.com"
|
|
14
|
-
}
|
|
15
|
-
],
|
|
16
9
|
"homepage": "https://github.com/fibjs/fib-pool",
|
|
17
10
|
"license": "MIT",
|
|
18
11
|
"keywords": [
|
|
@@ -22,29 +15,6 @@
|
|
|
22
15
|
],
|
|
23
16
|
"scripts": {
|
|
24
17
|
"test": "fibjs test",
|
|
25
|
-
"typecheck": "
|
|
26
|
-
"ci": "npm run typecheck && npm run test"
|
|
27
|
-
},
|
|
28
|
-
"devDependencies": {
|
|
29
|
-
"@fibjs/ci": "^2.3.0",
|
|
30
|
-
"@fibjs/types": "^0.27.1",
|
|
31
|
-
"fib-typify": "^0.8.4",
|
|
32
|
-
"typescript": "^5.9.3"
|
|
33
|
-
},
|
|
34
|
-
"ci": {
|
|
35
|
-
"type": "travis, appveyor",
|
|
36
|
-
"version": [
|
|
37
|
-
"0.21.0",
|
|
38
|
-
"0.22.0",
|
|
39
|
-
"0.23.0",
|
|
40
|
-
"0.24.0",
|
|
41
|
-
"0.25.0",
|
|
42
|
-
"0.26.0",
|
|
43
|
-
"0.26.1",
|
|
44
|
-
"0.27.0",
|
|
45
|
-
"0.28.0",
|
|
46
|
-
"0.29.0",
|
|
47
|
-
"0.30.0"
|
|
48
|
-
]
|
|
18
|
+
"typecheck": "fibjs --check"
|
|
49
19
|
}
|
|
50
20
|
}
|
package/src/index.ts
CHANGED
|
@@ -2,29 +2,24 @@ import * as coroutine from 'coroutine';
|
|
|
2
2
|
import * as util from 'util';
|
|
3
3
|
|
|
4
4
|
declare namespace FibPoolNS {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
/**
|
|
6
|
+
* The shape `create` must return. All three members are optional: the
|
|
7
|
+
* default destroy calls whichever of them the item actually provides, and
|
|
8
|
+
* a non-function value (a plain property) is ignored.
|
|
9
|
+
*/
|
|
7
10
|
interface DippedItemStruct {
|
|
8
|
-
close?:
|
|
9
|
-
destroy?:
|
|
10
|
-
dispose?:
|
|
11
|
+
close?: unknown;
|
|
12
|
+
destroy?: unknown;
|
|
13
|
+
dispose?: unknown;
|
|
11
14
|
}
|
|
12
15
|
|
|
13
16
|
interface FibPoolOptionArgs<T extends DippedItemStruct = {}> {
|
|
14
|
-
create: (name: string) => T
|
|
17
|
+
create: (name: string) => T;
|
|
15
18
|
destroy?: (item?: T) => any;
|
|
16
|
-
maxsize?: number
|
|
17
|
-
timeout?: number
|
|
18
|
-
retry?: number
|
|
19
|
-
strict?: boolean
|
|
20
|
-
}
|
|
21
|
-
interface FibPoolOptionResult<T extends DippedItemStruct = {}> {
|
|
22
|
-
create: FibPoolOptionArgs<T>['create']
|
|
23
|
-
destroy: FibPoolOptionArgs<T>['destroy']
|
|
24
|
-
maxsize: FibPoolOptionArgs<T>['maxsize']
|
|
25
|
-
timeout: FibPoolOptionArgs<T>['timeout']
|
|
26
|
-
retry: FibPoolOptionArgs<T>['retry']
|
|
27
|
-
strict: FibPoolOptionArgs<T>['strict']
|
|
19
|
+
maxsize?: number;
|
|
20
|
+
timeout?: number;
|
|
21
|
+
retry?: number;
|
|
22
|
+
strict?: boolean;
|
|
28
23
|
}
|
|
29
24
|
|
|
30
25
|
interface FibPoolDipperFn<T_CREATED_ITEM, T_POOL_FUNC_RETURN = any> {
|
|
@@ -35,23 +30,120 @@ declare namespace FibPoolNS {
|
|
|
35
30
|
<T_RETURN = T_POOL_FUNC_RETURN>(name: string, func: FibPoolDipperFn<T_CREATED_ITEM, T_RETURN>): T_RETURN
|
|
36
31
|
<T_RETURN = T_POOL_FUNC_RETURN>(func: FibPoolDipperFn<T_CREATED_ITEM, T_RETURN>): T_RETURN
|
|
37
32
|
} & {
|
|
38
|
-
connections
|
|
39
|
-
info
|
|
33
|
+
connections(): number;
|
|
34
|
+
info(): {
|
|
40
35
|
maxsize: number;
|
|
41
36
|
count: number;
|
|
42
37
|
running: number;
|
|
43
38
|
wait: number;
|
|
44
39
|
timeout: number;
|
|
45
|
-
};
|
|
46
|
-
clear
|
|
40
|
+
};
|
|
41
|
+
clear(): void;
|
|
47
42
|
}
|
|
48
43
|
}
|
|
49
44
|
|
|
50
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Calls the value when it is a function and ignores it otherwise: a pooled item
|
|
47
|
+
* may expose a plain property named close/destroy/dispose.
|
|
48
|
+
*/
|
|
49
|
+
function callIfFunction(v: unknown) {
|
|
50
|
+
if (util.isFunction(v))
|
|
51
|
+
(v as (...args: any[]) => any)();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* `maxsize` and `retry` are counts, `timeout` is a duration. Both are rejected
|
|
56
|
+
* up front instead of being silently coerced into a default or handed to the
|
|
57
|
+
* semaphore, which would fail far from the option that is wrong.
|
|
58
|
+
*/
|
|
59
|
+
function toPositiveInteger(value: unknown, name: string): number {
|
|
60
|
+
if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0 || Math.floor(value) !== value)
|
|
61
|
+
throw new TypeError(`${name} must be a positive integer, received ${String(value)}`);
|
|
62
|
+
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function toPositiveNumber(value: unknown, name: string): number {
|
|
67
|
+
if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0)
|
|
68
|
+
throw new TypeError(`${name} must be a positive number, received ${String(value)}`);
|
|
69
|
+
|
|
70
|
+
return value;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The pool always hands out an Error: `create` or the callback may throw a
|
|
75
|
+
* string, a number, or nothing at all, and the caller expects to be able to
|
|
76
|
+
* read `.message`. A thrown falsy value (0, '', false, null, undefined) is still
|
|
77
|
+
* a failure and must not be mistaken for a successful create, so this returns an
|
|
78
|
+
* Error for every input.
|
|
79
|
+
*/
|
|
80
|
+
function toError(e: unknown): Error {
|
|
81
|
+
if (e instanceof Error)
|
|
82
|
+
return e;
|
|
83
|
+
|
|
51
84
|
if (typeof e === 'string')
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
85
|
+
return new Error(e);
|
|
86
|
+
|
|
87
|
+
return new Error(String(e));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Guards a pooled item while the callback runs. The item is read through a
|
|
92
|
+
* getter rather than captured by value: once it goes back to the pool the
|
|
93
|
+
* handle must stop working, which is what the "outside of pool scope" error
|
|
94
|
+
* reports.
|
|
95
|
+
*
|
|
96
|
+
* The reflection traps matter as much as the getter: without them `key in obj`,
|
|
97
|
+
* `Object.keys`, `for...in` and `JSON.stringify` all see the empty proxy target
|
|
98
|
+
* instead of the item.
|
|
99
|
+
*/
|
|
100
|
+
function wrapStrict<T extends object>(item: () => T | undefined): T {
|
|
101
|
+
const current = () => {
|
|
102
|
+
const o = item();
|
|
103
|
+
if (o === undefined)
|
|
104
|
+
throw new Error("access object outside of pool scope");
|
|
105
|
+
|
|
106
|
+
return o;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
return new Proxy({}, {
|
|
110
|
+
get(target, prop) {
|
|
111
|
+
if (prop === 'close' || prop === 'destroy' || prop === 'dispose')
|
|
112
|
+
throw new Error("Cannot close/destroy/dispose");
|
|
113
|
+
|
|
114
|
+
const o = current();
|
|
115
|
+
const value = (o as any)[prop];
|
|
116
|
+
if (typeof value === 'function') {
|
|
117
|
+
/* the item is resolved again when the method runs: a method
|
|
118
|
+
extracted inside the callback and called after it returned
|
|
119
|
+
must not keep the item alive outside its scope */
|
|
120
|
+
return function () {
|
|
121
|
+
const self = current();
|
|
122
|
+
return (self as any)[prop].apply(self, arguments);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return value;
|
|
126
|
+
},
|
|
127
|
+
has(target, prop) {
|
|
128
|
+
/* no close/destroy/dispose refusal here: the key is reported like
|
|
129
|
+
any other (ownKeys lists it too), only reading or calling those
|
|
130
|
+
members is refused, in `get` */
|
|
131
|
+
return Reflect.has(current() as object, prop);
|
|
132
|
+
},
|
|
133
|
+
ownKeys() {
|
|
134
|
+
return Reflect.ownKeys(current() as object);
|
|
135
|
+
},
|
|
136
|
+
getOwnPropertyDescriptor(target, prop) {
|
|
137
|
+
const d = Reflect.getOwnPropertyDescriptor(current() as object, prop);
|
|
138
|
+
|
|
139
|
+
/* every key of the proxy target is configurable: a descriptor that
|
|
140
|
+
claims otherwise would break the proxy invariants */
|
|
141
|
+
return d && Object.assign(d, { configurable: true });
|
|
142
|
+
},
|
|
143
|
+
set() {
|
|
144
|
+
throw new Error("Cannot set property");
|
|
145
|
+
}
|
|
146
|
+
}) as T;
|
|
55
147
|
}
|
|
56
148
|
|
|
57
149
|
export default function GetPool<
|
|
@@ -63,34 +155,41 @@ export default function GetPool<
|
|
|
63
155
|
timeout?: number
|
|
64
156
|
): FibPoolNS.FibPool<T_CREATED_ITEM, T_POOL_FUNC_RETURN> {
|
|
65
157
|
/* clean input arguments :start */
|
|
66
|
-
let opt
|
|
158
|
+
let opt: FibPoolNS.FibPoolOptionArgs<T_CREATED_ITEM>;
|
|
159
|
+
|
|
67
160
|
if (util.isFunction(_opt)) {
|
|
68
|
-
opt =
|
|
69
|
-
create: _opt,
|
|
161
|
+
opt = {
|
|
162
|
+
create: _opt as (name: string) => T_CREATED_ITEM,
|
|
70
163
|
maxsize: maxsize,
|
|
71
164
|
timeout: timeout
|
|
72
165
|
};
|
|
166
|
+
} else {
|
|
167
|
+
opt = _opt as FibPoolNS.FibPoolOptionArgs<T_CREATED_ITEM>;
|
|
73
168
|
}
|
|
74
169
|
|
|
75
170
|
const create = opt.create;
|
|
76
|
-
const destroy = opt.destroy || ((o: {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
171
|
+
const destroy = opt.destroy || ((o: FibPoolNS.DippedItemStruct | undefined) => {
|
|
172
|
+
/* create is allowed to return null/undefined: there is nothing to
|
|
173
|
+
destroy then */
|
|
174
|
+
if (o === null || o === undefined)
|
|
175
|
+
return;
|
|
176
|
+
|
|
177
|
+
callIfFunction(o.close);
|
|
178
|
+
callIfFunction(o.destroy);
|
|
179
|
+
callIfFunction(o.dispose);
|
|
84
180
|
});
|
|
85
181
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
182
|
+
const maxsizeValue = opt.maxsize === undefined ? 10 : toPositiveInteger(opt.maxsize, 'maxsize');
|
|
183
|
+
let timeoutValue = opt.timeout === undefined ? 60000 : toPositiveNumber(opt.timeout, 'timeout');
|
|
184
|
+
|
|
185
|
+
/* the idle scan runs at a tenth of the timeout, but never more often than
|
|
186
|
+
every 10ms */
|
|
187
|
+
let tm = timeoutValue / 10;
|
|
89
188
|
if (tm < 10)
|
|
90
189
|
tm = 10;
|
|
91
190
|
|
|
92
|
-
const retry = opt.retry
|
|
93
|
-
const
|
|
191
|
+
const retry = opt.retry === undefined ? 1 : toPositiveInteger(opt.retry, 'retry');
|
|
192
|
+
const strictMode = opt.strict === undefined ? true : opt.strict;
|
|
94
193
|
/* clean input arguments :end */
|
|
95
194
|
|
|
96
195
|
let pools = <{
|
|
@@ -109,17 +208,16 @@ export default function GetPool<
|
|
|
109
208
|
let count = 0;
|
|
110
209
|
let running = 0;
|
|
111
210
|
|
|
112
|
-
const sem = new coroutine.Semaphore(
|
|
113
|
-
let clearTimer: Class_Timer;
|
|
211
|
+
const sem = new coroutine.Semaphore(maxsizeValue);
|
|
212
|
+
let clearTimer: Class_Timer | null = null;
|
|
114
213
|
|
|
115
214
|
function clearPool() {
|
|
116
|
-
let c: typeof pools[any];
|
|
117
215
|
const d = new Date().getTime();
|
|
118
216
|
|
|
119
217
|
while (count) {
|
|
120
|
-
c = pools[0];
|
|
218
|
+
const c = pools[0];
|
|
121
219
|
|
|
122
|
-
if (d - c.time.getTime() >
|
|
220
|
+
if (d - c.time.getTime() > timeoutValue) {
|
|
123
221
|
pools = pools.slice(1);
|
|
124
222
|
count--;
|
|
125
223
|
|
|
@@ -135,18 +233,22 @@ export default function GetPool<
|
|
|
135
233
|
clearTimer = null;
|
|
136
234
|
}
|
|
137
235
|
} else if (!clearTimer) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
236
|
+
/* the timer must not hold the process alive: the pool is idle
|
|
237
|
+
between uses */
|
|
238
|
+
clearTimer = setInterval(clearPool, tm).unref();
|
|
141
239
|
}
|
|
142
240
|
}
|
|
143
241
|
|
|
242
|
+
/**
|
|
243
|
+
* Hand the item back: wake the first waiter bound to `name`, or park the
|
|
244
|
+
* item in the idle list. An errored item is never parked, but a successful
|
|
245
|
+
* create may legitimately produce `undefined` and is parked as is.
|
|
246
|
+
*/
|
|
144
247
|
function putback(
|
|
145
|
-
name:
|
|
146
|
-
o: T_CREATED_ITEM,
|
|
248
|
+
name: string,
|
|
249
|
+
o: T_CREATED_ITEM | undefined,
|
|
147
250
|
e?: Error
|
|
148
251
|
) {
|
|
149
|
-
e = ensureError(e);
|
|
150
252
|
for (let i = 0; i < jobs.length; i++) {
|
|
151
253
|
const job = jobs[i];
|
|
152
254
|
if (job.name === name) {
|
|
@@ -158,26 +260,38 @@ export default function GetPool<
|
|
|
158
260
|
}
|
|
159
261
|
}
|
|
160
262
|
|
|
161
|
-
if (e === undefined)
|
|
263
|
+
if (e === undefined) {
|
|
264
|
+
/* The idle list is capped by maxsize: a pool used with more names
|
|
265
|
+
than that would otherwise keep one idle item per name and grow
|
|
266
|
+
without bound. The oldest idle item gives way to the one just
|
|
267
|
+
returned, so the most recently used names stay cached. */
|
|
268
|
+
if (count >= maxsizeValue && count) {
|
|
269
|
+
const evicted = pools[0];
|
|
270
|
+
pools = pools.slice(1);
|
|
271
|
+
count--;
|
|
272
|
+
coroutine.start(destroy, evicted.o);
|
|
273
|
+
}
|
|
274
|
+
|
|
162
275
|
pools[count++] = {
|
|
163
|
-
o: o,
|
|
276
|
+
o: o as T_CREATED_ITEM,
|
|
164
277
|
name: name,
|
|
165
278
|
time: new Date()
|
|
166
279
|
};
|
|
280
|
+
}
|
|
167
281
|
}
|
|
168
282
|
|
|
169
283
|
function connect(name: string) {
|
|
170
|
-
let o: T_CREATED_ITEM;
|
|
171
|
-
let
|
|
172
|
-
let err: Error;
|
|
284
|
+
let o: T_CREATED_ITEM | undefined;
|
|
285
|
+
let err: Error | undefined;
|
|
173
286
|
|
|
174
|
-
|
|
287
|
+
for (let cn = 1; ; cn++) {
|
|
175
288
|
try {
|
|
176
289
|
o = create(name);
|
|
177
290
|
break;
|
|
178
291
|
} catch (e) {
|
|
179
|
-
|
|
180
|
-
|
|
292
|
+
/* any thrown value is a failure, including 0 or '' */
|
|
293
|
+
if (cn >= retry) {
|
|
294
|
+
err = toError(e);
|
|
181
295
|
break;
|
|
182
296
|
}
|
|
183
297
|
}
|
|
@@ -186,100 +300,115 @@ export default function GetPool<
|
|
|
186
300
|
putback(name, o, err);
|
|
187
301
|
}
|
|
188
302
|
|
|
189
|
-
|
|
303
|
+
/* The fiber is used as fiber-local storage: it is the only place a marker
|
|
304
|
+
can live without leaking between fibers. A fiber that already holds an
|
|
305
|
+
item from this pool would wait on itself when it asks again, which the
|
|
306
|
+
semaphore can never satisfy once every permit is out. */
|
|
307
|
+
const fiberDepth = Symbol('fib-pool depth');
|
|
308
|
+
|
|
309
|
+
type FiberState = Class_Fiber & { [fiberDepth]?: true };
|
|
310
|
+
|
|
311
|
+
function enterFiber(): FiberState {
|
|
312
|
+
const fiber = coroutine.current() as FiberState;
|
|
313
|
+
|
|
314
|
+
if (fiber[fiberDepth])
|
|
315
|
+
throw new Error("Cannot pool again from the same fiber: the pool is already held by this callback");
|
|
316
|
+
|
|
317
|
+
fiber[fiberDepth] = true;
|
|
318
|
+
|
|
319
|
+
return fiber;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function leaveFiber(fiber: FiberState) {
|
|
323
|
+
delete fiber[fiberDepth];
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const pool = ((
|
|
190
327
|
name: string | FibPoolNS.FibPoolDipperFn<T_CREATED_ITEM, T_POOL_FUNC_RETURN>,
|
|
191
328
|
func?: FibPoolNS.FibPoolDipperFn<T_CREATED_ITEM, T_POOL_FUNC_RETURN>
|
|
192
329
|
) => {
|
|
193
330
|
if (typeof name === 'function') {
|
|
194
|
-
func = name
|
|
331
|
+
func = name;
|
|
195
332
|
name = "";
|
|
196
|
-
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const dip = func as FibPoolNS.FibPoolDipperFn<T_CREATED_ITEM, T_POOL_FUNC_RETURN>;
|
|
197
336
|
|
|
198
337
|
let r: T_POOL_FUNC_RETURN;
|
|
199
|
-
let o:
|
|
200
|
-
let
|
|
338
|
+
let o: T_CREATED_ITEM | undefined;
|
|
339
|
+
let reused = false;
|
|
340
|
+
const fiber = enterFiber();
|
|
201
341
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
342
|
+
try {
|
|
343
|
+
clearPool();
|
|
344
|
+
sem.acquire();
|
|
345
|
+
|
|
346
|
+
if (count) {
|
|
347
|
+
for (let i = count - 1; i >= 0; i--)
|
|
348
|
+
if (pools[i].name === name) {
|
|
349
|
+
reused = true
|
|
350
|
+
o = pools[i].o;
|
|
351
|
+
pools.splice(i, 1);
|
|
352
|
+
count--;
|
|
353
|
+
break;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
215
356
|
|
|
216
|
-
|
|
217
|
-
|
|
357
|
+
if (!reused) {
|
|
358
|
+
coroutine.start(connect, name);
|
|
218
359
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
360
|
+
const job: typeof jobs[any] = {
|
|
361
|
+
name: name,
|
|
362
|
+
ev: new coroutine.Event()
|
|
363
|
+
};
|
|
364
|
+
jobs.push(job);
|
|
365
|
+
|
|
366
|
+
job.ev.wait();
|
|
367
|
+
if (job.e) {
|
|
368
|
+
sem.post();
|
|
369
|
+
throw job.e;
|
|
370
|
+
}
|
|
371
|
+
o = job.o;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
running++;
|
|
375
|
+
try {
|
|
376
|
+
const po = (strictMode && o !== null && typeof o === 'object')
|
|
377
|
+
? wrapStrict<T_CREATED_ITEM & object>(() => o as T_CREATED_ITEM & object)
|
|
378
|
+
: o as T_CREATED_ITEM;
|
|
224
379
|
|
|
225
|
-
|
|
226
|
-
|
|
380
|
+
r = dip(po);
|
|
381
|
+
putback(name, o);
|
|
382
|
+
o = undefined;
|
|
383
|
+
} catch (e) {
|
|
384
|
+
if (o !== undefined)
|
|
385
|
+
coroutine.start(destroy, o);
|
|
386
|
+
o = undefined;
|
|
387
|
+
throw toError(e);
|
|
388
|
+
} finally {
|
|
389
|
+
running--;
|
|
227
390
|
sem.post();
|
|
228
|
-
|
|
391
|
+
clearPool();
|
|
229
392
|
}
|
|
230
|
-
o = job.o;
|
|
231
|
-
}
|
|
232
393
|
|
|
233
|
-
|
|
234
|
-
try {
|
|
235
|
-
const po = (_strict && typeof o === 'object') ? new Proxy({}, {
|
|
236
|
-
get: function (target, prop) {
|
|
237
|
-
if (prop === 'close' || prop === 'destroy' || prop === 'dispose')
|
|
238
|
-
throw new Error("Cannot close/destroy/dispose");
|
|
239
|
-
if (o === undefined)
|
|
240
|
-
throw new Error("access object outside of pool scope");
|
|
241
|
-
const value = o[prop];
|
|
242
|
-
if (typeof value === 'function') {
|
|
243
|
-
return function () {
|
|
244
|
-
return value.apply(o, arguments);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
return value;
|
|
248
|
-
},
|
|
249
|
-
set: function (target, prop, value) {
|
|
250
|
-
throw new Error("Cannot set property");
|
|
251
|
-
}
|
|
252
|
-
}) as any : o;
|
|
253
|
-
|
|
254
|
-
r = func(po as T_CREATED_ITEM);
|
|
255
|
-
putback(name, o);
|
|
256
|
-
o = undefined;
|
|
257
|
-
} catch (e) {
|
|
258
|
-
if (o !== undefined)
|
|
259
|
-
coroutine.start(destroy, o);
|
|
260
|
-
o = undefined;
|
|
261
|
-
throw ensureError(e);
|
|
394
|
+
return r;
|
|
262
395
|
} finally {
|
|
263
|
-
|
|
264
|
-
sem.post();
|
|
265
|
-
clearPool();
|
|
396
|
+
leaveFiber(fiber);
|
|
266
397
|
}
|
|
267
|
-
|
|
268
|
-
return r;
|
|
269
|
-
};
|
|
398
|
+
}) as FibPoolNS.FibPool<T_CREATED_ITEM, T_POOL_FUNC_RETURN>;
|
|
270
399
|
|
|
271
400
|
pool.connections = () => count
|
|
272
401
|
|
|
273
402
|
pool.info = () => ({
|
|
274
|
-
maxsize:
|
|
403
|
+
maxsize: maxsizeValue,
|
|
275
404
|
count: count,
|
|
276
405
|
running: running,
|
|
277
406
|
wait: sem.count(),
|
|
278
|
-
timeout:
|
|
407
|
+
timeout: timeoutValue
|
|
279
408
|
})
|
|
280
409
|
|
|
281
410
|
pool.clear = () => {
|
|
282
|
-
|
|
411
|
+
timeoutValue = -1;
|
|
283
412
|
clearPool();
|
|
284
413
|
}
|
|
285
414
|
|
package/tsconfig.json
CHANGED
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
|
-
|
|
3
|
+
// `fibjs --check` derives its default lib from the target: with es6
|
|
4
|
+
// that is lib.dom, which collides with the fibjs built-in types.
|
|
5
|
+
// es2020 is the lowest target whose default lib stays es-only.
|
|
6
|
+
"target": "es2020",
|
|
4
7
|
"module": "commonjs",
|
|
5
|
-
"
|
|
6
|
-
"removeComments": true,
|
|
7
|
-
"noEmit": true,
|
|
8
|
-
"types": [
|
|
9
|
-
"@fibjs/types"
|
|
10
|
-
]
|
|
8
|
+
"noEmit": true
|
|
11
9
|
},
|
|
12
10
|
"exclude": [
|
|
13
|
-
"test",
|
|
14
|
-
"typings",
|
|
15
11
|
"node_modules"
|
|
16
12
|
]
|
|
17
|
-
}
|
|
13
|
+
}
|
package/.travis.yml
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
language: node_js
|
|
2
|
-
node_js: '6'
|
|
3
|
-
matrix:
|
|
4
|
-
include:
|
|
5
|
-
- os: linux
|
|
6
|
-
env:
|
|
7
|
-
- VERSION=0.21.0
|
|
8
|
-
- os: linux
|
|
9
|
-
env:
|
|
10
|
-
- VERSION=0.22.0
|
|
11
|
-
- os: linux
|
|
12
|
-
env:
|
|
13
|
-
- VERSION=0.23.0
|
|
14
|
-
- os: linux
|
|
15
|
-
env:
|
|
16
|
-
- VERSION=0.24.0
|
|
17
|
-
- os: linux
|
|
18
|
-
env:
|
|
19
|
-
- VERSION=0.25.0
|
|
20
|
-
- os: linux
|
|
21
|
-
env:
|
|
22
|
-
- VERSION=0.26.0
|
|
23
|
-
- os: linux
|
|
24
|
-
env:
|
|
25
|
-
- VERSION=0.26.1
|
|
26
|
-
- os: linux
|
|
27
|
-
env:
|
|
28
|
-
- VERSION=0.27.0
|
|
29
|
-
- os: linux
|
|
30
|
-
env:
|
|
31
|
-
- VERSION=0.28.0
|
|
32
|
-
- os: linux
|
|
33
|
-
env:
|
|
34
|
-
- VERSION=0.29.0
|
|
35
|
-
- os: linux
|
|
36
|
-
env:
|
|
37
|
-
- VERSION=0.30.0
|
|
38
|
-
- os: osx
|
|
39
|
-
osx_image: xcode9
|
|
40
|
-
env:
|
|
41
|
-
- VERSION=0.21.0
|
|
42
|
-
- os: osx
|
|
43
|
-
osx_image: xcode9
|
|
44
|
-
env:
|
|
45
|
-
- VERSION=0.22.0
|
|
46
|
-
- os: osx
|
|
47
|
-
osx_image: xcode9
|
|
48
|
-
env:
|
|
49
|
-
- VERSION=0.23.0
|
|
50
|
-
- os: osx
|
|
51
|
-
osx_image: xcode9
|
|
52
|
-
env:
|
|
53
|
-
- VERSION=0.24.0
|
|
54
|
-
- os: osx
|
|
55
|
-
osx_image: xcode9
|
|
56
|
-
env:
|
|
57
|
-
- VERSION=0.25.0
|
|
58
|
-
- os: osx
|
|
59
|
-
osx_image: xcode9
|
|
60
|
-
env:
|
|
61
|
-
- VERSION=0.26.0
|
|
62
|
-
- os: osx
|
|
63
|
-
osx_image: xcode9
|
|
64
|
-
env:
|
|
65
|
-
- VERSION=0.26.1
|
|
66
|
-
- os: osx
|
|
67
|
-
osx_image: xcode9
|
|
68
|
-
env:
|
|
69
|
-
- VERSION=0.27.0
|
|
70
|
-
- os: osx
|
|
71
|
-
osx_image: xcode9
|
|
72
|
-
env:
|
|
73
|
-
- VERSION=0.28.0
|
|
74
|
-
- os: osx
|
|
75
|
-
osx_image: xcode9
|
|
76
|
-
env:
|
|
77
|
-
- VERSION=0.29.0
|
|
78
|
-
- os: osx
|
|
79
|
-
osx_image: xcode9
|
|
80
|
-
env:
|
|
81
|
-
- VERSION=0.30.0
|
|
82
|
-
before_install:
|
|
83
|
-
- OS=linux
|
|
84
|
-
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
|
|
85
|
-
rvm install ruby-2.4.0;
|
|
86
|
-
rvm --default use 2.4.0;
|
|
87
|
-
ruby -v;
|
|
88
|
-
brew update;
|
|
89
|
-
brew install xz;
|
|
90
|
-
OS=darwin;
|
|
91
|
-
fi
|
|
92
|
-
script:
|
|
93
|
-
- npm i
|
|
94
|
-
- mkdir -p ./node_modules/.bin
|
|
95
|
-
- rm -rf ./node_modules/.bin/fibjs
|
|
96
|
-
- curl -SL "https://github.com/fibjs/fibjs/releases/download/v${VERSION}/fibjs-v${VERSION}-${OS}-x64.xz" -o ./node_modules/.bin/fibjs.xz
|
|
97
|
-
- xz -d ./node_modules/.bin/fibjs.xz
|
|
98
|
-
- chmod a+x ./node_modules/.bin/fibjs
|
|
99
|
-
- npm run ci
|
package/appveyor.yml
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
environment:
|
|
2
|
-
nodejs_version: '6'
|
|
3
|
-
matrix:
|
|
4
|
-
- VERSION: 0.21.0
|
|
5
|
-
- VERSION: 0.22.0
|
|
6
|
-
- VERSION: 0.23.0
|
|
7
|
-
- VERSION: 0.24.0
|
|
8
|
-
- VERSION: 0.25.0
|
|
9
|
-
- VERSION: 0.26.0
|
|
10
|
-
- VERSION: 0.26.1
|
|
11
|
-
- VERSION: 0.27.0
|
|
12
|
-
- VERSION: 0.28.0
|
|
13
|
-
- VERSION: 0.29.0
|
|
14
|
-
- VERSION: 0.30.0
|
|
15
|
-
|
|
16
|
-
install:
|
|
17
|
-
- ps: Install-Product node $env:nodejs_version
|
|
18
|
-
- npm i
|
|
19
|
-
- If Not Exist "node_modules\.bin" (mkdir node_modules\.bin)
|
|
20
|
-
- curl -fsSL -o node_modules\.bin\fibjs.cab "https://github.com/fibjs/fibjs/releases/download/v%VERSION%/fibjs-v%VERSION%-windows-x64.cab"
|
|
21
|
-
- expand node_modules\.bin\fibjs.cab node_modules\.bin\fibjs.exe
|
|
22
|
-
|
|
23
|
-
test_script:
|
|
24
|
-
- npm run ci
|
|
25
|
-
|
|
26
|
-
build: off
|
package/lib/index.js
DELETED
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
const coroutine = require("coroutine");
|
|
3
|
-
const util = require("util");
|
|
4
|
-
function ensureError(e) {
|
|
5
|
-
if (typeof e === 'string')
|
|
6
|
-
e = new Error(e);
|
|
7
|
-
return e;
|
|
8
|
-
}
|
|
9
|
-
module.exports = function GetPool(_opt, maxsize, timeout) {
|
|
10
|
-
let opt = _opt;
|
|
11
|
-
if (util.isFunction(_opt)) {
|
|
12
|
-
opt = {
|
|
13
|
-
create: _opt,
|
|
14
|
-
maxsize: maxsize,
|
|
15
|
-
timeout: timeout
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
const create = opt.create;
|
|
19
|
-
const destroy = opt.destroy || ((o) => {
|
|
20
|
-
if (util.isFunction(o.close))
|
|
21
|
-
o.close();
|
|
22
|
-
if (util.isFunction(o.destroy))
|
|
23
|
-
o.destroy();
|
|
24
|
-
if (util.isFunction(o.dispose))
|
|
25
|
-
o.dispose();
|
|
26
|
-
});
|
|
27
|
-
maxsize = opt.maxsize || 10;
|
|
28
|
-
timeout = opt.timeout || 60000;
|
|
29
|
-
let tm = timeout / 10;
|
|
30
|
-
if (tm < 10)
|
|
31
|
-
tm = 10;
|
|
32
|
-
const retry = opt.retry || 1;
|
|
33
|
-
let pools = [];
|
|
34
|
-
const jobs = [];
|
|
35
|
-
let count = 0;
|
|
36
|
-
let running = 0;
|
|
37
|
-
const sem = new coroutine.Semaphore(maxsize);
|
|
38
|
-
let clearTimer;
|
|
39
|
-
function clearPool() {
|
|
40
|
-
let c;
|
|
41
|
-
const d = new Date().getTime();
|
|
42
|
-
while (count) {
|
|
43
|
-
c = pools[0];
|
|
44
|
-
if (d - c.time.getTime() > timeout) {
|
|
45
|
-
pools = pools.slice(1);
|
|
46
|
-
count--;
|
|
47
|
-
if (c.o !== undefined)
|
|
48
|
-
coroutine.start(destroy, c.o);
|
|
49
|
-
}
|
|
50
|
-
else
|
|
51
|
-
break;
|
|
52
|
-
}
|
|
53
|
-
if (!count) {
|
|
54
|
-
if (clearTimer) {
|
|
55
|
-
clearTimer.clear();
|
|
56
|
-
clearTimer = null;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
else if (!clearTimer)
|
|
60
|
-
clearTimer = setInterval(clearPool, tm);
|
|
61
|
-
}
|
|
62
|
-
function putback(name, o, e) {
|
|
63
|
-
e = ensureError(e);
|
|
64
|
-
for (let i = 0; i < jobs.length; i++) {
|
|
65
|
-
const job = jobs[i];
|
|
66
|
-
if (job.name === name) {
|
|
67
|
-
jobs.splice(i, 1);
|
|
68
|
-
job.o = o;
|
|
69
|
-
job.e = e;
|
|
70
|
-
job.ev.set();
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
if (e === undefined)
|
|
75
|
-
pools[count++] = {
|
|
76
|
-
o: o,
|
|
77
|
-
name: name,
|
|
78
|
-
time: new Date()
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
function connect(name) {
|
|
82
|
-
let o;
|
|
83
|
-
let cn = 0;
|
|
84
|
-
let err;
|
|
85
|
-
while (true) {
|
|
86
|
-
try {
|
|
87
|
-
o = create(name);
|
|
88
|
-
break;
|
|
89
|
-
}
|
|
90
|
-
catch (e) {
|
|
91
|
-
if (++cn >= retry) {
|
|
92
|
-
err = e;
|
|
93
|
-
break;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
putback(name, o, err);
|
|
98
|
-
}
|
|
99
|
-
const pool = (name, func) => {
|
|
100
|
-
if (typeof name === 'function') {
|
|
101
|
-
func = name;
|
|
102
|
-
name = "";
|
|
103
|
-
}
|
|
104
|
-
let r;
|
|
105
|
-
let o;
|
|
106
|
-
let p = false;
|
|
107
|
-
clearPool();
|
|
108
|
-
sem.acquire();
|
|
109
|
-
if (count) {
|
|
110
|
-
for (let i = count - 1; i >= 0; i--)
|
|
111
|
-
if (pools[i].name === name) {
|
|
112
|
-
p = true;
|
|
113
|
-
o = pools[i].o;
|
|
114
|
-
pools.splice(i, 1);
|
|
115
|
-
count--;
|
|
116
|
-
break;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
if (!p) {
|
|
120
|
-
coroutine.start(connect, name);
|
|
121
|
-
const job = {
|
|
122
|
-
name: name,
|
|
123
|
-
ev: new coroutine.Event()
|
|
124
|
-
};
|
|
125
|
-
jobs.push(job);
|
|
126
|
-
job.ev.wait();
|
|
127
|
-
if (job.e) {
|
|
128
|
-
sem.post();
|
|
129
|
-
throw ensureError(job.e);
|
|
130
|
-
}
|
|
131
|
-
o = job.o;
|
|
132
|
-
}
|
|
133
|
-
running++;
|
|
134
|
-
try {
|
|
135
|
-
r = func(o);
|
|
136
|
-
putback(name, o);
|
|
137
|
-
}
|
|
138
|
-
catch (e) {
|
|
139
|
-
if (o !== undefined)
|
|
140
|
-
coroutine.start(destroy, o);
|
|
141
|
-
throw ensureError(e);
|
|
142
|
-
}
|
|
143
|
-
finally {
|
|
144
|
-
running--;
|
|
145
|
-
sem.post();
|
|
146
|
-
clearPool();
|
|
147
|
-
}
|
|
148
|
-
return r;
|
|
149
|
-
};
|
|
150
|
-
pool.connections = () => count;
|
|
151
|
-
pool.info = () => ({
|
|
152
|
-
maxsize: maxsize,
|
|
153
|
-
count: count,
|
|
154
|
-
running: running,
|
|
155
|
-
wait: sem.count(),
|
|
156
|
-
timeout: timeout
|
|
157
|
-
});
|
|
158
|
-
pool.clear = () => {
|
|
159
|
-
timeout = -1;
|
|
160
|
-
clearPool();
|
|
161
|
-
};
|
|
162
|
-
return pool;
|
|
163
|
-
};
|
package/typings/index.d.ts
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
declare namespace FibPoolNS {
|
|
2
|
-
type FirstParameter<T> = T extends (arg: infer T, ...args: any[]) => any ? T : never;
|
|
3
|
-
interface DippedItemStruct {
|
|
4
|
-
close?: FibPoolOptionResult['destroy'];
|
|
5
|
-
destroy?: FibPoolOptionResult['destroy'];
|
|
6
|
-
dispose?: FibPoolOptionResult['destroy'];
|
|
7
|
-
}
|
|
8
|
-
interface FibPoolOptionArgs<T extends DippedItemStruct = {}> {
|
|
9
|
-
create: (name: string) => T;
|
|
10
|
-
destroy?: (item?: T) => any;
|
|
11
|
-
maxsize?: number;
|
|
12
|
-
timeout?: number;
|
|
13
|
-
retry?: number | boolean;
|
|
14
|
-
strict?: boolean;
|
|
15
|
-
}
|
|
16
|
-
interface FibPoolOptionResult<T extends DippedItemStruct = {}> {
|
|
17
|
-
create: FibPoolOptionArgs<T>['create'];
|
|
18
|
-
destroy: FibPoolOptionArgs<T>['destroy'];
|
|
19
|
-
maxsize: FibPoolOptionArgs<T>['maxsize'];
|
|
20
|
-
timeout: FibPoolOptionArgs<T>['timeout'];
|
|
21
|
-
retry: FibPoolOptionArgs<T>['retry'];
|
|
22
|
-
strict: FibPoolOptionArgs<T>['strict'];
|
|
23
|
-
}
|
|
24
|
-
interface FibPoolDipperFn<T_CREATED_ITEM, T_POOL_FUNC_RETURN = any> {
|
|
25
|
-
(o: T_CREATED_ITEM): T_POOL_FUNC_RETURN;
|
|
26
|
-
}
|
|
27
|
-
type FibPool<T_CREATED_ITEM, T_POOL_FUNC_RETURN = any> = {
|
|
28
|
-
<T_RETURN = T_POOL_FUNC_RETURN>(name: string, func: FibPoolDipperFn<T_CREATED_ITEM, T_RETURN>): T_RETURN;
|
|
29
|
-
<T_RETURN = T_POOL_FUNC_RETURN>(func: FibPoolDipperFn<T_CREATED_ITEM, T_RETURN>): T_RETURN;
|
|
30
|
-
} & {
|
|
31
|
-
connections?(): number;
|
|
32
|
-
info?(): {
|
|
33
|
-
maxsize: number;
|
|
34
|
-
count: number;
|
|
35
|
-
running: number;
|
|
36
|
-
wait: number;
|
|
37
|
-
timeout: number;
|
|
38
|
-
};
|
|
39
|
-
clear?(): void;
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
declare const _default: <T_CREATED_ITEM extends FibPoolNS.DippedItemStruct, T_POOL_FUNC_RETURN>(_opt: FibPoolNS.FibPoolOptionArgs<T_CREATED_ITEM> | ((name: string) => T_CREATED_ITEM), maxsize?: number, timeout?: number) => FibPoolNS.FibPool<T_CREATED_ITEM, T_POOL_FUNC_RETURN>;
|
|
43
|
-
export = _default;
|