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 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,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
- When strict mode is enabled, it also prevents:
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.6.2",
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": "tsc --noEmit",
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
- type FirstParameter<T> = T extends (arg: infer T, ...args: any[]) => any ? T : never;
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?: FibPoolOptionResult['destroy'];
9
- destroy?: FibPoolOptionResult['destroy'];
10
- dispose?: FibPoolOptionResult['destroy'];
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 | boolean
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?(): number;
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?(): void;
40
+ };
41
+ clear(): void;
47
42
  }
48
43
  }
49
44
 
50
- function ensureError (e: string | Error) {
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
- e = new Error(e)
53
-
54
- return e
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 = <FibPoolNS.FibPoolOptionResult<T_CREATED_ITEM>>_opt;
158
+ let opt: FibPoolNS.FibPoolOptionArgs<T_CREATED_ITEM>;
159
+
67
160
  if (util.isFunction(_opt)) {
68
- opt = <FibPoolNS.FibPoolOptionResult<T_CREATED_ITEM>>{
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
- close?: FibPoolNS.FibPoolOptionResult<T_CREATED_ITEM>['destroy'];
78
- destroy?: FibPoolNS.FibPoolOptionResult<T_CREATED_ITEM>['destroy'];
79
- dispose?: FibPoolNS.FibPoolOptionResult<T_CREATED_ITEM>['destroy'];
80
- }) => {
81
- if (util.isFunction(o.close)) o.close();
82
- if (util.isFunction(o.destroy)) o.destroy();
83
- if (util.isFunction(o.dispose)) o.dispose();
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
- maxsize = opt.maxsize || 10;
87
- timeout = opt.timeout || 60000;
88
- let tm = timeout / 10;
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 || 1;
93
- const _strict = opt.strict === undefined ? true : opt.strict;
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(maxsize);
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() > timeout) {
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
- clearTimer = setInterval(clearPool, tm);
139
- if (clearTimer && typeof clearTimer.unref === 'function')
140
- clearTimer.unref();
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: typeof jobs[any]['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 cn = 0;
172
- let err: Error;
284
+ let o: T_CREATED_ITEM | undefined;
285
+ let err: Error | undefined;
173
286
 
174
- while (true) {
287
+ for (let cn = 1; ; cn++) {
175
288
  try {
176
289
  o = create(name);
177
290
  break;
178
291
  } catch (e) {
179
- if (++cn >= (retry as number)) {
180
- err = e;
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
- const pool: FibPoolNS.FibPool<T_CREATED_ITEM, T_POOL_FUNC_RETURN> = (
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 as any;
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: typeof jobs[any]['o'];
200
- let p = false;
338
+ let o: T_CREATED_ITEM | undefined;
339
+ let reused = false;
340
+ const fiber = enterFiber();
201
341
 
202
- clearPool();
203
- sem.acquire();
204
-
205
- if (count) {
206
- for (let i = count - 1; i >= 0; i--)
207
- if (pools[i].name === name) {
208
- p = true
209
- o = pools[i].o;
210
- pools.splice(i, 1);
211
- count--;
212
- break;
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
- if (!p) {
217
- coroutine.start(connect, name);
357
+ if (!reused) {
358
+ coroutine.start(connect, name);
218
359
 
219
- const job = <typeof jobs[any]>{
220
- name: name,
221
- ev: new coroutine.Event()
222
- };
223
- jobs.push(job);
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
- job.ev.wait();
226
- if (job.e) {
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
- throw ensureError(job.e);
391
+ clearPool();
229
392
  }
230
- o = job.o;
231
- }
232
393
 
233
- running++;
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
- running--;
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: maxsize,
403
+ maxsize: maxsizeValue,
275
404
  count: count,
276
405
  running: running,
277
406
  wait: sem.count(),
278
- timeout: timeout
407
+ timeout: timeoutValue
279
408
  })
280
409
 
281
410
  pool.clear = () => {
282
- timeout = -1;
411
+ timeoutValue = -1;
283
412
  clearPool();
284
413
  }
285
414
 
package/tsconfig.json CHANGED
@@ -1,17 +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
- "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
- };
@@ -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;