fib-pool 1.6.1 → 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 CHANGED
@@ -1,8 +1,6 @@
1
1
  ## Generic resource pooling for fibjs
2
2
 
3
3
  [![NPM version](https://img.shields.io/npm/v/fib-pool.svg)](https://www.npmjs.org/package/fib-pool)
4
- [![Build Status](https://travis-ci.org/fibjs/fib-pool.svg)](https://travis-ci.org/fibjs/fib-pool)
5
- [![Build status](https://ci.appveyor.com/api/projects/status/p662f7ulpc4asu8s?svg=true)](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 run ci
14
+ npm test # fibjs test
15
+ npm run typecheck # fibjs --check
17
16
  ```
18
17
 
19
18
  ## Creating a pool
@@ -103,6 +102,90 @@ 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
+
146
+ ## Strict mode (default: enabled)
147
+
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.
149
+
150
+ ```js
151
+ var Pool = require("fib-pool");
152
+
153
+ var p = Pool({
154
+ create: () => {
155
+ return { value: 1, getValue: function() { return this.value; } };
156
+ }
157
+ });
158
+
159
+ var leaked;
160
+ p((obj) => {
161
+ leaked = obj;
162
+ // This works inside the pool callback
163
+ console.log(obj.getValue()); // OK
164
+ });
165
+
166
+ // This will throw an error: "access object outside of pool scope"
167
+ leaked.getValue(); // Error!
168
+ ```
169
+
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
+
174
+ - Setting properties on pooled objects
175
+ - Calling `close()`, `destroy()`, or `dispose()` methods directly
176
+ - Extracting a method and calling it after the callback returned
177
+
178
+ To disable strict mode:
179
+
180
+ ```js
181
+ var p = Pool({
182
+ create: () => {
183
+ return { value: 1 };
184
+ },
185
+ strict: false
186
+ });
187
+ ```
188
+
106
189
  ## Clear a pool
107
190
 
108
191
  Simple example.
package/package.json CHANGED
@@ -1,17 +1,11 @@
1
1
  {
2
2
  "name": "fib-pool",
3
- "version": "1.6.1",
3
+ "version": "1.7.0",
4
4
  "description": "Generic resource pooling for fibjs",
5
- "main": "./lib",
6
- "types": "typings/index.d.ts",
5
+ "main": "./src/index.ts",
6
+ "type": "module",
7
7
  "repository": "git://github.com/fibjs/fib-pool.git",
8
8
  "author": "Liu Hu <lion@9465.net>",
9
- "contributors": [
10
- {
11
- "name": "Richard",
12
- "email": "richardo2016@gmail.com"
13
- }
14
- ],
15
9
  "homepage": "https://github.com/fibjs/fib-pool",
16
10
  "license": "MIT",
17
11
  "keywords": [
@@ -20,30 +14,7 @@
20
14
  "fibjs"
21
15
  ],
22
16
  "scripts": {
23
- "build": "ftsc ./src/* --outDir lib",
24
17
  "test": "fibjs test",
25
- "ci": "npm run build && npm run test",
26
- "prepublishOnly": "npm run build"
27
- },
28
- "devDependencies": {
29
- "@fibjs/ci": "^2.3.0",
30
- "@fibjs/types": "^0.27.1",
31
- "fib-typify": "^0.8.4"
32
- },
33
- "ci": {
34
- "type": "travis, appveyor",
35
- "version": [
36
- "0.21.0",
37
- "0.22.0",
38
- "0.23.0",
39
- "0.24.0",
40
- "0.25.0",
41
- "0.26.0",
42
- "0.26.1",
43
- "0.27.0",
44
- "0.28.0",
45
- "0.29.0",
46
- "0.30.0"
47
- ]
18
+ "typecheck": "fibjs --check"
48
19
  }
49
20
  }
package/src/index.ts ADDED
@@ -0,0 +1,416 @@
1
+ import * as coroutine from 'coroutine';
2
+ import * as util from 'util';
3
+
4
+ declare namespace FibPoolNS {
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
+ */
10
+ interface DippedItemStruct {
11
+ close?: unknown;
12
+ destroy?: unknown;
13
+ dispose?: unknown;
14
+ }
15
+
16
+ interface FibPoolOptionArgs<T extends DippedItemStruct = {}> {
17
+ create: (name: string) => T;
18
+ destroy?: (item?: T) => any;
19
+ maxsize?: number;
20
+ timeout?: number;
21
+ retry?: number;
22
+ strict?: boolean;
23
+ }
24
+
25
+ interface FibPoolDipperFn<T_CREATED_ITEM, T_POOL_FUNC_RETURN = any> {
26
+ (o: T_CREATED_ITEM): T_POOL_FUNC_RETURN
27
+ }
28
+
29
+ type FibPool<T_CREATED_ITEM, T_POOL_FUNC_RETURN = any> = {
30
+ <T_RETURN = T_POOL_FUNC_RETURN>(name: string, func: FibPoolDipperFn<T_CREATED_ITEM, T_RETURN>): T_RETURN
31
+ <T_RETURN = T_POOL_FUNC_RETURN>(func: FibPoolDipperFn<T_CREATED_ITEM, T_RETURN>): T_RETURN
32
+ } & {
33
+ connections(): number;
34
+ info(): {
35
+ maxsize: number;
36
+ count: number;
37
+ running: number;
38
+ wait: number;
39
+ timeout: number;
40
+ };
41
+ clear(): void;
42
+ }
43
+ }
44
+
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
+
84
+ if (typeof e === 'string')
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;
147
+ }
148
+
149
+ export default function GetPool<
150
+ T_CREATED_ITEM extends FibPoolNS.DippedItemStruct,
151
+ T_POOL_FUNC_RETURN
152
+ > (
153
+ _opt: FibPoolNS.FibPoolOptionArgs<T_CREATED_ITEM> | FibPoolNS.FibPoolOptionArgs<T_CREATED_ITEM>['create'],
154
+ maxsize?: number,
155
+ timeout?: number
156
+ ): FibPoolNS.FibPool<T_CREATED_ITEM, T_POOL_FUNC_RETURN> {
157
+ /* clean input arguments :start */
158
+ let opt: FibPoolNS.FibPoolOptionArgs<T_CREATED_ITEM>;
159
+
160
+ if (util.isFunction(_opt)) {
161
+ opt = {
162
+ create: _opt as (name: string) => T_CREATED_ITEM,
163
+ maxsize: maxsize,
164
+ timeout: timeout
165
+ };
166
+ } else {
167
+ opt = _opt as FibPoolNS.FibPoolOptionArgs<T_CREATED_ITEM>;
168
+ }
169
+
170
+ const create = opt.create;
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);
180
+ });
181
+
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;
188
+ if (tm < 10)
189
+ tm = 10;
190
+
191
+ const retry = opt.retry === undefined ? 1 : toPositiveInteger(opt.retry, 'retry');
192
+ const strictMode = opt.strict === undefined ? true : opt.strict;
193
+ /* clean input arguments :end */
194
+
195
+ let pools = <{
196
+ o: T_CREATED_ITEM;
197
+ name: string;
198
+ time: Date;
199
+ }[]>[];
200
+
201
+ const jobs = <{
202
+ name: string;
203
+ ev: Class_Event;
204
+ o?: T_CREATED_ITEM;
205
+ e?: Error;
206
+ }[]>[];
207
+
208
+ let count = 0;
209
+ let running = 0;
210
+
211
+ const sem = new coroutine.Semaphore(maxsizeValue);
212
+ let clearTimer: Class_Timer | null = null;
213
+
214
+ function clearPool() {
215
+ const d = new Date().getTime();
216
+
217
+ while (count) {
218
+ const c = pools[0];
219
+
220
+ if (d - c.time.getTime() > timeoutValue) {
221
+ pools = pools.slice(1);
222
+ count--;
223
+
224
+ if (c.o !== undefined)
225
+ coroutine.start(destroy, c.o);
226
+ } else
227
+ break;
228
+ }
229
+
230
+ if (!count) {
231
+ if (clearTimer) {
232
+ clearTimer.clear();
233
+ clearTimer = null;
234
+ }
235
+ } else if (!clearTimer) {
236
+ /* the timer must not hold the process alive: the pool is idle
237
+ between uses */
238
+ clearTimer = setInterval(clearPool, tm).unref();
239
+ }
240
+ }
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
+ */
247
+ function putback(
248
+ name: string,
249
+ o: T_CREATED_ITEM | undefined,
250
+ e?: Error
251
+ ) {
252
+ for (let i = 0; i < jobs.length; i++) {
253
+ const job = jobs[i];
254
+ if (job.name === name) {
255
+ jobs.splice(i, 1);
256
+ job.o = o;
257
+ job.e = e;
258
+ job.ev.set();
259
+ return;
260
+ }
261
+ }
262
+
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
+
275
+ pools[count++] = {
276
+ o: o as T_CREATED_ITEM,
277
+ name: name,
278
+ time: new Date()
279
+ };
280
+ }
281
+ }
282
+
283
+ function connect(name: string) {
284
+ let o: T_CREATED_ITEM | undefined;
285
+ let err: Error | undefined;
286
+
287
+ for (let cn = 1; ; cn++) {
288
+ try {
289
+ o = create(name);
290
+ break;
291
+ } catch (e) {
292
+ /* any thrown value is a failure, including 0 or '' */
293
+ if (cn >= retry) {
294
+ err = toError(e);
295
+ break;
296
+ }
297
+ }
298
+ }
299
+
300
+ putback(name, o, err);
301
+ }
302
+
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 = ((
327
+ name: string | FibPoolNS.FibPoolDipperFn<T_CREATED_ITEM, T_POOL_FUNC_RETURN>,
328
+ func?: FibPoolNS.FibPoolDipperFn<T_CREATED_ITEM, T_POOL_FUNC_RETURN>
329
+ ) => {
330
+ if (typeof name === 'function') {
331
+ func = name;
332
+ name = "";
333
+ }
334
+
335
+ const dip = func as FibPoolNS.FibPoolDipperFn<T_CREATED_ITEM, T_POOL_FUNC_RETURN>;
336
+
337
+ let r: T_POOL_FUNC_RETURN;
338
+ let o: T_CREATED_ITEM | undefined;
339
+ let reused = false;
340
+ const fiber = enterFiber();
341
+
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
+ }
356
+
357
+ if (!reused) {
358
+ coroutine.start(connect, name);
359
+
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;
379
+
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--;
390
+ sem.post();
391
+ clearPool();
392
+ }
393
+
394
+ return r;
395
+ } finally {
396
+ leaveFiber(fiber);
397
+ }
398
+ }) as FibPoolNS.FibPool<T_CREATED_ITEM, T_POOL_FUNC_RETURN>;
399
+
400
+ pool.connections = () => count
401
+
402
+ pool.info = () => ({
403
+ maxsize: maxsizeValue,
404
+ count: count,
405
+ running: running,
406
+ wait: sem.count(),
407
+ timeout: timeoutValue
408
+ })
409
+
410
+ pool.clear = () => {
411
+ timeoutValue = -1;
412
+ clearPool();
413
+ }
414
+
415
+ return pool;
416
+ }
package/tsconfig.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "target": "es6",
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
- "alwaysStrict": false,
6
- "removeComments": true,
7
- "declaration": true,
8
- "declarationDir": "typings",
9
- "types": [
10
- "@fibjs/types"
11
- ]
12
- }
13
- }
8
+ "noEmit": true
9
+ },
10
+ "exclude": [
11
+ "node_modules"
12
+ ]
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,164 +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
- console.log(e);
92
- if (++cn >= retry) {
93
- err = e;
94
- break;
95
- }
96
- }
97
- }
98
- putback(name, o, err);
99
- }
100
- const pool = (name, func) => {
101
- if (typeof name === 'function') {
102
- func = name;
103
- name = "";
104
- }
105
- let r;
106
- let o;
107
- let p = false;
108
- clearPool();
109
- sem.acquire();
110
- if (count) {
111
- for (let i = count - 1; i >= 0; i--)
112
- if (pools[i].name === name) {
113
- p = true;
114
- o = pools[i].o;
115
- pools.splice(i, 1);
116
- count--;
117
- break;
118
- }
119
- }
120
- if (!p) {
121
- coroutine.start(connect, name);
122
- const job = {
123
- name: name,
124
- ev: new coroutine.Event()
125
- };
126
- jobs.push(job);
127
- job.ev.wait();
128
- if (job.e) {
129
- sem.post();
130
- throw ensureError(job.e);
131
- }
132
- o = job.o;
133
- }
134
- running++;
135
- try {
136
- r = func(o);
137
- putback(name, o);
138
- }
139
- catch (e) {
140
- if (o !== undefined)
141
- coroutine.start(destroy, o);
142
- throw ensureError(e);
143
- }
144
- finally {
145
- running--;
146
- sem.post();
147
- clearPool();
148
- }
149
- return r;
150
- };
151
- pool.connections = () => count;
152
- pool.info = () => ({
153
- maxsize: maxsize,
154
- count: count,
155
- running: running,
156
- wait: sem.count(),
157
- timeout: timeout
158
- });
159
- pool.clear = () => {
160
- timeout = -1;
161
- clearPool();
162
- };
163
- return pool;
164
- };
@@ -1,41 +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
- }
15
- interface FibPoolOptionResult<T extends DippedItemStruct = {}> {
16
- create: FibPoolOptionArgs<T>['create'];
17
- destroy: FibPoolOptionArgs<T>['destroy'];
18
- maxsize: FibPoolOptionArgs<T>['maxsize'];
19
- timeout: FibPoolOptionArgs<T>['timeout'];
20
- retry: FibPoolOptionArgs<T>['retry'];
21
- }
22
- interface FibPoolDipperFn<T_CREATED_ITEM, T_POOL_FUNC_RETURN = any> {
23
- (o: T_CREATED_ITEM): T_POOL_FUNC_RETURN;
24
- }
25
- type FibPool<T_CREATED_ITEM, T_POOL_FUNC_RETURN = any> = {
26
- <T_RETURN = T_POOL_FUNC_RETURN>(name: string, func: FibPoolDipperFn<T_CREATED_ITEM, T_RETURN>): T_RETURN;
27
- <T_RETURN = T_POOL_FUNC_RETURN>(func: FibPoolDipperFn<T_CREATED_ITEM, T_RETURN>): T_RETURN;
28
- } & {
29
- connections?(): number;
30
- info?(): {
31
- maxsize: number;
32
- count: number;
33
- running: number;
34
- wait: number;
35
- timeout: number;
36
- };
37
- clear?(): void;
38
- };
39
- }
40
- 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>;
41
- export = _default;