fib-pool 1.6.2 → 1.7.1

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, including test/types.ts
17
16
  ```
18
17
 
19
18
  ## Creating a pool
@@ -103,6 +102,60 @@ 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
+ The pooled item is unconstrained: `create` may return an object, a class
117
+ instance, a function, or a primitive. The default `destroy` calls whichever of
118
+ `close` / `destroy` / `dispose` the item provides and ignores items that have
119
+ none of them.
120
+
121
+ ```js
122
+ var p = Pool(() => 10); // a primitive item
123
+ p((v) => v + 1);
124
+
125
+ var q = Pool(() => ({ value: 1 })); // a plain object, nothing to destroy
126
+ q((o) => o.value);
127
+ ```
128
+
129
+ Invalid `maxsize`, `timeout` and `retry` values throw a `TypeError` when the
130
+ pool is created rather than being silently coerced:
131
+
132
+ ```js
133
+ Pool({ create: () => ({}), maxsize: 0 }); // TypeError: maxsize must be a positive integer, received 0
134
+ Pool({ create: () => ({}), timeout: -5 }); // TypeError: timeout must be a positive number, received -5
135
+ Pool({ create: () => ({}), retry: true }); // TypeError: retry must be a positive integer, received true
136
+ ```
137
+
138
+ The idle list is capped by `maxsize` as well: a pool used with more names than
139
+ that evicts the least recently used item (and destroys it) instead of keeping
140
+ one idle item per name.
141
+
142
+ ## Reentrancy
143
+
144
+ A pool cannot be acquired twice from the same fiber. The inner call would wait
145
+ for a permit that the outer call is holding, which never arrives, so it is
146
+ rejected instead of deadlocking:
147
+
148
+ ```js
149
+ var p = Pool({ create: () => ({}), maxsize: 1 });
150
+
151
+ p(() => {
152
+ p(() => {}); // Error: Cannot pool again from the same fiber
153
+ });
154
+ ```
155
+
156
+ Nesting different pools, or acquiring the same pool from another fiber, works as
157
+ expected.
158
+
106
159
  ## Strict mode (default: enabled)
107
160
 
108
161
  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 +180,13 @@ p((obj) => {
127
180
  leaked.getValue(); // Error!
128
181
  ```
129
182
 
130
- When strict mode is enabled, it also prevents:
183
+ The guarded object behaves like the item for reading and inspection —
184
+ `key in obj`, `Object.keys(obj)`, `for...in`, `JSON.stringify(obj)` and
185
+ `util.inspect(obj)` all report the item's own members — while the guards remain:
186
+
131
187
  - Setting properties on pooled objects
132
188
  - Calling `close()`, `destroy()`, or `dispose()` methods directly
189
+ - Extracting a method and calling it after the callback returned
133
190
 
134
191
  To disable strict mode:
135
192
 
package/package.json CHANGED
@@ -1,18 +1,24 @@
1
1
  {
2
2
  "name": "fib-pool",
3
- "version": "1.6.2",
3
+ "version": "1.7.1",
4
4
  "description": "Generic resource pooling for fibjs",
5
- "main": "./src",
6
- "types": "typings/index.d.ts",
5
+ "main": "./src/index.cts",
6
+ "types": "./src/index.ts",
7
7
  "type": "module",
8
+ "exports": {
9
+ ".": {
10
+ "import": {
11
+ "types": "./src/index.ts",
12
+ "default": "./src/index.ts"
13
+ },
14
+ "require": {
15
+ "types": "./src/index.d.cts",
16
+ "default": "./src/index.cts"
17
+ }
18
+ }
19
+ },
8
20
  "repository": "git://github.com/fibjs/fib-pool.git",
9
21
  "author": "Liu Hu <lion@9465.net>",
10
- "contributors": [
11
- {
12
- "name": "Richard",
13
- "email": "richardo2016@gmail.com"
14
- }
15
- ],
16
22
  "homepage": "https://github.com/fibjs/fib-pool",
17
23
  "license": "MIT",
18
24
  "keywords": [
@@ -22,29 +28,6 @@
22
28
  ],
23
29
  "scripts": {
24
30
  "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
- ]
31
+ "typecheck": "fibjs --check"
49
32
  }
50
33
  }
package/src/index.cts ADDED
@@ -0,0 +1,18 @@
1
+ /**
2
+ * CommonJS entry point.
3
+ *
4
+ * The pool is implemented in `index.ts` as an ES module, so requiring that
5
+ * file yields the module namespace object (`{ default, __esModule }`) rather
6
+ * than the factory. Callers of `require('fib-pool')` expect the export to *be*
7
+ * the factory function, so this bridge unwraps the default export.
8
+ *
9
+ * A static `import` is not an option here: this file is CommonJS, so it must
10
+ * load the implementation through `require` to get the namespace object.
11
+ *
12
+ * `module.exports =` is used instead of `export =` because fibjs runs
13
+ * TypeScript in strip-only mode, which rejects export assignments. The type of
14
+ * this file is described by the sibling `index.d.cts`.
15
+ */
16
+ const impl = require('./index.ts');
17
+
18
+ module.exports = impl.default;
@@ -0,0 +1,3 @@
1
+ import Pool from './index.ts';
2
+
3
+ export = Pool;
package/src/index.ts CHANGED
@@ -2,29 +2,36 @@ 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 the default destroy looks for. All three members are optional:
7
+ * it calls whichever of them the item actually provides, and a non-function
8
+ * value (a plain property) is ignored.
9
+ *
10
+ * This is deliberately *not* a constraint on the pooled item. Every member
11
+ * is optional, which makes it a weak type, and TypeScript rejects any type
12
+ * without a property in common with a weak type. Constraining the item to
13
+ * it would reject the perfectly valid `Pool(() => 10)` and
14
+ * `Pool(() => ({ value: 1 }))`, so the constraint is absent and the cast to
15
+ * this shape happens only inside the default destroy, where a primitive
16
+ * simply has no such member to call.
17
+ */
7
18
  interface DippedItemStruct {
8
- close?: FibPoolOptionResult['destroy'];
9
- destroy?: FibPoolOptionResult['destroy'];
10
- dispose?: FibPoolOptionResult['destroy'];
19
+ close?: unknown;
20
+ destroy?: unknown;
21
+ dispose?: unknown;
11
22
  }
12
23
 
13
- interface FibPoolOptionArgs<T extends DippedItemStruct = {}> {
14
- create: (name: string) => T
24
+ /**
25
+ * `T` is the pooled item and may be anything `create` returns, including a
26
+ * primitive. It is only inferred, never constrained.
27
+ */
28
+ interface FibPoolOptionArgs<T = any> {
29
+ create: (name: string) => T;
15
30
  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']
31
+ maxsize?: number;
32
+ timeout?: number;
33
+ retry?: number;
34
+ strict?: boolean;
28
35
  }
29
36
 
30
37
  interface FibPoolDipperFn<T_CREATED_ITEM, T_POOL_FUNC_RETURN = any> {
@@ -35,62 +42,171 @@ declare namespace FibPoolNS {
35
42
  <T_RETURN = T_POOL_FUNC_RETURN>(name: string, func: FibPoolDipperFn<T_CREATED_ITEM, T_RETURN>): T_RETURN
36
43
  <T_RETURN = T_POOL_FUNC_RETURN>(func: FibPoolDipperFn<T_CREATED_ITEM, T_RETURN>): T_RETURN
37
44
  } & {
38
- connections?(): number;
39
- info?(): {
45
+ connections(): number;
46
+ info(): {
40
47
  maxsize: number;
41
48
  count: number;
42
49
  running: number;
43
50
  wait: number;
44
51
  timeout: number;
45
- };
46
- clear?(): void;
52
+ };
53
+ clear(): void;
47
54
  }
48
55
  }
49
56
 
50
- function ensureError (e: string | Error) {
57
+ /**
58
+ * Calls the value when it is a function and ignores it otherwise: a pooled item
59
+ * may expose a plain property named close/destroy/dispose.
60
+ */
61
+ function callIfFunction(v: unknown) {
62
+ if (util.isFunction(v))
63
+ (v as (...args: any[]) => any)();
64
+ }
65
+
66
+ /**
67
+ * `maxsize` and `retry` are counts, `timeout` is a duration. Both are rejected
68
+ * up front instead of being silently coerced into a default or handed to the
69
+ * semaphore, which would fail far from the option that is wrong.
70
+ */
71
+ function toPositiveInteger(value: unknown, name: string): number {
72
+ if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0 || Math.floor(value) !== value)
73
+ throw new TypeError(`${name} must be a positive integer, received ${String(value)}`);
74
+
75
+ return value;
76
+ }
77
+
78
+ function toPositiveNumber(value: unknown, name: string): number {
79
+ if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0)
80
+ throw new TypeError(`${name} must be a positive number, received ${String(value)}`);
81
+
82
+ return value;
83
+ }
84
+
85
+ /**
86
+ * The pool always hands out an Error: `create` or the callback may throw a
87
+ * string, a number, or nothing at all, and the caller expects to be able to
88
+ * read `.message`. A thrown falsy value (0, '', false, null, undefined) is still
89
+ * a failure and must not be mistaken for a successful create, so this returns an
90
+ * Error for every input.
91
+ */
92
+ function toError(e: unknown): Error {
93
+ if (e instanceof Error)
94
+ return e;
95
+
51
96
  if (typeof e === 'string')
52
- e = new Error(e)
53
-
54
- return e
97
+ return new Error(e);
98
+
99
+ return new Error(String(e));
100
+ }
101
+
102
+ /**
103
+ * Guards a pooled item while the callback runs. The item is read through a
104
+ * getter rather than captured by value: once it goes back to the pool the
105
+ * handle must stop working, which is what the "outside of pool scope" error
106
+ * reports.
107
+ *
108
+ * The reflection traps matter as much as the getter: without them `key in obj`,
109
+ * `Object.keys`, `for...in` and `JSON.stringify` all see the empty proxy target
110
+ * instead of the item.
111
+ */
112
+ function wrapStrict<T extends object>(item: () => T | undefined): T {
113
+ const current = () => {
114
+ const o = item();
115
+ if (o === undefined)
116
+ throw new Error("access object outside of pool scope");
117
+
118
+ return o;
119
+ };
120
+
121
+ return new Proxy({}, {
122
+ get(target, prop) {
123
+ if (prop === 'close' || prop === 'destroy' || prop === 'dispose')
124
+ throw new Error("Cannot close/destroy/dispose");
125
+
126
+ const o = current();
127
+ const value = (o as any)[prop];
128
+ if (typeof value === 'function') {
129
+ /* the item is resolved again when the method runs: a method
130
+ extracted inside the callback and called after it returned
131
+ must not keep the item alive outside its scope */
132
+ return function () {
133
+ const self = current();
134
+ return (self as any)[prop].apply(self, arguments);
135
+ }
136
+ }
137
+ return value;
138
+ },
139
+ has(target, prop) {
140
+ /* no close/destroy/dispose refusal here: the key is reported like
141
+ any other (ownKeys lists it too), only reading or calling those
142
+ members is refused, in `get` */
143
+ return Reflect.has(current() as object, prop);
144
+ },
145
+ ownKeys() {
146
+ return Reflect.ownKeys(current() as object);
147
+ },
148
+ getOwnPropertyDescriptor(target, prop) {
149
+ const d = Reflect.getOwnPropertyDescriptor(current() as object, prop);
150
+
151
+ /* every key of the proxy target is configurable: a descriptor that
152
+ claims otherwise would break the proxy invariants */
153
+ return d && Object.assign(d, { configurable: true });
154
+ },
155
+ set() {
156
+ throw new Error("Cannot set property");
157
+ }
158
+ }) as T;
55
159
  }
56
160
 
57
161
  export default function GetPool<
58
- T_CREATED_ITEM extends FibPoolNS.DippedItemStruct,
59
- T_POOL_FUNC_RETURN
162
+ T_CREATED_ITEM,
163
+ T_POOL_FUNC_RETURN = any
60
164
  > (
61
165
  _opt: FibPoolNS.FibPoolOptionArgs<T_CREATED_ITEM> | FibPoolNS.FibPoolOptionArgs<T_CREATED_ITEM>['create'],
62
166
  maxsize?: number,
63
167
  timeout?: number
64
168
  ): FibPoolNS.FibPool<T_CREATED_ITEM, T_POOL_FUNC_RETURN> {
65
169
  /* clean input arguments :start */
66
- let opt = <FibPoolNS.FibPoolOptionResult<T_CREATED_ITEM>>_opt;
170
+ let opt: FibPoolNS.FibPoolOptionArgs<T_CREATED_ITEM>;
171
+
67
172
  if (util.isFunction(_opt)) {
68
- opt = <FibPoolNS.FibPoolOptionResult<T_CREATED_ITEM>>{
69
- create: _opt,
173
+ opt = {
174
+ create: _opt as (name: string) => T_CREATED_ITEM,
70
175
  maxsize: maxsize,
71
176
  timeout: timeout
72
177
  };
178
+ } else {
179
+ opt = _opt as FibPoolNS.FibPoolOptionArgs<T_CREATED_ITEM>;
73
180
  }
74
181
 
75
182
  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();
183
+ const destroy = opt.destroy || ((o: unknown) => {
184
+ /* create may return null/undefined or any primitive: there is nothing
185
+ to destroy then. Functions are kept, since they can carry members. */
186
+ if (o === null || o === undefined)
187
+ return;
188
+
189
+ if (typeof o !== 'object' && typeof o !== 'function')
190
+ return;
191
+
192
+ const item = o as FibPoolNS.DippedItemStruct;
193
+
194
+ callIfFunction(item.close);
195
+ callIfFunction(item.destroy);
196
+ callIfFunction(item.dispose);
84
197
  });
85
198
 
86
- maxsize = opt.maxsize || 10;
87
- timeout = opt.timeout || 60000;
88
- let tm = timeout / 10;
199
+ const maxsizeValue = opt.maxsize === undefined ? 10 : toPositiveInteger(opt.maxsize, 'maxsize');
200
+ let timeoutValue = opt.timeout === undefined ? 60000 : toPositiveNumber(opt.timeout, 'timeout');
201
+
202
+ /* the idle scan runs at a tenth of the timeout, but never more often than
203
+ every 10ms */
204
+ let tm = timeoutValue / 10;
89
205
  if (tm < 10)
90
206
  tm = 10;
91
207
 
92
- const retry = opt.retry || 1;
93
- const _strict = opt.strict === undefined ? true : opt.strict;
208
+ const retry = opt.retry === undefined ? 1 : toPositiveInteger(opt.retry, 'retry');
209
+ const strictMode = opt.strict === undefined ? true : opt.strict;
94
210
  /* clean input arguments :end */
95
211
 
96
212
  let pools = <{
@@ -109,17 +225,16 @@ export default function GetPool<
109
225
  let count = 0;
110
226
  let running = 0;
111
227
 
112
- const sem = new coroutine.Semaphore(maxsize);
113
- let clearTimer: Class_Timer;
228
+ const sem = new coroutine.Semaphore(maxsizeValue);
229
+ let clearTimer: Class_Timer | null = null;
114
230
 
115
231
  function clearPool() {
116
- let c: typeof pools[any];
117
232
  const d = new Date().getTime();
118
233
 
119
234
  while (count) {
120
- c = pools[0];
235
+ const c = pools[0];
121
236
 
122
- if (d - c.time.getTime() > timeout) {
237
+ if (d - c.time.getTime() > timeoutValue) {
123
238
  pools = pools.slice(1);
124
239
  count--;
125
240
 
@@ -135,18 +250,22 @@ export default function GetPool<
135
250
  clearTimer = null;
136
251
  }
137
252
  } else if (!clearTimer) {
138
- clearTimer = setInterval(clearPool, tm);
139
- if (clearTimer && typeof clearTimer.unref === 'function')
140
- clearTimer.unref();
253
+ /* the timer must not hold the process alive: the pool is idle
254
+ between uses */
255
+ clearTimer = setInterval(clearPool, tm).unref();
141
256
  }
142
257
  }
143
258
 
259
+ /**
260
+ * Hand the item back: wake the first waiter bound to `name`, or park the
261
+ * item in the idle list. An errored item is never parked, but a successful
262
+ * create may legitimately produce `undefined` and is parked as is.
263
+ */
144
264
  function putback(
145
- name: typeof jobs[any]['name'],
146
- o: T_CREATED_ITEM,
265
+ name: string,
266
+ o: T_CREATED_ITEM | undefined,
147
267
  e?: Error
148
268
  ) {
149
- e = ensureError(e);
150
269
  for (let i = 0; i < jobs.length; i++) {
151
270
  const job = jobs[i];
152
271
  if (job.name === name) {
@@ -158,26 +277,38 @@ export default function GetPool<
158
277
  }
159
278
  }
160
279
 
161
- if (e === undefined)
280
+ if (e === undefined) {
281
+ /* The idle list is capped by maxsize: a pool used with more names
282
+ than that would otherwise keep one idle item per name and grow
283
+ without bound. The oldest idle item gives way to the one just
284
+ returned, so the most recently used names stay cached. */
285
+ if (count >= maxsizeValue && count) {
286
+ const evicted = pools[0];
287
+ pools = pools.slice(1);
288
+ count--;
289
+ coroutine.start(destroy, evicted.o);
290
+ }
291
+
162
292
  pools[count++] = {
163
- o: o,
293
+ o: o as T_CREATED_ITEM,
164
294
  name: name,
165
295
  time: new Date()
166
296
  };
297
+ }
167
298
  }
168
299
 
169
300
  function connect(name: string) {
170
- let o: T_CREATED_ITEM;
171
- let cn = 0;
172
- let err: Error;
301
+ let o: T_CREATED_ITEM | undefined;
302
+ let err: Error | undefined;
173
303
 
174
- while (true) {
304
+ for (let cn = 1; ; cn++) {
175
305
  try {
176
306
  o = create(name);
177
307
  break;
178
308
  } catch (e) {
179
- if (++cn >= (retry as number)) {
180
- err = e;
309
+ /* any thrown value is a failure, including 0 or '' */
310
+ if (cn >= retry) {
311
+ err = toError(e);
181
312
  break;
182
313
  }
183
314
  }
@@ -186,100 +317,115 @@ export default function GetPool<
186
317
  putback(name, o, err);
187
318
  }
188
319
 
189
- const pool: FibPoolNS.FibPool<T_CREATED_ITEM, T_POOL_FUNC_RETURN> = (
320
+ /* The fiber is used as fiber-local storage: it is the only place a marker
321
+ can live without leaking between fibers. A fiber that already holds an
322
+ item from this pool would wait on itself when it asks again, which the
323
+ semaphore can never satisfy once every permit is out. */
324
+ const fiberDepth = Symbol('fib-pool depth');
325
+
326
+ type FiberState = Class_Fiber & { [fiberDepth]?: true };
327
+
328
+ function enterFiber(): FiberState {
329
+ const fiber = coroutine.current() as FiberState;
330
+
331
+ if (fiber[fiberDepth])
332
+ throw new Error("Cannot pool again from the same fiber: the pool is already held by this callback");
333
+
334
+ fiber[fiberDepth] = true;
335
+
336
+ return fiber;
337
+ }
338
+
339
+ function leaveFiber(fiber: FiberState) {
340
+ delete fiber[fiberDepth];
341
+ }
342
+
343
+ const pool = ((
190
344
  name: string | FibPoolNS.FibPoolDipperFn<T_CREATED_ITEM, T_POOL_FUNC_RETURN>,
191
345
  func?: FibPoolNS.FibPoolDipperFn<T_CREATED_ITEM, T_POOL_FUNC_RETURN>
192
346
  ) => {
193
347
  if (typeof name === 'function') {
194
- func = name as any;
348
+ func = name;
195
349
  name = "";
196
- }
350
+ }
351
+
352
+ const dip = func as FibPoolNS.FibPoolDipperFn<T_CREATED_ITEM, T_POOL_FUNC_RETURN>;
197
353
 
198
354
  let r: T_POOL_FUNC_RETURN;
199
- let o: typeof jobs[any]['o'];
200
- let p = false;
355
+ let o: T_CREATED_ITEM | undefined;
356
+ let reused = false;
357
+ const fiber = enterFiber();
201
358
 
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
- }
359
+ try {
360
+ clearPool();
361
+ sem.acquire();
362
+
363
+ if (count) {
364
+ for (let i = count - 1; i >= 0; i--)
365
+ if (pools[i].name === name) {
366
+ reused = true
367
+ o = pools[i].o;
368
+ pools.splice(i, 1);
369
+ count--;
370
+ break;
371
+ }
372
+ }
215
373
 
216
- if (!p) {
217
- coroutine.start(connect, name);
374
+ if (!reused) {
375
+ coroutine.start(connect, name);
218
376
 
219
- const job = <typeof jobs[any]>{
220
- name: name,
221
- ev: new coroutine.Event()
222
- };
223
- jobs.push(job);
377
+ const job: typeof jobs[any] = {
378
+ name: name,
379
+ ev: new coroutine.Event()
380
+ };
381
+ jobs.push(job);
382
+
383
+ job.ev.wait();
384
+ if (job.e) {
385
+ sem.post();
386
+ throw job.e;
387
+ }
388
+ o = job.o;
389
+ }
390
+
391
+ running++;
392
+ try {
393
+ const po = (strictMode && o !== null && typeof o === 'object')
394
+ ? wrapStrict<T_CREATED_ITEM & object>(() => o as T_CREATED_ITEM & object)
395
+ : o as T_CREATED_ITEM;
224
396
 
225
- job.ev.wait();
226
- if (job.e) {
397
+ r = dip(po);
398
+ putback(name, o);
399
+ o = undefined;
400
+ } catch (e) {
401
+ if (o !== undefined)
402
+ coroutine.start(destroy, o);
403
+ o = undefined;
404
+ throw toError(e);
405
+ } finally {
406
+ running--;
227
407
  sem.post();
228
- throw ensureError(job.e);
408
+ clearPool();
229
409
  }
230
- o = job.o;
231
- }
232
410
 
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);
411
+ return r;
262
412
  } finally {
263
- running--;
264
- sem.post();
265
- clearPool();
413
+ leaveFiber(fiber);
266
414
  }
267
-
268
- return r;
269
- };
415
+ }) as FibPoolNS.FibPool<T_CREATED_ITEM, T_POOL_FUNC_RETURN>;
270
416
 
271
417
  pool.connections = () => count
272
418
 
273
419
  pool.info = () => ({
274
- maxsize: maxsize,
420
+ maxsize: maxsizeValue,
275
421
  count: count,
276
422
  running: running,
277
423
  wait: sem.count(),
278
- timeout: timeout
424
+ timeout: timeoutValue
279
425
  })
280
426
 
281
427
  pool.clear = () => {
282
- timeout = -1;
428
+ timeoutValue = -1;
283
429
  clearPool();
284
430
  }
285
431
 
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;