$
This commit is contained in:
241
node_modules/workerpool/HISTORY.md
generated
vendored
Normal file
241
node_modules/workerpool/HISTORY.md
generated
vendored
Normal file
@@ -0,0 +1,241 @@
|
||||
# workerpool history
|
||||
https://github.com/josdejong/workerpool
|
||||
|
||||
## 2021-01-31, version 6.1.0
|
||||
|
||||
- Implemented support for sending events from the worker to the main thread,
|
||||
see #51, #227. Thanks @Akryum.
|
||||
- Fix an issue in Node.js nightly, see #230. Thanks @aduh95.
|
||||
- Fix #232 `workerpool` not working on IE 10.
|
||||
|
||||
|
||||
## 2021-01-16, version 6.0.4
|
||||
|
||||
- Make evaluation of offloaded functions a bit more secure by using
|
||||
`new Function` instead of `eval`. Thanks @tjenkinson.
|
||||
|
||||
|
||||
## 2020-10-28, version 6.0.3
|
||||
|
||||
- Fixes and more robustness in terminating workers. Thanks @boneskull.
|
||||
|
||||
|
||||
## 2020-10-03, version 6.0.2
|
||||
|
||||
- Fix #32, #175: the promise returned by `Pool.terminate()` now waits until
|
||||
subprocesses are dead before resolving. Thanks @boneskull.
|
||||
|
||||
|
||||
## 2020-09-23, version 6.0.1
|
||||
|
||||
- Removed examples from the npm package. Thanks @madbence.
|
||||
|
||||
|
||||
## 2020-05-13, version 6.0.0
|
||||
|
||||
WARNING: the library entry points are changed and new source maps are added.
|
||||
This may have impact on your project depending on your setup.
|
||||
|
||||
- Created separate library entry points in package.json for node.js and browser.
|
||||
Thanks @boneskull.
|
||||
- Generated source maps for both minified and non-minified bundles.
|
||||
- Removed deprecation warnings for `options.nodeWorker` (renamed to
|
||||
`options.workerType`) and `pool.clear()` (renamed to `pool.terminate()`).
|
||||
|
||||
|
||||
## 2019-12-31, version 5.0.4
|
||||
|
||||
- Fixed #121: `isMainThread` not working when using `worker_threads`.
|
||||
- Drop official support for node.js 8 (end of life).
|
||||
|
||||
|
||||
## 2019-12-23, version 5.0.3
|
||||
|
||||
- Fixed library not working in the browser. See #106.
|
||||
|
||||
|
||||
## 2019-11-06, version 5.0.2
|
||||
|
||||
- Fixed environment detection in browser. See #106. Thanks @acgrid.
|
||||
|
||||
|
||||
## 2019-10-13, version 5.0.1
|
||||
|
||||
- Fixed #96: WorkerPool not cancelling any pending tasks on termination.
|
||||
|
||||
|
||||
## 2019-08-25, version 5.0.0
|
||||
|
||||
- Deprecated option `nodeWorker` and created a new, more extensive option
|
||||
`workerType` giving full control over the selected type of worker.
|
||||
Added new option `'web'` to enforce use a Web Worker. See #85, #74.
|
||||
- In a node.js environment, the default `workerType` is changed from
|
||||
`'process'` to `'thread'`. See #85, #50.
|
||||
- Improved detection of environment (`browser` or `node`), fixing wrong
|
||||
detection in a Jest test environment. See #85.
|
||||
|
||||
|
||||
## 2019-08-21, version 4.0.0
|
||||
|
||||
- Pass argument `--max-old-space-size` to child processes. Thanks @patte.
|
||||
- Removed redundant dependencies, upgraded all devDependencies.
|
||||
- Fixed Webpack issues of missing modules `child_process` and `worker_threads`.
|
||||
See #43.
|
||||
- Bundled library changed due to the upgrade to Webpack 4. This could possibly
|
||||
lead to breaking changes.
|
||||
- Implemented new option `maxQueueSize`. Thanks @colomboe.
|
||||
- Fixed exiting workers when the parent process is killed. Thanks @RogerKang.
|
||||
- Fixed #81: Option `minWorkers: 'max'` not using the configured `maxWorkers`.
|
||||
- Fixed not passing `nodeWorker` to workers initialized when creating a pool.
|
||||
Thanks @spacelan.
|
||||
- Internal restructure of the code: moved from `lib` to `src`.
|
||||
|
||||
|
||||
## 2019-03-12, version 3.1.2
|
||||
|
||||
- Improved error message when a node.js worker unexpectedly exits (see #58).
|
||||
Thanks @stefanpenner.
|
||||
|
||||
- Allocate debug ports safely, this fixes an issue cause workers to exit
|
||||
unexpectedly if more then one worker pool is active, and the process is
|
||||
started with a debugger (`node debug` or `node --inspect`).
|
||||
Thanks @stefanpenner.
|
||||
|
||||
|
||||
## 2019-02-25, version 3.1.1
|
||||
|
||||
- Fix option `nodeWorker: 'auto'` not using worker threads when available.
|
||||
Thanks @stefanpenner.
|
||||
|
||||
|
||||
## 2019-02-17, version 3.1.0
|
||||
|
||||
- Implemented support for using `worker_threads` in Node.js, via the new option
|
||||
`nodeWorker: 'thread'`. Thanks @stefanpenner.
|
||||
|
||||
|
||||
## 2018-12-11, version 3.0.0
|
||||
|
||||
- Enable usage in ES6 Webpack projects.
|
||||
- Dropped support for AMD module system.
|
||||
|
||||
|
||||
## 2018-09-12, version 2.3.3
|
||||
|
||||
- Fixed space in license field in `package.json`. Thanks @sagotsky.
|
||||
|
||||
|
||||
## 2018-09-08, version 2.3.2
|
||||
|
||||
- Add licence field to `package.json`. Thanks @greyd.
|
||||
|
||||
|
||||
## 2018-07-24, version 2.3.1
|
||||
|
||||
- Fixed bug where tasks that are cancelled in a Pool's queue
|
||||
causes following tasks to not run. Thanks @greemo.
|
||||
|
||||
|
||||
## 2017-09-30, version 2.3.0
|
||||
|
||||
- New method `Pool.terminate(force, timeout)` which will replace
|
||||
`Pool.clear(force)`. Thanks @jimsugg.
|
||||
- Fixed issue with never terminating zombie child processes.
|
||||
Thanks @jimsugg.
|
||||
|
||||
|
||||
## 2017-08-20, version 2.2.4
|
||||
|
||||
- Fixed a debug issue: look for `--inspect` within argument strings,
|
||||
instead of exact match. Thanks @jimsugg.
|
||||
|
||||
|
||||
## 2017-08-19, version 2.2.3
|
||||
|
||||
- Updated all examples to neatly include `.catch(...)` callbacks.
|
||||
|
||||
|
||||
## 2017-07-08, version 2.2.2
|
||||
|
||||
- Fixed #25: timer of a timeout starting when the task is created
|
||||
instead of when the task is started. Thanks @eclipsesk for input.
|
||||
|
||||
|
||||
## 2017-05-07, version 2.2.1
|
||||
|
||||
- Fixed #2 and #19: support for debugging child processes. Thanks @tptee.
|
||||
|
||||
|
||||
## 2016-11-26, version 2.2.0
|
||||
|
||||
- Implemented #18: method `pool.stats()`.
|
||||
|
||||
|
||||
## 2016-10-11, version 2.1.0
|
||||
|
||||
- Implemented support for registering the workers methods asynchronously.
|
||||
This enables asynchronous initialization of workers, for example when
|
||||
using AMD modules. Thanks @natlibfi-arlehiko.
|
||||
- Implemented environment variables `platform`, `isMainThread`, and `cpus`.
|
||||
Thanks @natlibfi-arlehiko.
|
||||
- Implemented option `minWorkers`. Thanks @sergei202.
|
||||
|
||||
|
||||
## 2016-09-18, version 2.0.0
|
||||
|
||||
- Replaced conversion of Error-objecting using serializerr to custom
|
||||
implementation to prevent issues with serializing/deserializing functions.
|
||||
This conversion implementation loses the prototype object which means that
|
||||
e.g. 'TypeError' will become just 'Error' in the main code. See #8.
|
||||
Thanks @natlibfi-arlehiko.
|
||||
|
||||
|
||||
## 2016-09-12, version 1.3.1
|
||||
|
||||
- Fix for a bug in PhantomJS (see #7). Thanks @natlibfi-arlehiko.
|
||||
|
||||
|
||||
## 2016-08-21, version 1.3.0
|
||||
|
||||
- Determine `maxWorkers` as the number of CPU's minus one in browsers too. See #6.
|
||||
|
||||
|
||||
## 2016-06-25, version 1.2.1
|
||||
|
||||
- Fixed #5 error when loading via AMD or bundling using Webpack.
|
||||
|
||||
|
||||
## 2016-05-22, version 1.2.0
|
||||
|
||||
- Implemented serializing errors with stacktrace. Thanks @mujx.
|
||||
|
||||
|
||||
## 2016-01-25, version 1.1.0
|
||||
|
||||
- Added an error message when wrongly calling `pool.proxy`.
|
||||
- Fixed function `worker.pool` not accepting both a script and options. See #1.
|
||||
Thanks @freund17.
|
||||
|
||||
|
||||
## 2014-05-29, version 1.0.0
|
||||
|
||||
- Merged function `Pool.run` into `Pool.exec`, simplifying the API.
|
||||
|
||||
|
||||
## 2014-05-14, version 0.2.0
|
||||
|
||||
- Implemented support for cancelling running tasks.
|
||||
- Implemented support for cancelling running tasks after a timeout.
|
||||
|
||||
|
||||
## 2014-05-07, version 0.1.0
|
||||
|
||||
- Implemented support for both node.js and the browser.
|
||||
- Implemented offloading functions.
|
||||
- Implemented worker proxy.
|
||||
- Added docs and examples.
|
||||
|
||||
|
||||
## 2014-05-02, version 0.0.1
|
||||
|
||||
- Module name registered at npm.
|
201
node_modules/workerpool/LICENSE
generated
vendored
Normal file
201
node_modules/workerpool/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
486
node_modules/workerpool/README.md
generated
vendored
Normal file
486
node_modules/workerpool/README.md
generated
vendored
Normal file
@@ -0,0 +1,486 @@
|
||||
# workerpool
|
||||
|
||||
|
||||
|
||||
**workerpool** offers an easy way to create a pool of workers for both dynamically offloading computations as well as managing a pool of dedicated workers. **workerpool** basically implements a [thread pool pattern](http://en.wikipedia.org/wiki/Thread_pool_pattern). There is a pool of workers to execute tasks. New tasks are put in a queue. A worker executes one task at a time, and once finished, picks a new task from the queue. Workers can be accessed via a natural, promise based proxy, as if they are available straight in the main application.
|
||||
|
||||
**workerpool** runs on node.js, Chrome, Firefox, Opera, Safari, and IE10+.
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- Easy to use
|
||||
- Runs in the browser and on node.js
|
||||
- Dynamically offload functions to a worker
|
||||
- Access workers via a proxy
|
||||
- Cancel running tasks
|
||||
- Set a timeout on tasks
|
||||
- Handles crashed workers
|
||||
- Small: 5 kB minified and gzipped
|
||||
|
||||
|
||||
## Why
|
||||
|
||||
JavaScript is based upon a single event loop which handles one event at a time. Jeremy Epstein [explains this clearly](http://greenash.net.au/thoughts/2012/11/nodejs-itself-is-blocking-only-its-io-is-non-blocking/):
|
||||
|
||||
> In Node.js everything runs in parallel, except your code.
|
||||
> What this means is that all I/O code that you write in Node.js is non-blocking,
|
||||
> while (conversely) all non-I/O code that you write in Node.js is blocking.
|
||||
|
||||
This means that CPU heavy tasks will block other tasks from being executed. In case of a browser environment, the browser will not react to user events like a mouse click while executing a CPU intensive task (the browser "hangs"). In case of a node.js server, the server will not respond to any new request while executing a single, heavy request.
|
||||
|
||||
For front-end processes, this is not a desired situation.
|
||||
Therefore, CPU intensive tasks should be offloaded from the main event loop onto dedicated *workers*. In a browser environment, [Web Workers](http://www.html5rocks.com/en/tutorials/workers/basics/) can be used. In node.js, [child processes](https://nodejs.org/api/child_process.html) and [worker_threads](https://nodejs.org/api/worker_threads.html) are available. An application should be split in separate, decoupled parts, which can run independent of each other in a parallelized way. Effectively, this results in an architecture which achieves concurrency by means of isolated processes and message passing.
|
||||
|
||||
|
||||
## Install
|
||||
|
||||
Install via npm:
|
||||
|
||||
npm install workerpool
|
||||
|
||||
|
||||
## Load
|
||||
|
||||
To load workerpool in a node.js application (both main application as well as workers):
|
||||
|
||||
```js
|
||||
const workerpool = require('workerpool');
|
||||
```
|
||||
|
||||
To load workerpool in the browser:
|
||||
|
||||
```html
|
||||
<script src="workerpool.js"></script>
|
||||
```
|
||||
|
||||
To load workerpool in a web worker in the browser:
|
||||
|
||||
```js
|
||||
importScripts('workerpool.js');
|
||||
```
|
||||
|
||||
|
||||
## Use
|
||||
|
||||
### Offload functions dynamically
|
||||
|
||||
In the following example there is a function `add`, which is offloaded dynamically to a worker to be executed for a given set of arguments.
|
||||
|
||||
**myApp.js**
|
||||
```js
|
||||
const workerpool = require('workerpool');
|
||||
const pool = workerpool.pool();
|
||||
|
||||
function add(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
pool.exec(add, [3, 4])
|
||||
.then(function (result) {
|
||||
console.log('result', result); // outputs 7
|
||||
})
|
||||
.catch(function (err) {
|
||||
console.error(err);
|
||||
})
|
||||
.then(function () {
|
||||
pool.terminate(); // terminate all workers when done
|
||||
});
|
||||
```
|
||||
|
||||
Note that both function and arguments must be static and stringifiable, as they need to be sent to the worker in a serialized form. In case of large functions or function arguments, the overhead of sending the data to the worker can be significant.
|
||||
|
||||
|
||||
### Dedicated workers
|
||||
|
||||
A dedicated worker can be created in a separate script, and then used via a worker pool.
|
||||
|
||||
**myWorker.js**
|
||||
```js
|
||||
const workerpool = require('workerpool');
|
||||
|
||||
// a deliberately inefficient implementation of the fibonacci sequence
|
||||
function fibonacci(n) {
|
||||
if (n < 2) return n;
|
||||
return fibonacci(n - 2) + fibonacci(n - 1);
|
||||
}
|
||||
|
||||
// create a worker and register public functions
|
||||
workerpool.worker({
|
||||
fibonacci: fibonacci
|
||||
});
|
||||
```
|
||||
|
||||
This worker can be used by a worker pool:
|
||||
|
||||
**myApp.js**
|
||||
```js
|
||||
const workerpool = require('workerpool');
|
||||
|
||||
// create a worker pool using an external worker script
|
||||
const pool = workerpool.pool(__dirname + '/myWorker.js');
|
||||
|
||||
// run registered functions on the worker via exec
|
||||
pool.exec('fibonacci', [10])
|
||||
.then(function (result) {
|
||||
console.log('Result: ' + result); // outputs 55
|
||||
})
|
||||
.catch(function (err) {
|
||||
console.error(err);
|
||||
})
|
||||
.then(function () {
|
||||
pool.terminate(); // terminate all workers when done
|
||||
});
|
||||
|
||||
// or run registered functions on the worker via a proxy:
|
||||
pool.proxy()
|
||||
.then(function (worker) {
|
||||
return worker.fibonacci(10);
|
||||
})
|
||||
.then(function (result) {
|
||||
console.log('Result: ' + result); // outputs 55
|
||||
})
|
||||
.catch(function (err) {
|
||||
console.error(err);
|
||||
})
|
||||
.then(function () {
|
||||
pool.terminate(); // terminate all workers when done
|
||||
});
|
||||
```
|
||||
|
||||
Worker can also initialize asynchronously:
|
||||
|
||||
**myAsyncWorker.js**
|
||||
```js
|
||||
define(['workerpool/dist/workerpool'], function(workerpool) {
|
||||
|
||||
// a deliberately inefficient implementation of the fibonacci sequence
|
||||
function fibonacci(n) {
|
||||
if (n < 2) return n;
|
||||
return fibonacci(n - 2) + fibonacci(n - 1);
|
||||
}
|
||||
|
||||
// create a worker and register public functions
|
||||
workerpool.worker({
|
||||
fibonacci: fibonacci
|
||||
});
|
||||
|
||||
});
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
Examples are available in the examples directory:
|
||||
|
||||
[https://github.com/josdejong/workerpool/tree/master/examples](https://github.com/josdejong/workerpool/tree/master/examples)
|
||||
|
||||
|
||||
## API
|
||||
|
||||
The API of workerpool consists of two parts: a function `workerpool.pool` to create a worker pool, and a function `workerpool.worker` to create a worker.
|
||||
|
||||
### pool
|
||||
|
||||
A workerpool can be created using the function `workerpool.pool`:
|
||||
|
||||
`workerpool.pool([script: string] [, options: Object]) : Pool`
|
||||
|
||||
When a `script` argument is provided, the provided script will be started as a dedicated worker. When no `script` argument is provided, a default worker is started which can be used to offload functions dynamically via `Pool.exec`. Note that on node.js, `script` must be an absolute file path like `__dirname + '/myWorker.js'`. In a browser environment, `script` can also be a data URL like `'data:application/javascript;base64,...'`. This allows embedding the bundled code of a worker in your main application. See `examples/embeddedWorker` for a demo.
|
||||
|
||||
The following options are available:
|
||||
|
||||
- `minWorkers: number | 'max'`. The minimum number of workers that must be initialized and kept available. Setting this to `'max'` will create `maxWorkers` default workers (see below).
|
||||
- `maxWorkers: number`. The default number of maxWorkers is the number of CPU's minus one. When the number of CPU's could not be determined (for example in older browsers), `maxWorkers` is set to 3.
|
||||
- `maxQueueSize: number`. The maximum number of tasks allowed to be queued. Can be used to prevent running out of memory. If the maximum is exceeded, adding a new task will throw an error. The default value is `Infinity`.
|
||||
- `workerType: 'auto' | 'web' | 'process' | 'thread'`.
|
||||
- In case of `'auto'` (default), workerpool will automatically pick a suitable type of worker: when in a browser environment, `'web'` will be used. When in a node.js environment, `worker_threads` will be used if available (Node.js >= 11.7.0), else `child_process` will be used.
|
||||
- In case of `'web'`, a Web Worker will be used. Only available in a browser environment.
|
||||
- In case of `'process'`, `child_process` will be used. Only available in a node.js environment.
|
||||
- In case of `'thread'`, `worker_threads` will be used. If `worker_threads` are not available, an error is thrown. Only available in a node.js environment.
|
||||
|
||||
> Important note on `'workerType'`: when sending and receiving primitive data types (plain JSON) from and to a worker, the different worker types (`'web'`, `'process'`, `'thread'`) can be used interchangeably. However, when using more advanced data types like buffers, the API and returned results can vary. In these cases, it is best not to use the `'auto'` setting but have a fixed `'workerType'` and good unit testing in place.
|
||||
|
||||
A worker pool contains the following functions:
|
||||
|
||||
- `Pool.exec(method: Function | string, params: Array | null [, options: Object]) : Promise.<*, Error>`<br>
|
||||
Execute a function on a worker with given arguments.
|
||||
- When `method` is a string, a method with this name must exist at the worker and must be registered to make it accessible via the pool. The function will be executed on the worker with given parameters.
|
||||
- When `method` is a function, the provided function `fn` will be stringified, send to the worker, and executed there with the provided parameters. The provided function must be static, it must not depend on variables in a surrounding scope.
|
||||
- The following options are available:
|
||||
- `on: (payload: any) => void`. An event listener, to handle events sent by the worker for this execution. See [Events](#events) for more details.
|
||||
|
||||
- `Pool.proxy() : Promise.<Object, Error>`<br>
|
||||
Create a proxy for the worker pool. The proxy contains a proxy for all methods available on the worker. All methods return promises resolving the methods result.
|
||||
|
||||
- `Pool.stats() : Object`<br>
|
||||
Retrieve statistics on workers, and active and pending tasks.
|
||||
|
||||
Returns an object containing the following properties:
|
||||
|
||||
```
|
||||
{
|
||||
totalWorkers: 0,
|
||||
busyWorkers: 0,
|
||||
idleWorkers: 0,
|
||||
pendingTasks: 0,
|
||||
activeTasks: 0
|
||||
}
|
||||
```
|
||||
|
||||
- `Pool.terminate([force: boolean [, timeout: number]])`
|
||||
|
||||
If parameter `force` is false (default), workers will finish the tasks they are working on before terminating themselves. Any pending tasks will be rejected with an error 'Pool terminated'. When `force` is true, all workers are terminated immediately without finishing running tasks. If `timeout` is provided, worker will be forced to terminate when the timeout expires and the worker has not finished.
|
||||
|
||||
The function `Pool.exec` and the proxy functions all return a `Promise`. The promise has the following functions available:
|
||||
|
||||
- `Promise.then(fn: Function.<result: *>)`<br>
|
||||
Get the result of the promise once resolve.
|
||||
- `Promise.catch(fn: Function.<error: Error>)`<br>
|
||||
Get the error of the promise when rejected.
|
||||
- `Promise.cancel()`<br>
|
||||
A running task can be cancelled. The worker executing the task is enforced to terminate immediately.
|
||||
The promise will be rejected with a `Promise.CancellationError`.
|
||||
- `Promise.timeout(delay: number)`<br>
|
||||
Cancel a running task when it is not resolved or rejected within given delay in milliseconds. The timer will start when the task is actually started, not when the task is created and queued.
|
||||
The worker executing the task is enforced to terminate immediately.
|
||||
The promise will be rejected with a `Promise.TimeoutError`.
|
||||
|
||||
Example usage:
|
||||
|
||||
```js
|
||||
const workerpool = require('workerpool');
|
||||
|
||||
function add(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
const pool1 = workerpool.pool();
|
||||
|
||||
// offload a function to a worker
|
||||
pool1.exec(add, [2, 4])
|
||||
.then(function (result) {
|
||||
console.log(result); // will output 6
|
||||
})
|
||||
.catch(function (err) {
|
||||
console.error(err);
|
||||
});
|
||||
|
||||
// create a dedicated worker
|
||||
const pool2 = workerpool.pool(__dirname + '/myWorker.js');
|
||||
|
||||
// supposed myWorker.js contains a function 'fibonacci'
|
||||
pool2.exec('fibonacci', [10])
|
||||
.then(function (result) {
|
||||
console.log(result); // will output 55
|
||||
})
|
||||
.catch(function (err) {
|
||||
console.error(err);
|
||||
});
|
||||
|
||||
// create a proxy to myWorker.js
|
||||
pool2.proxy()
|
||||
.then(function (myWorker) {
|
||||
return myWorker.fibonacci(10)
|
||||
})
|
||||
.then(function (result) {
|
||||
console.log(result); // will output 55
|
||||
})
|
||||
.catch(function (err) {
|
||||
console.error(err);
|
||||
});
|
||||
|
||||
// create a pool with a specified maximum number of workers
|
||||
const pool3 = workerpool.pool({maxWorkers: 7});
|
||||
```
|
||||
|
||||
|
||||
### worker
|
||||
|
||||
A worker is constructed as:
|
||||
|
||||
`workerpool.worker([methods: Object.<String, Function>])`
|
||||
|
||||
Argument `methods` is optional can can be an object with functions available in the worker. Registered functions will be available via the worker pool.
|
||||
|
||||
Example usage:
|
||||
|
||||
```js
|
||||
// file myWorker.js
|
||||
const workerpool = require('workerpool');
|
||||
|
||||
function add(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
function multiply(a, b) {
|
||||
return a * b;
|
||||
}
|
||||
|
||||
// create a worker and register functions
|
||||
workerpool.worker({
|
||||
add: add,
|
||||
multiply: multiply
|
||||
});
|
||||
```
|
||||
|
||||
Asynchronous results can be handled by returning a Promise from a function in the worker:
|
||||
|
||||
```js
|
||||
// file myWorker.js
|
||||
const workerpool = require('workerpool');
|
||||
|
||||
function timeout(delay) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
setTimeout(resolve, delay)
|
||||
});
|
||||
}
|
||||
|
||||
// create a worker and register functions
|
||||
workerpool.worker({
|
||||
timeout: timeout
|
||||
});
|
||||
```
|
||||
|
||||
### Events
|
||||
|
||||
You can send data back from workers to the pool while the task is being executed using the `workerEmit` function:
|
||||
|
||||
`workerEmit(payload: any)`
|
||||
|
||||
This function only works inside a worker **and** during a task.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
// file myWorker.js
|
||||
const workerpool = require('workerpool');
|
||||
|
||||
function eventExample(delay) {
|
||||
workerpool.workerEmit({
|
||||
status: 'in_progress'
|
||||
});
|
||||
|
||||
workerpool.workerEmit({
|
||||
status: 'complete'
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// create a worker and register functions
|
||||
workerpool.worker({
|
||||
eventExample: eventExample
|
||||
});
|
||||
```
|
||||
|
||||
To receive those events, you can use the `on` option of the pool `exec` method:
|
||||
|
||||
```js
|
||||
pool.exec('eventExample', [], {
|
||||
on: function (payload) {
|
||||
if (payload.status === 'in_progress') {
|
||||
console.log('In progress...');
|
||||
} else if (payload.status === 'complete') {
|
||||
console.log('Done!');
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Utilities
|
||||
|
||||
Following properties are available for convenience:
|
||||
|
||||
- **platform**: The Javascript platform. Either *node* or *browser*
|
||||
- **isMainThread**: Whether the code is running in main thread or not (Workers)
|
||||
- **cpus**: The number of CPUs/cores available
|
||||
|
||||
|
||||
## Roadmap
|
||||
|
||||
- Implement functions for parallel processing: `map`, `reduce`, `forEach`,
|
||||
`filter`, `some`, `every`, ...
|
||||
- Implement graceful degradation on old browsers not supporting webworkers:
|
||||
fallback to processing tasks in the main application.
|
||||
- Implement session support: be able to handle a series of related tasks by a
|
||||
single worker, which can keep a state for the session.
|
||||
|
||||
|
||||
## Related libraries
|
||||
|
||||
- https://github.com/learnboost/cluster
|
||||
- https://github.com/adambom/parallel.js
|
||||
- https://github.com/padolsey/operative
|
||||
- https://github.com/calvinmetcalf/catiline
|
||||
- https://github.com/Unitech/pm2
|
||||
- https://github.com/godaddy/node-cluster-service
|
||||
- https://github.com/ramesaliyev/EasyWebWorker
|
||||
- https://github.com/rvagg/node-worker-farm
|
||||
|
||||
|
||||
## Build
|
||||
|
||||
First clone the project from github:
|
||||
|
||||
git clone git://github.com/josdejong/workerpool.git
|
||||
cd workerpool
|
||||
|
||||
Install the project dependencies:
|
||||
|
||||
npm install
|
||||
|
||||
Then, the project can be build by executing the build script via npm:
|
||||
|
||||
npm run build
|
||||
|
||||
This will build the library workerpool.js and workerpool.min.js from the source
|
||||
files and put them in the folder dist.
|
||||
|
||||
|
||||
## Test
|
||||
|
||||
To execute tests for the library, install the project dependencies once:
|
||||
|
||||
npm install
|
||||
|
||||
Then, the tests can be executed:
|
||||
|
||||
npm test
|
||||
|
||||
To test code coverage of the tests:
|
||||
|
||||
npm run coverage
|
||||
|
||||
To see the coverage results, open the generated report in your browser:
|
||||
|
||||
./coverage/lcov-report/index.html
|
||||
|
||||
|
||||
## Publish
|
||||
|
||||
- Describe changes in HISTORY.md
|
||||
- Update version in package.json, run `npm install` to update it in `package-lock.json` too.
|
||||
- Push to github
|
||||
- Deploy to npm via `npm publish`
|
||||
- Add a git tag with the version number like:
|
||||
```
|
||||
git tag v1.2.3
|
||||
git push --tags
|
||||
```
|
||||
|
||||
|
||||
## License
|
||||
|
||||
Copyright (C) 2014-2020 Jos de Jong <wjosdejong@gmail.com>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
257
node_modules/workerpool/dist/worker.js
generated
vendored
Normal file
257
node_modules/workerpool/dist/worker.js
generated
vendored
Normal file
@@ -0,0 +1,257 @@
|
||||
/**
|
||||
* workerpool.js
|
||||
* https://github.com/josdejong/workerpool
|
||||
*
|
||||
* Offload tasks to a pool of workers on node.js and in the browser.
|
||||
*
|
||||
* @version 6.1.0
|
||||
* @date 2021-01-31
|
||||
*
|
||||
* @license
|
||||
* Copyright (C) 2014-2020 Jos de Jong <wjosdejong@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy
|
||||
* of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
/******/ (function() { // webpackBootstrap
|
||||
/******/ var __webpack_modules__ = ({
|
||||
|
||||
/***/ 744:
|
||||
/***/ (function(__unused_webpack_module, exports) {
|
||||
|
||||
var __webpack_unused_export__;
|
||||
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
|
||||
|
||||
/**
|
||||
* worker must be started as a child process or a web worker.
|
||||
* It listens for RPC messages from the parent process.
|
||||
*/
|
||||
// source of inspiration: https://github.com/sindresorhus/require-fool-webpack
|
||||
var requireFoolWebpack = eval('typeof require !== \'undefined\'' + ' ? require' + ' : function (module) { throw new Error(\'Module " + module + " not found.\') }');
|
||||
/**
|
||||
* Special message sent by parent which causes the worker to terminate itself.
|
||||
* Not a "message object"; this string is the entire message.
|
||||
*/
|
||||
|
||||
var TERMINATE_METHOD_ID = '__workerpool-terminate__'; // var nodeOSPlatform = require('./environment').nodeOSPlatform;
|
||||
// create a worker API for sending and receiving messages which works both on
|
||||
// node.js and in the browser
|
||||
|
||||
var worker = {
|
||||
exit: function exit() {}
|
||||
};
|
||||
|
||||
if (typeof self !== 'undefined' && typeof postMessage === 'function' && typeof addEventListener === 'function') {
|
||||
// worker in the browser
|
||||
worker.on = function (event, callback) {
|
||||
addEventListener(event, function (message) {
|
||||
callback(message.data);
|
||||
});
|
||||
};
|
||||
|
||||
worker.send = function (message) {
|
||||
postMessage(message);
|
||||
};
|
||||
} else if (typeof process !== 'undefined') {
|
||||
// node.js
|
||||
var WorkerThreads;
|
||||
|
||||
try {
|
||||
WorkerThreads = requireFoolWebpack('worker_threads');
|
||||
} catch (error) {
|
||||
if (_typeof(error) === 'object' && error !== null && error.code === 'MODULE_NOT_FOUND') {// no worker_threads, fallback to sub-process based workers
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
if (WorkerThreads &&
|
||||
/* if there is a parentPort, we are in a WorkerThread */
|
||||
WorkerThreads.parentPort !== null) {
|
||||
var parentPort = WorkerThreads.parentPort;
|
||||
worker.send = parentPort.postMessage.bind(parentPort);
|
||||
worker.on = parentPort.on.bind(parentPort);
|
||||
} else {
|
||||
worker.on = process.on.bind(process);
|
||||
worker.send = process.send.bind(process); // register disconnect handler only for subprocess worker to exit when parent is killed unexpectedly
|
||||
|
||||
worker.on('disconnect', function () {
|
||||
process.exit(1);
|
||||
});
|
||||
worker.exit = process.exit.bind(process);
|
||||
}
|
||||
} else {
|
||||
throw new Error('Script must be executed as a worker');
|
||||
}
|
||||
|
||||
function convertError(error) {
|
||||
return Object.getOwnPropertyNames(error).reduce(function (product, name) {
|
||||
return Object.defineProperty(product, name, {
|
||||
value: error[name],
|
||||
enumerable: true
|
||||
});
|
||||
}, {});
|
||||
}
|
||||
/**
|
||||
* Test whether a value is a Promise via duck typing.
|
||||
* @param {*} value
|
||||
* @returns {boolean} Returns true when given value is an object
|
||||
* having functions `then` and `catch`.
|
||||
*/
|
||||
|
||||
|
||||
function isPromise(value) {
|
||||
return value && typeof value.then === 'function' && typeof value["catch"] === 'function';
|
||||
} // functions available externally
|
||||
|
||||
|
||||
worker.methods = {};
|
||||
/**
|
||||
* Execute a function with provided arguments
|
||||
* @param {String} fn Stringified function
|
||||
* @param {Array} [args] Function arguments
|
||||
* @returns {*}
|
||||
*/
|
||||
|
||||
worker.methods.run = function run(fn, args) {
|
||||
var f = new Function('return (' + fn + ').apply(null, arguments);');
|
||||
return f.apply(f, args);
|
||||
};
|
||||
/**
|
||||
* Get a list with methods available on this worker
|
||||
* @return {String[]} methods
|
||||
*/
|
||||
|
||||
|
||||
worker.methods.methods = function methods() {
|
||||
return Object.keys(worker.methods);
|
||||
};
|
||||
|
||||
var currentRequestId = null;
|
||||
worker.on('message', function (request) {
|
||||
if (request === TERMINATE_METHOD_ID) {
|
||||
return worker.exit(0);
|
||||
}
|
||||
|
||||
try {
|
||||
var method = worker.methods[request.method];
|
||||
|
||||
if (method) {
|
||||
currentRequestId = request.id; // execute the function
|
||||
|
||||
var result = method.apply(method, request.params);
|
||||
|
||||
if (isPromise(result)) {
|
||||
// promise returned, resolve this and then return
|
||||
result.then(function (result) {
|
||||
worker.send({
|
||||
id: request.id,
|
||||
result: result,
|
||||
error: null
|
||||
});
|
||||
currentRequestId = null;
|
||||
})["catch"](function (err) {
|
||||
worker.send({
|
||||
id: request.id,
|
||||
result: null,
|
||||
error: convertError(err)
|
||||
});
|
||||
currentRequestId = null;
|
||||
});
|
||||
} else {
|
||||
// immediate result
|
||||
worker.send({
|
||||
id: request.id,
|
||||
result: result,
|
||||
error: null
|
||||
});
|
||||
currentRequestId = null;
|
||||
}
|
||||
} else {
|
||||
throw new Error('Unknown method "' + request.method + '"');
|
||||
}
|
||||
} catch (err) {
|
||||
worker.send({
|
||||
id: request.id,
|
||||
result: null,
|
||||
error: convertError(err)
|
||||
});
|
||||
}
|
||||
});
|
||||
/**
|
||||
* Register methods to the worker
|
||||
* @param {Object} methods
|
||||
*/
|
||||
|
||||
worker.register = function (methods) {
|
||||
if (methods) {
|
||||
for (var name in methods) {
|
||||
if (methods.hasOwnProperty(name)) {
|
||||
worker.methods[name] = methods[name];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
worker.send('ready');
|
||||
};
|
||||
|
||||
worker.emit = function (payload) {
|
||||
if (currentRequestId) {
|
||||
worker.send({
|
||||
id: currentRequestId,
|
||||
isEvent: true,
|
||||
payload: payload
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (true) {
|
||||
__webpack_unused_export__ = worker.register;
|
||||
__webpack_unused_export__ = worker.emit;
|
||||
}
|
||||
|
||||
/***/ })
|
||||
|
||||
/******/ });
|
||||
/************************************************************************/
|
||||
/******/ // The module cache
|
||||
/******/ var __webpack_module_cache__ = {};
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/ // Check if module is in cache
|
||||
/******/ if(__webpack_module_cache__[moduleId]) {
|
||||
/******/ return __webpack_module_cache__[moduleId].exports;
|
||||
/******/ }
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = __webpack_module_cache__[moduleId] = {
|
||||
/******/ // no module.id needed
|
||||
/******/ // no module.loaded needed
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/************************************************************************/
|
||||
/******/ // startup
|
||||
/******/ // Load entry module
|
||||
/******/ __webpack_require__(744);
|
||||
/******/ // This entry module used 'exports' so it can't be inlined
|
||||
/******/ })()
|
||||
;
|
||||
//# sourceMappingURL=worker.js.map
|
1
node_modules/workerpool/dist/worker.js.map
generated
vendored
Normal file
1
node_modules/workerpool/dist/worker.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1640
node_modules/workerpool/dist/workerpool.js
generated
vendored
Normal file
1640
node_modules/workerpool/dist/workerpool.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
node_modules/workerpool/dist/workerpool.js.map
generated
vendored
Normal file
1
node_modules/workerpool/dist/workerpool.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
3
node_modules/workerpool/dist/workerpool.min.js
generated
vendored
Normal file
3
node_modules/workerpool/dist/workerpool.min.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
24
node_modules/workerpool/dist/workerpool.min.js.LICENSE.txt
generated
vendored
Normal file
24
node_modules/workerpool/dist/workerpool.min.js.LICENSE.txt
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* workerpool.js
|
||||
* https://github.com/josdejong/workerpool
|
||||
*
|
||||
* Offload tasks to a pool of workers on node.js and in the browser.
|
||||
*
|
||||
* @version 6.1.0
|
||||
* @date 2021-01-31
|
||||
*
|
||||
* @license
|
||||
* Copyright (C) 2014-2020 Jos de Jong <wjosdejong@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy
|
||||
* of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
1
node_modules/workerpool/dist/workerpool.min.js.map
generated
vendored
Normal file
1
node_modules/workerpool/dist/workerpool.min.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
52
node_modules/workerpool/package.json
generated
vendored
Normal file
52
node_modules/workerpool/package.json
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"name": "workerpool",
|
||||
"license": "Apache-2.0",
|
||||
"version": "6.1.0",
|
||||
"description": "Offload tasks to a pool of workers on node.js and in the browser",
|
||||
"homepage": "https://github.com/josdejong/workerpool",
|
||||
"author": "Jos de Jong <wjosdejong@gmail.com> (https://github.com/josdejong)",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/josdejong/workerpool.git"
|
||||
},
|
||||
"keywords": [
|
||||
"worker",
|
||||
"web worker",
|
||||
"cluster",
|
||||
"pool",
|
||||
"isomorphic"
|
||||
],
|
||||
"main": "src/index.js",
|
||||
"browser": "dist/workerpool.js",
|
||||
"files": [
|
||||
"dist",
|
||||
"src",
|
||||
"HISTORY.md",
|
||||
"LICENSE",
|
||||
"README.md"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "gulp",
|
||||
"watch": "gulp watch",
|
||||
"test": "npm run build && mocha test --timeout 2000",
|
||||
"test:debug": "npm run build && mocha debug test --timeout 10000",
|
||||
"coverage": "npm run build && istanbul cover _mocha -- test; echo \"\nCoverage report is available at ./coverage/lcov-report/index.html\"",
|
||||
"prepublishOnly": "npm run test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.12.10",
|
||||
"@babel/preset-env": "7.12.11",
|
||||
"babel-loader": "8.2.2",
|
||||
"date-format": "3.0.0",
|
||||
"del": "6.0.0",
|
||||
"fancy-log": "1.3.3",
|
||||
"find-process": "1.4.4",
|
||||
"gulp": "4.0.2",
|
||||
"handlebars": "4.7.6",
|
||||
"istanbul": "0.4.5",
|
||||
"mocha": "8.2.1",
|
||||
"uglify-js": "3.12.5",
|
||||
"webpack": "5.19.0"
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
416
node_modules/workerpool/src/Pool.js
generated
vendored
Normal file
416
node_modules/workerpool/src/Pool.js
generated
vendored
Normal file
@@ -0,0 +1,416 @@
|
||||
var Promise = require('./Promise');
|
||||
var WorkerHandler = require('./WorkerHandler');
|
||||
var environment = require('./environment');
|
||||
var DebugPortAllocator = require('./debug-port-allocator');
|
||||
var DEBUG_PORT_ALLOCATOR = new DebugPortAllocator();
|
||||
/**
|
||||
* A pool to manage workers
|
||||
* @param {String} [script] Optional worker script
|
||||
* @param {WorkerPoolOptions} [options] See docs
|
||||
* @constructor
|
||||
*/
|
||||
function Pool(script, options) {
|
||||
if (typeof script === 'string') {
|
||||
this.script = script || null;
|
||||
}
|
||||
else {
|
||||
this.script = null;
|
||||
options = script;
|
||||
}
|
||||
|
||||
this.workers = []; // queue with all workers
|
||||
this.tasks = []; // queue with tasks awaiting execution
|
||||
|
||||
options = options || {};
|
||||
|
||||
this.forkArgs = options.forkArgs || [];
|
||||
this.forkOpts = options.forkOpts || {};
|
||||
this.debugPortStart = (options.debugPortStart || 43210);
|
||||
this.nodeWorker = options.nodeWorker;
|
||||
this.workerType = options.workerType || options.nodeWorker || 'auto'
|
||||
this.maxQueueSize = options.maxQueueSize || Infinity;
|
||||
|
||||
// configuration
|
||||
if (options && 'maxWorkers' in options) {
|
||||
validateMaxWorkers(options.maxWorkers);
|
||||
this.maxWorkers = options.maxWorkers;
|
||||
}
|
||||
else {
|
||||
this.maxWorkers = Math.max((environment.cpus || 4) - 1, 1);
|
||||
}
|
||||
|
||||
if (options && 'minWorkers' in options) {
|
||||
if(options.minWorkers === 'max') {
|
||||
this.minWorkers = this.maxWorkers;
|
||||
} else {
|
||||
validateMinWorkers(options.minWorkers);
|
||||
this.minWorkers = options.minWorkers;
|
||||
this.maxWorkers = Math.max(this.minWorkers, this.maxWorkers); // in case minWorkers is higher than maxWorkers
|
||||
}
|
||||
this._ensureMinWorkers();
|
||||
}
|
||||
|
||||
this._boundNext = this._next.bind(this);
|
||||
|
||||
|
||||
if (this.workerType === 'thread') {
|
||||
WorkerHandler.ensureWorkerThreads();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Execute a function on a worker.
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* var pool = new Pool()
|
||||
*
|
||||
* // call a function available on the worker
|
||||
* pool.exec('fibonacci', [6])
|
||||
*
|
||||
* // offload a function
|
||||
* function add(a, b) {
|
||||
* return a + b
|
||||
* };
|
||||
* pool.exec(add, [2, 4])
|
||||
* .then(function (result) {
|
||||
* console.log(result); // outputs 6
|
||||
* })
|
||||
* .catch(function(error) {
|
||||
* console.log(error);
|
||||
* });
|
||||
*
|
||||
* @param {String | Function} method Function name or function.
|
||||
* If `method` is a string, the corresponding
|
||||
* method on the worker will be executed
|
||||
* If `method` is a Function, the function
|
||||
* will be stringified and executed via the
|
||||
* workers built-in function `run(fn, args)`.
|
||||
* @param {Array} [params] Function arguments applied when calling the function
|
||||
* @param {ExecOptions} [options] Options object
|
||||
* @return {Promise.<*, Error>} result
|
||||
*/
|
||||
Pool.prototype.exec = function (method, params, options) {
|
||||
// validate type of arguments
|
||||
if (params && !Array.isArray(params)) {
|
||||
throw new TypeError('Array expected as argument "params"');
|
||||
}
|
||||
|
||||
if (typeof method === 'string') {
|
||||
var resolver = Promise.defer();
|
||||
|
||||
if (this.tasks.length >= this.maxQueueSize) {
|
||||
throw new Error('Max queue size of ' + this.maxQueueSize + ' reached');
|
||||
}
|
||||
|
||||
// add a new task to the queue
|
||||
var tasks = this.tasks;
|
||||
var task = {
|
||||
method: method,
|
||||
params: params,
|
||||
resolver: resolver,
|
||||
timeout: null,
|
||||
options: options
|
||||
};
|
||||
tasks.push(task);
|
||||
|
||||
// replace the timeout method of the Promise with our own,
|
||||
// which starts the timer as soon as the task is actually started
|
||||
var originalTimeout = resolver.promise.timeout;
|
||||
resolver.promise.timeout = function timeout (delay) {
|
||||
if (tasks.indexOf(task) !== -1) {
|
||||
// task is still queued -> start the timer later on
|
||||
task.timeout = delay;
|
||||
return resolver.promise;
|
||||
}
|
||||
else {
|
||||
// task is already being executed -> start timer immediately
|
||||
return originalTimeout.call(resolver.promise, delay);
|
||||
}
|
||||
};
|
||||
|
||||
// trigger task execution
|
||||
this._next();
|
||||
|
||||
return resolver.promise;
|
||||
}
|
||||
else if (typeof method === 'function') {
|
||||
// send stringified function and function arguments to worker
|
||||
return this.exec('run', [String(method), params]);
|
||||
}
|
||||
else {
|
||||
throw new TypeError('Function or string expected as argument "method"');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a proxy for current worker. Returns an object containing all
|
||||
* methods available on the worker. The methods always return a promise.
|
||||
*
|
||||
* @return {Promise.<Object, Error>} proxy
|
||||
*/
|
||||
Pool.prototype.proxy = function () {
|
||||
if (arguments.length > 0) {
|
||||
throw new Error('No arguments expected');
|
||||
}
|
||||
|
||||
var pool = this;
|
||||
return this.exec('methods')
|
||||
.then(function (methods) {
|
||||
var proxy = {};
|
||||
|
||||
methods.forEach(function (method) {
|
||||
proxy[method] = function () {
|
||||
return pool.exec(method, Array.prototype.slice.call(arguments));
|
||||
}
|
||||
});
|
||||
|
||||
return proxy;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates new array with the results of calling a provided callback function
|
||||
* on every element in this array.
|
||||
* @param {Array} array
|
||||
* @param {function} callback Function taking two arguments:
|
||||
* `callback(currentValue, index)`
|
||||
* @return {Promise.<Array>} Returns a promise which resolves with an Array
|
||||
* containing the results of the callback function
|
||||
* executed for each of the array elements.
|
||||
*/
|
||||
/* TODO: implement map
|
||||
Pool.prototype.map = function (array, callback) {
|
||||
};
|
||||
*/
|
||||
|
||||
/**
|
||||
* Grab the first task from the queue, find a free worker, and assign the
|
||||
* worker to the task.
|
||||
* @protected
|
||||
*/
|
||||
Pool.prototype._next = function () {
|
||||
if (this.tasks.length > 0) {
|
||||
// there are tasks in the queue
|
||||
|
||||
// find an available worker
|
||||
var worker = this._getWorker();
|
||||
if (worker) {
|
||||
// get the first task from the queue
|
||||
var me = this;
|
||||
var task = this.tasks.shift();
|
||||
|
||||
// check if the task is still pending (and not cancelled -> promise rejected)
|
||||
if (task.resolver.promise.pending) {
|
||||
// send the request to the worker
|
||||
var promise = worker.exec(task.method, task.params, task.resolver, task.options)
|
||||
.then(me._boundNext)
|
||||
.catch(function () {
|
||||
// if the worker crashed and terminated, remove it from the pool
|
||||
if (worker.terminated) {
|
||||
return me._removeWorker(worker);
|
||||
}
|
||||
}).then(function() {
|
||||
me._next(); // trigger next task in the queue
|
||||
});
|
||||
|
||||
// start queued timer now
|
||||
if (typeof task.timeout === 'number') {
|
||||
promise.timeout(task.timeout);
|
||||
}
|
||||
} else {
|
||||
// The task taken was already complete (either rejected or resolved), so just trigger next task in the queue
|
||||
me._next();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get an available worker. If no worker is available and the maximum number
|
||||
* of workers isn't yet reached, a new worker will be created and returned.
|
||||
* If no worker is available and the maximum number of workers is reached,
|
||||
* null will be returned.
|
||||
*
|
||||
* @return {WorkerHandler | null} worker
|
||||
* @private
|
||||
*/
|
||||
Pool.prototype._getWorker = function() {
|
||||
// find a non-busy worker
|
||||
var workers = this.workers;
|
||||
for (var i = 0; i < workers.length; i++) {
|
||||
var worker = workers[i];
|
||||
if (worker.busy() === false) {
|
||||
return worker;
|
||||
}
|
||||
}
|
||||
|
||||
if (workers.length < this.maxWorkers) {
|
||||
// create a new worker
|
||||
worker = this._createWorkerHandler();
|
||||
workers.push(worker);
|
||||
return worker;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a worker from the pool.
|
||||
* Attempts to terminate worker if not already terminated, and ensures the minimum
|
||||
* pool size is met.
|
||||
* @param {WorkerHandler} worker
|
||||
* @return {Promise<WorkerHandler>}
|
||||
* @protected
|
||||
*/
|
||||
Pool.prototype._removeWorker = function(worker) {
|
||||
DEBUG_PORT_ALLOCATOR.releasePort(worker.debugPort);
|
||||
// _removeWorker will call this, but we need it to be removed synchronously
|
||||
this._removeWorkerFromList(worker);
|
||||
// If minWorkers set, spin up new workers to replace the crashed ones
|
||||
this._ensureMinWorkers();
|
||||
// terminate the worker (if not already terminated)
|
||||
return new Promise(function(resolve, reject) {
|
||||
worker.terminate(false, function(err) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(worker);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a worker from the pool list.
|
||||
* @param {WorkerHandler} worker
|
||||
* @protected
|
||||
*/
|
||||
Pool.prototype._removeWorkerFromList = function(worker) {
|
||||
// remove from the list with workers
|
||||
var index = this.workers.indexOf(worker);
|
||||
if (index !== -1) {
|
||||
this.workers.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Close all active workers. Tasks currently being executed will be finished first.
|
||||
* @param {boolean} [force=false] If false (default), the workers are terminated
|
||||
* after finishing all tasks currently in
|
||||
* progress. If true, the workers will be
|
||||
* terminated immediately.
|
||||
* @param {number} [timeout] If provided and non-zero, worker termination promise will be rejected
|
||||
* after timeout if worker process has not been terminated.
|
||||
* @return {Promise.<void, Error>}
|
||||
*/
|
||||
Pool.prototype.terminate = function (force, timeout) {
|
||||
// cancel any pending tasks
|
||||
this.tasks.forEach(function (task) {
|
||||
task.resolver.reject(new Error('Pool terminated'));
|
||||
});
|
||||
this.tasks.length = 0;
|
||||
|
||||
var f = function (worker) {
|
||||
this._removeWorkerFromList(worker);
|
||||
};
|
||||
var removeWorker = f.bind(this);
|
||||
|
||||
var promises = [];
|
||||
var workers = this.workers.slice();
|
||||
workers.forEach(function (worker) {
|
||||
var termPromise = worker.terminateAndNotify(force, timeout)
|
||||
.then(removeWorker);
|
||||
promises.push(termPromise);
|
||||
});
|
||||
return Promise.all(promises);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve statistics on tasks and workers.
|
||||
* @return {{totalWorkers: number, busyWorkers: number, idleWorkers: number, pendingTasks: number, activeTasks: number}} Returns an object with statistics
|
||||
*/
|
||||
Pool.prototype.stats = function () {
|
||||
var totalWorkers = this.workers.length;
|
||||
var busyWorkers = this.workers.filter(function (worker) {
|
||||
return worker.busy();
|
||||
}).length;
|
||||
|
||||
return {
|
||||
totalWorkers: totalWorkers,
|
||||
busyWorkers: busyWorkers,
|
||||
idleWorkers: totalWorkers - busyWorkers,
|
||||
|
||||
pendingTasks: this.tasks.length,
|
||||
activeTasks: busyWorkers
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Ensures that a minimum of minWorkers is up and running
|
||||
* @protected
|
||||
*/
|
||||
Pool.prototype._ensureMinWorkers = function() {
|
||||
if (this.minWorkers) {
|
||||
for(var i = this.workers.length; i < this.minWorkers; i++) {
|
||||
this.workers.push(this._createWorkerHandler());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function to create a new WorkerHandler and pass all options.
|
||||
* @return {WorkerHandler}
|
||||
* @private
|
||||
*/
|
||||
Pool.prototype._createWorkerHandler = function () {
|
||||
return new WorkerHandler(this.script, {
|
||||
forkArgs: this.forkArgs,
|
||||
forkOpts: this.forkOpts,
|
||||
debugPort: DEBUG_PORT_ALLOCATOR.nextAvailableStartingAt(this.debugPortStart),
|
||||
workerType: this.workerType
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the maxWorkers option is an integer >= 1
|
||||
* @param {*} maxWorkers
|
||||
* @returns {boolean} returns true maxWorkers has a valid value
|
||||
*/
|
||||
function validateMaxWorkers(maxWorkers) {
|
||||
if (!isNumber(maxWorkers) || !isInteger(maxWorkers) || maxWorkers < 1) {
|
||||
throw new TypeError('Option maxWorkers must be an integer number >= 1');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the minWorkers option is an integer >= 0
|
||||
* @param {*} minWorkers
|
||||
* @returns {boolean} returns true when minWorkers has a valid value
|
||||
*/
|
||||
function validateMinWorkers(minWorkers) {
|
||||
if (!isNumber(minWorkers) || !isInteger(minWorkers) || minWorkers < 0) {
|
||||
throw new TypeError('Option minWorkers must be an integer number >= 0');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether a variable is a number
|
||||
* @param {*} value
|
||||
* @returns {boolean} returns true when value is a number
|
||||
*/
|
||||
function isNumber(value) {
|
||||
return typeof value === 'number';
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether a number is an integer
|
||||
* @param {number} value
|
||||
* @returns {boolean} Returns true if value is an integer
|
||||
*/
|
||||
function isInteger(value) {
|
||||
return Math.round(value) == value;
|
||||
}
|
||||
|
||||
module.exports = Pool;
|
279
node_modules/workerpool/src/Promise.js
generated
vendored
Normal file
279
node_modules/workerpool/src/Promise.js
generated
vendored
Normal file
@@ -0,0 +1,279 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Promise
|
||||
*
|
||||
* Inspired by https://gist.github.com/RubaXa/8501359 from RubaXa <trash@rubaxa.org>
|
||||
*
|
||||
* @param {Function} handler Called as handler(resolve: Function, reject: Function)
|
||||
* @param {Promise} [parent] Parent promise for propagation of cancel and timeout
|
||||
*/
|
||||
function Promise(handler, parent) {
|
||||
var me = this;
|
||||
|
||||
if (!(this instanceof Promise)) {
|
||||
throw new SyntaxError('Constructor must be called with the new operator');
|
||||
}
|
||||
|
||||
if (typeof handler !== 'function') {
|
||||
throw new SyntaxError('Function parameter handler(resolve, reject) missing');
|
||||
}
|
||||
|
||||
var _onSuccess = [];
|
||||
var _onFail = [];
|
||||
|
||||
// status
|
||||
this.resolved = false;
|
||||
this.rejected = false;
|
||||
this.pending = true;
|
||||
|
||||
/**
|
||||
* Process onSuccess and onFail callbacks: add them to the queue.
|
||||
* Once the promise is resolve, the function _promise is replace.
|
||||
* @param {Function} onSuccess
|
||||
* @param {Function} onFail
|
||||
* @private
|
||||
*/
|
||||
var _process = function (onSuccess, onFail) {
|
||||
_onSuccess.push(onSuccess);
|
||||
_onFail.push(onFail);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add an onSuccess callback and optionally an onFail callback to the Promise
|
||||
* @param {Function} onSuccess
|
||||
* @param {Function} [onFail]
|
||||
* @returns {Promise} promise
|
||||
*/
|
||||
this.then = function (onSuccess, onFail) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var s = onSuccess ? _then(onSuccess, resolve, reject) : resolve;
|
||||
var f = onFail ? _then(onFail, resolve, reject) : reject;
|
||||
|
||||
_process(s, f);
|
||||
}, me);
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolve the promise
|
||||
* @param {*} result
|
||||
* @type {Function}
|
||||
*/
|
||||
var _resolve = function (result) {
|
||||
// update status
|
||||
me.resolved = true;
|
||||
me.rejected = false;
|
||||
me.pending = false;
|
||||
|
||||
_onSuccess.forEach(function (fn) {
|
||||
fn(result);
|
||||
});
|
||||
|
||||
_process = function (onSuccess, onFail) {
|
||||
onSuccess(result);
|
||||
};
|
||||
|
||||
_resolve = _reject = function () { };
|
||||
|
||||
return me;
|
||||
};
|
||||
|
||||
/**
|
||||
* Reject the promise
|
||||
* @param {Error} error
|
||||
* @type {Function}
|
||||
*/
|
||||
var _reject = function (error) {
|
||||
// update status
|
||||
me.resolved = false;
|
||||
me.rejected = true;
|
||||
me.pending = false;
|
||||
|
||||
_onFail.forEach(function (fn) {
|
||||
fn(error);
|
||||
});
|
||||
|
||||
_process = function (onSuccess, onFail) {
|
||||
onFail(error);
|
||||
};
|
||||
|
||||
_resolve = _reject = function () { }
|
||||
|
||||
return me;
|
||||
};
|
||||
|
||||
/**
|
||||
* Cancel te promise. This will reject the promise with a CancellationError
|
||||
* @returns {Promise} self
|
||||
*/
|
||||
this.cancel = function () {
|
||||
if (parent) {
|
||||
parent.cancel();
|
||||
}
|
||||
else {
|
||||
_reject(new CancellationError());
|
||||
}
|
||||
|
||||
return me;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set a timeout for the promise. If the promise is not resolved within
|
||||
* the time, the promise will be cancelled and a TimeoutError is thrown.
|
||||
* If the promise is resolved in time, the timeout is removed.
|
||||
* @param {number} delay Delay in milliseconds
|
||||
* @returns {Promise} self
|
||||
*/
|
||||
this.timeout = function (delay) {
|
||||
if (parent) {
|
||||
parent.timeout(delay);
|
||||
}
|
||||
else {
|
||||
var timer = setTimeout(function () {
|
||||
_reject(new TimeoutError('Promise timed out after ' + delay + ' ms'));
|
||||
}, delay);
|
||||
|
||||
me.always(function () {
|
||||
clearTimeout(timer);
|
||||
});
|
||||
}
|
||||
|
||||
return me;
|
||||
};
|
||||
|
||||
// attach handler passing the resolve and reject functions
|
||||
handler(function (result) {
|
||||
_resolve(result);
|
||||
}, function (error) {
|
||||
_reject(error);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute given callback, then call resolve/reject based on the returned result
|
||||
* @param {Function} callback
|
||||
* @param {Function} resolve
|
||||
* @param {Function} reject
|
||||
* @returns {Function}
|
||||
* @private
|
||||
*/
|
||||
function _then(callback, resolve, reject) {
|
||||
return function (result) {
|
||||
try {
|
||||
var res = callback(result);
|
||||
if (res && typeof res.then === 'function' && typeof res['catch'] === 'function') {
|
||||
// method returned a promise
|
||||
res.then(resolve, reject);
|
||||
}
|
||||
else {
|
||||
resolve(res);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an onFail callback to the Promise
|
||||
* @param {Function} onFail
|
||||
* @returns {Promise} promise
|
||||
*/
|
||||
Promise.prototype['catch'] = function (onFail) {
|
||||
return this.then(null, onFail);
|
||||
};
|
||||
|
||||
// TODO: add support for Promise.catch(Error, callback)
|
||||
// TODO: add support for Promise.catch(Error, Error, callback)
|
||||
|
||||
/**
|
||||
* Execute given callback when the promise either resolves or rejects.
|
||||
* @param {Function} fn
|
||||
* @returns {Promise} promise
|
||||
*/
|
||||
Promise.prototype.always = function (fn) {
|
||||
return this.then(fn, fn);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a promise which resolves when all provided promises are resolved,
|
||||
* and fails when any of the promises resolves.
|
||||
* @param {Promise[]} promises
|
||||
* @returns {Promise} promise
|
||||
*/
|
||||
Promise.all = function (promises){
|
||||
return new Promise(function (resolve, reject) {
|
||||
var remaining = promises.length,
|
||||
results = [];
|
||||
|
||||
if (remaining) {
|
||||
promises.forEach(function (p, i) {
|
||||
p.then(function (result) {
|
||||
results[i] = result;
|
||||
remaining--;
|
||||
if (remaining == 0) {
|
||||
resolve(results);
|
||||
}
|
||||
}, function (error) {
|
||||
remaining = 0;
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
resolve(results);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a promise resolver
|
||||
* @returns {{promise: Promise, resolve: Function, reject: Function}} resolver
|
||||
*/
|
||||
Promise.defer = function () {
|
||||
var resolver = {};
|
||||
|
||||
resolver.promise = new Promise(function (resolve, reject) {
|
||||
resolver.resolve = resolve;
|
||||
resolver.reject = reject;
|
||||
});
|
||||
|
||||
return resolver;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a cancellation error
|
||||
* @param {String} [message]
|
||||
* @extends Error
|
||||
*/
|
||||
function CancellationError(message) {
|
||||
this.message = message || 'promise cancelled';
|
||||
this.stack = (new Error()).stack;
|
||||
}
|
||||
|
||||
CancellationError.prototype = new Error();
|
||||
CancellationError.prototype.constructor = Error;
|
||||
CancellationError.prototype.name = 'CancellationError';
|
||||
|
||||
Promise.CancellationError = CancellationError;
|
||||
|
||||
|
||||
/**
|
||||
* Create a timeout error
|
||||
* @param {String} [message]
|
||||
* @extends Error
|
||||
*/
|
||||
function TimeoutError(message) {
|
||||
this.message = message || 'timeout exceeded';
|
||||
this.stack = (new Error()).stack;
|
||||
}
|
||||
|
||||
TimeoutError.prototype = new Error();
|
||||
TimeoutError.prototype.constructor = Error;
|
||||
TimeoutError.prototype.name = 'TimeoutError';
|
||||
|
||||
Promise.TimeoutError = TimeoutError;
|
||||
|
||||
|
||||
module.exports = Promise;
|
489
node_modules/workerpool/src/WorkerHandler.js
generated
vendored
Normal file
489
node_modules/workerpool/src/WorkerHandler.js
generated
vendored
Normal file
@@ -0,0 +1,489 @@
|
||||
'use strict';
|
||||
|
||||
var Promise = require('./Promise');
|
||||
var environment = require('./environment');
|
||||
var requireFoolWebpack = require('./requireFoolWebpack');
|
||||
|
||||
/**
|
||||
* Special message sent by parent which causes a child process worker to terminate itself.
|
||||
* Not a "message object"; this string is the entire message.
|
||||
*/
|
||||
var TERMINATE_METHOD_ID = '__workerpool-terminate__';
|
||||
|
||||
/**
|
||||
* If sending `TERMINATE_METHOD_ID` does not cause the child process to exit in this many milliseconds,
|
||||
* force-kill the child process.
|
||||
*/
|
||||
var CHILD_PROCESS_EXIT_TIMEOUT = 1000;
|
||||
|
||||
function ensureWorkerThreads() {
|
||||
var WorkerThreads = tryRequireWorkerThreads()
|
||||
if (!WorkerThreads) {
|
||||
throw new Error('WorkerPool: workerType = \'thread\' is not supported, Node >= 11.7.0 required')
|
||||
}
|
||||
|
||||
return WorkerThreads;
|
||||
}
|
||||
|
||||
// check whether Worker is supported by the browser
|
||||
function ensureWebWorker() {
|
||||
// Workaround for a bug in PhantomJS (Or QtWebkit): https://github.com/ariya/phantomjs/issues/14534
|
||||
if (typeof Worker !== 'function' && (typeof Worker !== 'object' || typeof Worker.prototype.constructor !== 'function')) {
|
||||
throw new Error('WorkerPool: Web Workers not supported');
|
||||
}
|
||||
}
|
||||
|
||||
function tryRequireWorkerThreads() {
|
||||
try {
|
||||
return requireFoolWebpack('worker_threads');
|
||||
} catch(error) {
|
||||
if (typeof error === 'object' && error !== null && error.code === 'MODULE_NOT_FOUND') {
|
||||
// no worker_threads available (old version of node.js)
|
||||
return null;
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get the default worker script
|
||||
function getDefaultWorker() {
|
||||
if (environment.platform === 'browser') {
|
||||
// test whether the browser supports all features that we need
|
||||
if (typeof Blob === 'undefined') {
|
||||
throw new Error('Blob not supported by the browser');
|
||||
}
|
||||
if (!window.URL || typeof window.URL.createObjectURL !== 'function') {
|
||||
throw new Error('URL.createObjectURL not supported by the browser');
|
||||
}
|
||||
|
||||
// use embedded worker.js
|
||||
var blob = new Blob([require('./generated/embeddedWorker')], {type: 'text/javascript'});
|
||||
return window.URL.createObjectURL(blob);
|
||||
}
|
||||
else {
|
||||
// use external worker.js in current directory
|
||||
return __dirname + '/worker.js';
|
||||
}
|
||||
}
|
||||
|
||||
function setupWorker(script, options) {
|
||||
if (options.workerType === 'web') { // browser only
|
||||
ensureWebWorker();
|
||||
return setupBrowserWorker(script, Worker);
|
||||
} else if (options.workerType === 'thread') { // node.js only
|
||||
WorkerThreads = ensureWorkerThreads();
|
||||
return setupWorkerThreadWorker(script, WorkerThreads);
|
||||
} else if (options.workerType === 'process' || !options.workerType) { // node.js only
|
||||
return setupProcessWorker(script, resolveForkOptions(options), requireFoolWebpack('child_process'));
|
||||
} else { // options.workerType === 'auto' or undefined
|
||||
if (environment.platform === 'browser') {
|
||||
ensureWebWorker();
|
||||
return setupBrowserWorker(script, Worker);
|
||||
}
|
||||
else { // environment.platform === 'node'
|
||||
var WorkerThreads = tryRequireWorkerThreads();
|
||||
if (WorkerThreads) {
|
||||
return setupWorkerThreadWorker(script, WorkerThreads);
|
||||
} else {
|
||||
return setupProcessWorker(script, resolveForkOptions(options), requireFoolWebpack('child_process'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setupBrowserWorker(script, Worker) {
|
||||
// create the web worker
|
||||
var worker = new Worker(script);
|
||||
|
||||
worker.isBrowserWorker = true;
|
||||
// add node.js API to the web worker
|
||||
worker.on = function (event, callback) {
|
||||
this.addEventListener(event, function (message) {
|
||||
callback(message.data);
|
||||
});
|
||||
};
|
||||
worker.send = function (message) {
|
||||
this.postMessage(message);
|
||||
};
|
||||
return worker;
|
||||
}
|
||||
|
||||
function setupWorkerThreadWorker(script, WorkerThreads) {
|
||||
var worker = new WorkerThreads.Worker(script, {
|
||||
stdout: false, // automatically pipe worker.STDOUT to process.STDOUT
|
||||
stderr: false // automatically pipe worker.STDERR to process.STDERR
|
||||
});
|
||||
worker.isWorkerThread = true;
|
||||
// make the worker mimic a child_process
|
||||
worker.send = function(message) {
|
||||
this.postMessage(message);
|
||||
};
|
||||
|
||||
worker.kill = function() {
|
||||
this.terminate();
|
||||
return true;
|
||||
};
|
||||
|
||||
worker.disconnect = function() {
|
||||
this.terminate();
|
||||
};
|
||||
|
||||
return worker;
|
||||
}
|
||||
|
||||
function setupProcessWorker(script, options, child_process) {
|
||||
// no WorkerThreads, fallback to sub-process based workers
|
||||
var worker = child_process.fork(
|
||||
script,
|
||||
options.forkArgs,
|
||||
options.forkOpts
|
||||
);
|
||||
|
||||
worker.isChildProcess = true;
|
||||
return worker;
|
||||
}
|
||||
|
||||
// add debug flags to child processes if the node inspector is active
|
||||
function resolveForkOptions(opts) {
|
||||
opts = opts || {};
|
||||
|
||||
var processExecArgv = process.execArgv.join(' ');
|
||||
var inspectorActive = processExecArgv.indexOf('--inspect') !== -1;
|
||||
var debugBrk = processExecArgv.indexOf('--debug-brk') !== -1;
|
||||
|
||||
var execArgv = [];
|
||||
if (inspectorActive) {
|
||||
execArgv.push('--inspect=' + opts.debugPort);
|
||||
|
||||
if (debugBrk) {
|
||||
execArgv.push('--debug-brk');
|
||||
}
|
||||
}
|
||||
|
||||
process.execArgv.forEach(function(arg) {
|
||||
if (arg.indexOf('--max-old-space-size') > -1) {
|
||||
execArgv.push(arg)
|
||||
}
|
||||
})
|
||||
|
||||
return Object.assign({}, opts, {
|
||||
forkArgs: opts.forkArgs,
|
||||
forkOpts: Object.assign({}, opts.forkOpts, {
|
||||
execArgv: (opts.forkOpts && opts.forkOpts.execArgv || [])
|
||||
.concat(execArgv)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a serialized error to Error
|
||||
* @param {Object} obj Error that has been serialized and parsed to object
|
||||
* @return {Error} The equivalent Error.
|
||||
*/
|
||||
function objectToError (obj) {
|
||||
var temp = new Error('')
|
||||
var props = Object.keys(obj)
|
||||
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
temp[props[i]] = obj[props[i]]
|
||||
}
|
||||
|
||||
return temp
|
||||
}
|
||||
|
||||
/**
|
||||
* A WorkerHandler controls a single worker. This worker can be a child process
|
||||
* on node.js or a WebWorker in a browser environment.
|
||||
* @param {String} [script] If no script is provided, a default worker with a
|
||||
* function run will be created.
|
||||
* @param {WorkerPoolOptions} _options See docs
|
||||
* @constructor
|
||||
*/
|
||||
function WorkerHandler(script, _options) {
|
||||
var me = this;
|
||||
var options = _options || {};
|
||||
|
||||
this.script = script || getDefaultWorker();
|
||||
this.worker = setupWorker(this.script, options);
|
||||
this.debugPort = options.debugPort;
|
||||
|
||||
// The ready message is only sent if the worker.add method is called (And the default script is not used)
|
||||
if (!script) {
|
||||
this.worker.ready = true;
|
||||
}
|
||||
|
||||
// queue for requests that are received before the worker is ready
|
||||
this.requestQueue = [];
|
||||
this.worker.on('message', function (response) {
|
||||
if (typeof response === 'string' && response === 'ready') {
|
||||
me.worker.ready = true;
|
||||
dispatchQueuedRequests();
|
||||
} else {
|
||||
// find the task from the processing queue, and run the tasks callback
|
||||
var id = response.id;
|
||||
var task = me.processing[id];
|
||||
if (task !== undefined) {
|
||||
if (response.isEvent) {
|
||||
if (task.options && typeof task.options.on === 'function') {
|
||||
task.options.on(response.payload);
|
||||
}
|
||||
} else {
|
||||
// remove the task from the queue
|
||||
delete me.processing[id];
|
||||
|
||||
// test if we need to terminate
|
||||
if (me.terminating === true) {
|
||||
// complete worker termination if all tasks are finished
|
||||
me.terminate();
|
||||
}
|
||||
|
||||
// resolve the task's promise
|
||||
if (response.error) {
|
||||
task.resolver.reject(objectToError(response.error));
|
||||
}
|
||||
else {
|
||||
task.resolver.resolve(response.result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// reject all running tasks on worker error
|
||||
function onError(error) {
|
||||
me.terminated = true;
|
||||
|
||||
for (var id in me.processing) {
|
||||
if (me.processing[id] !== undefined) {
|
||||
me.processing[id].resolver.reject(error);
|
||||
}
|
||||
}
|
||||
me.processing = Object.create(null);
|
||||
}
|
||||
|
||||
// send all queued requests to worker
|
||||
function dispatchQueuedRequests()
|
||||
{
|
||||
for(const request of me.requestQueue.splice(0)) {
|
||||
me.worker.send(request);
|
||||
}
|
||||
}
|
||||
|
||||
var worker = this.worker;
|
||||
// listen for worker messages error and exit
|
||||
this.worker.on('error', onError);
|
||||
this.worker.on('exit', function (exitCode, signalCode) {
|
||||
var message = 'Workerpool Worker terminated Unexpectedly\n';
|
||||
|
||||
message += ' exitCode: `' + exitCode + '`\n';
|
||||
message += ' signalCode: `' + signalCode + '`\n';
|
||||
|
||||
message += ' workerpool.script: `' + me.script + '`\n';
|
||||
message += ' spawnArgs: `' + worker.spawnargs + '`\n';
|
||||
message += ' spawnfile: `' + worker.spawnfile + '`\n'
|
||||
|
||||
message += ' stdout: `' + worker.stdout + '`\n'
|
||||
message += ' stderr: `' + worker.stderr + '`\n'
|
||||
|
||||
onError(new Error(message));
|
||||
});
|
||||
|
||||
this.processing = Object.create(null); // queue with tasks currently in progress
|
||||
|
||||
this.terminating = false;
|
||||
this.terminated = false;
|
||||
this.terminationHandler = null;
|
||||
this.lastId = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list with methods available on the worker.
|
||||
* @return {Promise.<String[], Error>} methods
|
||||
*/
|
||||
WorkerHandler.prototype.methods = function () {
|
||||
return this.exec('methods');
|
||||
};
|
||||
|
||||
/**
|
||||
* Execute a method with given parameters on the worker
|
||||
* @param {String} method
|
||||
* @param {Array} [params]
|
||||
* @param {{resolve: Function, reject: Function}} [resolver]
|
||||
* @param {ExecOptions} [options]
|
||||
* @return {Promise.<*, Error>} result
|
||||
*/
|
||||
WorkerHandler.prototype.exec = function(method, params, resolver, options) {
|
||||
if (!resolver) {
|
||||
resolver = Promise.defer();
|
||||
}
|
||||
|
||||
// generate a unique id for the task
|
||||
var id = ++this.lastId;
|
||||
|
||||
// register a new task as being in progress
|
||||
this.processing[id] = {
|
||||
id: id,
|
||||
resolver: resolver,
|
||||
options: options
|
||||
};
|
||||
|
||||
// build a JSON-RPC request
|
||||
var request = {
|
||||
id: id,
|
||||
method: method,
|
||||
params: params
|
||||
};
|
||||
|
||||
if (this.terminated) {
|
||||
resolver.reject(new Error('Worker is terminated'));
|
||||
} else if (this.worker.ready) {
|
||||
// send the request to the worker
|
||||
this.worker.send(request);
|
||||
} else {
|
||||
this.requestQueue.push(request);
|
||||
}
|
||||
|
||||
// on cancellation, force the worker to terminate
|
||||
var me = this;
|
||||
return resolver.promise.catch(function (error) {
|
||||
if (error instanceof Promise.CancellationError || error instanceof Promise.TimeoutError) {
|
||||
// remove this task from the queue. It is already rejected (hence this
|
||||
// catch event), and else it will be rejected again when terminating
|
||||
delete me.processing[id];
|
||||
|
||||
// terminate worker
|
||||
return me.terminateAndNotify(true)
|
||||
.then(function() {
|
||||
throw error;
|
||||
}, function(err) {
|
||||
throw err;
|
||||
});
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the worker is working or not
|
||||
* @return {boolean} Returns true if the worker is busy
|
||||
*/
|
||||
WorkerHandler.prototype.busy = function () {
|
||||
return Object.keys(this.processing).length > 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Terminate the worker.
|
||||
* @param {boolean} [force=false] If false (default), the worker is terminated
|
||||
* after finishing all tasks currently in
|
||||
* progress. If true, the worker will be
|
||||
* terminated immediately.
|
||||
* @param {function} [callback=null] If provided, will be called when process terminates.
|
||||
*/
|
||||
WorkerHandler.prototype.terminate = function (force, callback) {
|
||||
var me = this;
|
||||
if (force) {
|
||||
// cancel all tasks in progress
|
||||
for (var id in this.processing) {
|
||||
if (this.processing[id] !== undefined) {
|
||||
this.processing[id].resolver.reject(new Error('Worker terminated'));
|
||||
}
|
||||
}
|
||||
this.processing = Object.create(null);
|
||||
}
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
this.terminationHandler = callback;
|
||||
}
|
||||
if (!this.busy()) {
|
||||
// all tasks are finished. kill the worker
|
||||
var cleanup = function(err) {
|
||||
me.terminated = true;
|
||||
me.worker = null;
|
||||
me.terminating = false;
|
||||
if (me.terminationHandler) {
|
||||
me.terminationHandler(err, me);
|
||||
} else if (err) {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.worker) {
|
||||
if (typeof this.worker.kill === 'function') {
|
||||
if (this.worker.killed) {
|
||||
cleanup(new Error('worker already killed!'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.worker.isChildProcess) {
|
||||
var cleanExitTimeout = setTimeout(function() {
|
||||
me.worker.kill();
|
||||
}, CHILD_PROCESS_EXIT_TIMEOUT);
|
||||
|
||||
this.worker.once('exit', function() {
|
||||
clearTimeout(cleanExitTimeout);
|
||||
me.worker.killed = true;
|
||||
cleanup();
|
||||
});
|
||||
|
||||
if (this.worker.ready) {
|
||||
this.worker.send(TERMINATE_METHOD_ID);
|
||||
} else {
|
||||
this.worker.requestQueue.push(TERMINATE_METHOD_ID)
|
||||
}
|
||||
} else {
|
||||
// worker_thread
|
||||
this.worker.kill();
|
||||
this.worker.killed = true;
|
||||
cleanup();
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (typeof this.worker.terminate === 'function') {
|
||||
this.worker.terminate(); // web worker
|
||||
this.worker.killed = true;
|
||||
}
|
||||
else {
|
||||
throw new Error('Failed to terminate worker');
|
||||
}
|
||||
}
|
||||
cleanup();
|
||||
}
|
||||
else {
|
||||
// we can't terminate immediately, there are still tasks being executed
|
||||
this.terminating = true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Terminate the worker, returning a Promise that resolves when the termination has been done.
|
||||
* @param {boolean} [force=false] If false (default), the worker is terminated
|
||||
* after finishing all tasks currently in
|
||||
* progress. If true, the worker will be
|
||||
* terminated immediately.
|
||||
* @param {number} [timeout] If provided and non-zero, worker termination promise will be rejected
|
||||
* after timeout if worker process has not been terminated.
|
||||
* @return {Promise.<WorkerHandler, Error>}
|
||||
*/
|
||||
WorkerHandler.prototype.terminateAndNotify = function (force, timeout) {
|
||||
var resolver = Promise.defer();
|
||||
if (timeout) {
|
||||
resolver.promise.timeout = timeout;
|
||||
}
|
||||
this.terminate(force, function(err, worker) {
|
||||
if (err) {
|
||||
resolver.reject(err);
|
||||
} else {
|
||||
resolver.resolve(worker);
|
||||
}
|
||||
});
|
||||
return resolver.promise;
|
||||
};
|
||||
|
||||
module.exports = WorkerHandler;
|
||||
module.exports._tryRequireWorkerThreads = tryRequireWorkerThreads;
|
||||
module.exports._setupProcessWorker = setupProcessWorker;
|
||||
module.exports._setupBrowserWorker = setupBrowserWorker;
|
||||
module.exports._setupWorkerThreadWorker = setupWorkerThreadWorker;
|
||||
module.exports.ensureWorkerThreads = ensureWorkerThreads;
|
28
node_modules/workerpool/src/debug-port-allocator.js
generated
vendored
Normal file
28
node_modules/workerpool/src/debug-port-allocator.js
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
'use strict';
|
||||
|
||||
var MAX_PORTS = 65535;
|
||||
module.exports = DebugPortAllocator;
|
||||
function DebugPortAllocator() {
|
||||
this.ports = Object.create(null);
|
||||
this.length = 0;
|
||||
}
|
||||
|
||||
DebugPortAllocator.prototype.nextAvailableStartingAt = function(starting) {
|
||||
while (this.ports[starting] === true) {
|
||||
starting++;
|
||||
}
|
||||
|
||||
if (starting >= MAX_PORTS) {
|
||||
throw new Error('WorkerPool debug port limit reached: ' + starting + '>= ' + MAX_PORTS );
|
||||
}
|
||||
|
||||
this.ports[starting] = true;
|
||||
this.length++;
|
||||
return starting;
|
||||
};
|
||||
|
||||
DebugPortAllocator.prototype.releasePort = function(port) {
|
||||
delete this.ports[port];
|
||||
this.length--;
|
||||
};
|
||||
|
36
node_modules/workerpool/src/environment.js
generated
vendored
Normal file
36
node_modules/workerpool/src/environment.js
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
var requireFoolWebpack = require('./requireFoolWebpack');
|
||||
|
||||
// source: https://github.com/flexdinesh/browser-or-node
|
||||
var isNode = function (nodeProcess) {
|
||||
return (
|
||||
typeof nodeProcess !== 'undefined' &&
|
||||
nodeProcess.versions != null &&
|
||||
nodeProcess.versions.node != null
|
||||
);
|
||||
}
|
||||
module.exports.isNode = isNode
|
||||
|
||||
// determines the JavaScript platform: browser or node
|
||||
module.exports.platform = typeof process !== 'undefined' && isNode(process)
|
||||
? 'node'
|
||||
: 'browser';
|
||||
|
||||
// determines whether the code is running in main thread or not
|
||||
// note that in node.js we have to check both worker_thread and child_process
|
||||
var worker_threads = tryRequireFoolWebpack('worker_threads');
|
||||
module.exports.isMainThread = module.exports.platform === 'node'
|
||||
? ((!worker_threads || worker_threads.isMainThread) && !process.connected)
|
||||
: typeof Window !== 'undefined';
|
||||
|
||||
// determines the number of cpus available
|
||||
module.exports.cpus = module.exports.platform === 'browser'
|
||||
? self.navigator.hardwareConcurrency
|
||||
: requireFoolWebpack('os').cpus().length;
|
||||
|
||||
function tryRequireFoolWebpack (module) {
|
||||
try {
|
||||
return requireFoolWebpack(module);
|
||||
} catch(err) {
|
||||
return null
|
||||
}
|
||||
}
|
6
node_modules/workerpool/src/generated/embeddedWorker.js
generated
vendored
Normal file
6
node_modules/workerpool/src/generated/embeddedWorker.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
24
node_modules/workerpool/src/header.js
generated
vendored
Normal file
24
node_modules/workerpool/src/header.js
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* workerpool.js
|
||||
* https://github.com/josdejong/workerpool
|
||||
*
|
||||
* Offload tasks to a pool of workers on node.js and in the browser.
|
||||
*
|
||||
* @version @@version
|
||||
* @date @@date
|
||||
*
|
||||
* @license
|
||||
* Copyright (C) 2014-2020 Jos de Jong <wjosdejong@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy
|
||||
* of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
41
node_modules/workerpool/src/index.js
generated
vendored
Normal file
41
node_modules/workerpool/src/index.js
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
var environment = require('./environment');
|
||||
|
||||
/**
|
||||
* Create a new worker pool
|
||||
* @param {string} [script]
|
||||
* @param {WorkerPoolOptions} [options]
|
||||
* @returns {Pool} pool
|
||||
*/
|
||||
exports.pool = function pool(script, options) {
|
||||
var Pool = require('./Pool');
|
||||
|
||||
return new Pool(script, options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a worker and optionally register a set of methods to the worker.
|
||||
* @param {Object} [methods]
|
||||
*/
|
||||
exports.worker = function worker(methods) {
|
||||
var worker = require('./worker');
|
||||
worker.add(methods);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sends an event to the parent worker pool.
|
||||
* @param {any} payload
|
||||
*/
|
||||
exports.workerEmit = function workerEmit(payload) {
|
||||
var worker = require('./worker');
|
||||
worker.emit(payload);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a promise.
|
||||
* @type {Promise} promise
|
||||
*/
|
||||
exports.Promise = require('./Promise');
|
||||
|
||||
exports.platform = environment.platform;
|
||||
exports.isMainThread = environment.isMainThread;
|
||||
exports.cpus = environment.cpus;
|
8
node_modules/workerpool/src/requireFoolWebpack.js
generated
vendored
Normal file
8
node_modules/workerpool/src/requireFoolWebpack.js
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
// source of inspiration: https://github.com/sindresorhus/require-fool-webpack
|
||||
var requireFoolWebpack = eval(
|
||||
'typeof require !== \'undefined\' ' +
|
||||
'? require ' +
|
||||
': function (module) { throw new Error(\'Module " + module + " not found.\') }'
|
||||
);
|
||||
|
||||
module.exports = requireFoolWebpack;
|
15
node_modules/workerpool/src/types.js
generated
vendored
Normal file
15
node_modules/workerpool/src/types.js
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* @typedef {Object} WorkerPoolOptions
|
||||
* @property {number | 'max'} [minWorkers]
|
||||
* @property {number} [maxWorkers]
|
||||
* @property {number} [maxQueueSize]
|
||||
* @property {'auto' | 'web' | 'process' | 'thread'} [workerType]
|
||||
* @property {*} [forkArgs]
|
||||
* @property {*} [forkOpts]
|
||||
* @property {number} [debugPortStart]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} ExecOptions
|
||||
* @property {(payload: any) => unknown} [on]
|
||||
*/
|
202
node_modules/workerpool/src/worker.js
generated
vendored
Normal file
202
node_modules/workerpool/src/worker.js
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
/**
|
||||
* worker must be started as a child process or a web worker.
|
||||
* It listens for RPC messages from the parent process.
|
||||
*/
|
||||
|
||||
// source of inspiration: https://github.com/sindresorhus/require-fool-webpack
|
||||
var requireFoolWebpack = eval(
|
||||
'typeof require !== \'undefined\'' +
|
||||
' ? require' +
|
||||
' : function (module) { throw new Error(\'Module " + module + " not found.\') }'
|
||||
);
|
||||
|
||||
/**
|
||||
* Special message sent by parent which causes the worker to terminate itself.
|
||||
* Not a "message object"; this string is the entire message.
|
||||
*/
|
||||
var TERMINATE_METHOD_ID = '__workerpool-terminate__';
|
||||
|
||||
// var nodeOSPlatform = require('./environment').nodeOSPlatform;
|
||||
|
||||
// create a worker API for sending and receiving messages which works both on
|
||||
// node.js and in the browser
|
||||
var worker = {
|
||||
exit: function() {}
|
||||
};
|
||||
if (typeof self !== 'undefined' && typeof postMessage === 'function' && typeof addEventListener === 'function') {
|
||||
// worker in the browser
|
||||
worker.on = function (event, callback) {
|
||||
addEventListener(event, function (message) {
|
||||
callback(message.data);
|
||||
})
|
||||
};
|
||||
worker.send = function (message) {
|
||||
postMessage(message);
|
||||
};
|
||||
}
|
||||
else if (typeof process !== 'undefined') {
|
||||
// node.js
|
||||
|
||||
var WorkerThreads;
|
||||
try {
|
||||
WorkerThreads = requireFoolWebpack('worker_threads');
|
||||
} catch(error) {
|
||||
if (typeof error === 'object' && error !== null && error.code === 'MODULE_NOT_FOUND') {
|
||||
// no worker_threads, fallback to sub-process based workers
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
if (WorkerThreads &&
|
||||
/* if there is a parentPort, we are in a WorkerThread */
|
||||
WorkerThreads.parentPort !== null) {
|
||||
var parentPort = WorkerThreads.parentPort;
|
||||
worker.send = parentPort.postMessage.bind(parentPort);
|
||||
worker.on = parentPort.on.bind(parentPort);
|
||||
} else {
|
||||
worker.on = process.on.bind(process);
|
||||
worker.send = process.send.bind(process);
|
||||
// register disconnect handler only for subprocess worker to exit when parent is killed unexpectedly
|
||||
worker.on('disconnect', function () {
|
||||
process.exit(1);
|
||||
});
|
||||
worker.exit = process.exit.bind(process);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new Error('Script must be executed as a worker');
|
||||
}
|
||||
|
||||
function convertError(error) {
|
||||
return Object.getOwnPropertyNames(error).reduce(function(product, name) {
|
||||
return Object.defineProperty(product, name, {
|
||||
value: error[name],
|
||||
enumerable: true
|
||||
});
|
||||
}, {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether a value is a Promise via duck typing.
|
||||
* @param {*} value
|
||||
* @returns {boolean} Returns true when given value is an object
|
||||
* having functions `then` and `catch`.
|
||||
*/
|
||||
function isPromise(value) {
|
||||
return value && (typeof value.then === 'function') && (typeof value.catch === 'function');
|
||||
}
|
||||
|
||||
// functions available externally
|
||||
worker.methods = {};
|
||||
|
||||
/**
|
||||
* Execute a function with provided arguments
|
||||
* @param {String} fn Stringified function
|
||||
* @param {Array} [args] Function arguments
|
||||
* @returns {*}
|
||||
*/
|
||||
worker.methods.run = function run(fn, args) {
|
||||
var f = new Function('return (' + fn + ').apply(null, arguments);');
|
||||
return f.apply(f, args);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a list with methods available on this worker
|
||||
* @return {String[]} methods
|
||||
*/
|
||||
worker.methods.methods = function methods() {
|
||||
return Object.keys(worker.methods);
|
||||
};
|
||||
|
||||
var currentRequestId = null;
|
||||
|
||||
worker.on('message', function (request) {
|
||||
if (request === TERMINATE_METHOD_ID) {
|
||||
return worker.exit(0);
|
||||
}
|
||||
try {
|
||||
var method = worker.methods[request.method];
|
||||
|
||||
if (method) {
|
||||
currentRequestId = request.id;
|
||||
|
||||
// execute the function
|
||||
var result = method.apply(method, request.params);
|
||||
|
||||
if (isPromise(result)) {
|
||||
// promise returned, resolve this and then return
|
||||
result
|
||||
.then(function (result) {
|
||||
worker.send({
|
||||
id: request.id,
|
||||
result: result,
|
||||
error: null
|
||||
});
|
||||
currentRequestId = null;
|
||||
})
|
||||
.catch(function (err) {
|
||||
worker.send({
|
||||
id: request.id,
|
||||
result: null,
|
||||
error: convertError(err)
|
||||
});
|
||||
currentRequestId = null;
|
||||
});
|
||||
}
|
||||
else {
|
||||
// immediate result
|
||||
worker.send({
|
||||
id: request.id,
|
||||
result: result,
|
||||
error: null
|
||||
});
|
||||
|
||||
currentRequestId = null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new Error('Unknown method "' + request.method + '"');
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
worker.send({
|
||||
id: request.id,
|
||||
result: null,
|
||||
error: convertError(err)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Register methods to the worker
|
||||
* @param {Object} methods
|
||||
*/
|
||||
worker.register = function (methods) {
|
||||
|
||||
if (methods) {
|
||||
for (var name in methods) {
|
||||
if (methods.hasOwnProperty(name)) {
|
||||
worker.methods[name] = methods[name];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
worker.send('ready');
|
||||
|
||||
};
|
||||
|
||||
worker.emit = function (payload) {
|
||||
if (currentRequestId) {
|
||||
worker.send({
|
||||
id: currentRequestId,
|
||||
isEvent: true,
|
||||
payload
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (typeof exports !== 'undefined') {
|
||||
exports.add = worker.register;
|
||||
exports.emit = worker.emit;
|
||||
}
|
Reference in New Issue
Block a user