@xylabs/threads 3.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +21 -0
  3. package/README.md +227 -0
  4. package/dist/common.d.ts +4 -0
  5. package/dist/common.js +18 -0
  6. package/dist/index.d.ts +7 -0
  7. package/dist/index.js +27 -0
  8. package/dist/master/get-bundle-url.browser.d.ts +3 -0
  9. package/dist/master/get-bundle-url.browser.js +29 -0
  10. package/dist/master/implementation.browser.d.ts +4 -0
  11. package/dist/master/implementation.browser.js +69 -0
  12. package/dist/master/implementation.d.ts +6 -0
  13. package/dist/master/implementation.js +41 -0
  14. package/dist/master/implementation.node.d.ts +5 -0
  15. package/dist/master/implementation.node.js +255 -0
  16. package/dist/master/index.d.ts +13 -0
  17. package/dist/master/index.js +16 -0
  18. package/dist/master/invocation-proxy.d.ts +3 -0
  19. package/dist/master/invocation-proxy.js +130 -0
  20. package/dist/master/pool-types.d.ts +65 -0
  21. package/dist/master/pool-types.js +15 -0
  22. package/dist/master/pool.d.ts +90 -0
  23. package/dist/master/pool.js +281 -0
  24. package/dist/master/register.d.ts +1 -0
  25. package/dist/master/register.js +12 -0
  26. package/dist/master/spawn.d.ts +20 -0
  27. package/dist/master/spawn.js +130 -0
  28. package/dist/master/thread.d.ts +12 -0
  29. package/dist/master/thread.js +22 -0
  30. package/dist/observable-promise.d.ts +38 -0
  31. package/dist/observable-promise.js +156 -0
  32. package/dist/observable.d.ts +19 -0
  33. package/dist/observable.js +43 -0
  34. package/dist/ponyfills.d.ts +8 -0
  35. package/dist/ponyfills.js +22 -0
  36. package/dist/promise.d.ts +5 -0
  37. package/dist/promise.js +29 -0
  38. package/dist/serializers.d.ts +16 -0
  39. package/dist/serializers.js +41 -0
  40. package/dist/symbols.d.ts +5 -0
  41. package/dist/symbols.js +8 -0
  42. package/dist/transferable.d.ts +42 -0
  43. package/dist/transferable.js +28 -0
  44. package/dist/types/master.d.ts +99 -0
  45. package/dist/types/master.js +14 -0
  46. package/dist/types/messages.d.ts +62 -0
  47. package/dist/types/messages.js +20 -0
  48. package/dist/types/worker.d.ts +11 -0
  49. package/dist/types/worker.js +2 -0
  50. package/dist/worker/bundle-entry.d.ts +1 -0
  51. package/dist/worker/bundle-entry.js +27 -0
  52. package/dist/worker/implementation.browser.d.ts +7 -0
  53. package/dist/worker/implementation.browser.js +28 -0
  54. package/dist/worker/implementation.d.ts +3 -0
  55. package/dist/worker/implementation.js +24 -0
  56. package/dist/worker/implementation.tiny-worker.d.ts +7 -0
  57. package/dist/worker/implementation.tiny-worker.js +38 -0
  58. package/dist/worker/implementation.worker_threads.d.ts +8 -0
  59. package/dist/worker/implementation.worker_threads.js +42 -0
  60. package/dist/worker/index.d.ts +13 -0
  61. package/dist/worker/index.js +195 -0
  62. package/dist/worker_threads.d.ts +8 -0
  63. package/dist/worker_threads.js +17 -0
  64. package/dist-esm/common.js +12 -0
  65. package/dist-esm/index.js +6 -0
  66. package/dist-esm/master/get-bundle-url.browser.js +25 -0
  67. package/dist-esm/master/implementation.browser.js +64 -0
  68. package/dist-esm/master/implementation.js +15 -0
  69. package/dist-esm/master/implementation.node.js +224 -0
  70. package/dist-esm/master/index.js +9 -0
  71. package/dist-esm/master/invocation-proxy.js +122 -0
  72. package/dist-esm/master/pool-types.js +12 -0
  73. package/dist-esm/master/pool.js +273 -0
  74. package/dist-esm/master/register.js +10 -0
  75. package/dist-esm/master/spawn.js +123 -0
  76. package/dist-esm/master/thread.js +19 -0
  77. package/dist-esm/observable-promise.js +152 -0
  78. package/dist-esm/observable.js +38 -0
  79. package/dist-esm/ponyfills.js +18 -0
  80. package/dist-esm/promise.js +25 -0
  81. package/dist-esm/serializers.js +37 -0
  82. package/dist-esm/symbols.js +5 -0
  83. package/dist-esm/transferable.js +23 -0
  84. package/dist-esm/types/master.js +11 -0
  85. package/dist-esm/types/messages.js +17 -0
  86. package/dist-esm/types/worker.js +1 -0
  87. package/dist-esm/worker/bundle-entry.js +11 -0
  88. package/dist-esm/worker/implementation.browser.js +26 -0
  89. package/dist-esm/worker/implementation.js +19 -0
  90. package/dist-esm/worker/implementation.tiny-worker.js +36 -0
  91. package/dist-esm/worker/implementation.worker_threads.js +37 -0
  92. package/dist-esm/worker/index.js +186 -0
  93. package/dist-esm/worker_threads.js +14 -0
  94. package/index.mjs +11 -0
  95. package/observable.d.ts +2 -0
  96. package/observable.js +3 -0
  97. package/observable.mjs +5 -0
  98. package/package.json +141 -0
  99. package/register.d.ts +3 -0
  100. package/register.js +3 -0
  101. package/register.mjs +2 -0
  102. package/rollup.config.js +16 -0
  103. package/src/common.ts +16 -0
  104. package/src/index.ts +8 -0
  105. package/src/master/get-bundle-url.browser.ts +31 -0
  106. package/src/master/implementation.browser.ts +80 -0
  107. package/src/master/implementation.node.ts +284 -0
  108. package/src/master/implementation.ts +21 -0
  109. package/src/master/index.ts +20 -0
  110. package/src/master/invocation-proxy.ts +146 -0
  111. package/src/master/pool-types.ts +83 -0
  112. package/src/master/pool.ts +391 -0
  113. package/src/master/register.ts +10 -0
  114. package/src/master/spawn.ts +172 -0
  115. package/src/master/thread.ts +26 -0
  116. package/src/observable-promise.ts +181 -0
  117. package/src/observable.ts +43 -0
  118. package/src/ponyfills.ts +31 -0
  119. package/src/promise.ts +26 -0
  120. package/src/serializers.ts +67 -0
  121. package/src/symbols.ts +5 -0
  122. package/src/transferable.ts +68 -0
  123. package/src/types/master.ts +130 -0
  124. package/src/types/messages.ts +81 -0
  125. package/src/types/worker.ts +14 -0
  126. package/src/worker/bundle-entry.ts +10 -0
  127. package/src/worker/implementation.browser.ts +40 -0
  128. package/src/worker/implementation.tiny-worker.ts +52 -0
  129. package/src/worker/implementation.ts +23 -0
  130. package/src/worker/implementation.worker_threads.ts +50 -0
  131. package/src/worker/index.ts +228 -0
  132. package/src/worker_threads.ts +28 -0
  133. package/test/lib/serialization.ts +38 -0
  134. package/test/observable-promise.test.ts +189 -0
  135. package/test/observable.test.ts +86 -0
  136. package/test/pool.test.ts +173 -0
  137. package/test/serialization.test.ts +21 -0
  138. package/test/spawn.chromium.mocha.ts +49 -0
  139. package/test/spawn.test.ts +71 -0
  140. package/test/streaming.test.ts +27 -0
  141. package/test/transferables.test.ts +69 -0
  142. package/test/workers/arraybuffer-xor.ts +11 -0
  143. package/test/workers/count-to-five.ts +13 -0
  144. package/test/workers/counter.ts +20 -0
  145. package/test/workers/faulty-function.ts +6 -0
  146. package/test/workers/hello-world.ts +6 -0
  147. package/test/workers/increment.ts +9 -0
  148. package/test/workers/minmax.ts +25 -0
  149. package/test/workers/serialization.ts +12 -0
  150. package/test/workers/top-level-throw.ts +1 -0
  151. package/test-tooling/rollup/app.js +20 -0
  152. package/test-tooling/rollup/rollup.config.ts +15 -0
  153. package/test-tooling/rollup/rollup.test.ts +44 -0
  154. package/test-tooling/rollup/worker.js +7 -0
  155. package/test-tooling/tsconfig/minimal-tsconfig.test.ts +7 -0
  156. package/test-tooling/tsconfig/minimal.ts +10 -0
  157. package/test-tooling/webpack/addition-worker.ts +10 -0
  158. package/test-tooling/webpack/app-with-inlined-worker.ts +29 -0
  159. package/test-tooling/webpack/app.ts +58 -0
  160. package/test-tooling/webpack/pool-worker.ts +6 -0
  161. package/test-tooling/webpack/raw-loader.d.ts +4 -0
  162. package/test-tooling/webpack/webpack.chromium.mocha.ts +21 -0
  163. package/test-tooling/webpack/webpack.node.config.js +38 -0
  164. package/test-tooling/webpack/webpack.test.ts +90 -0
  165. package/test-tooling/webpack/webpack.web.config.js +35 -0
  166. package/types/is-observable.d.ts +7 -0
  167. package/types/tiny-worker.d.ts +4 -0
  168. package/types/webworker.d.ts +9 -0
  169. package/worker.d.ts +2 -0
  170. package/worker.js +3 -0
  171. package/worker.mjs +7 -0
@@ -0,0 +1,255 @@
1
+ "use strict";
2
+ /* eslint-disable unicorn/prefer-logical-operator-over-ternary */
3
+ /* eslint-disable unicorn/prefer-regexp-test */
4
+ /* eslint-disable @typescript-eslint/no-var-requires */
5
+ /* eslint-disable unicorn/prefer-add-event-listener */
6
+ /* eslint-disable unicorn/prefer-event-target */
7
+ /* eslint-disable @typescript-eslint/no-explicit-any */
8
+ /* eslint-disable unicorn/text-encoding-identifier-case */
9
+ /* eslint-disable unicorn/no-process-exit */
10
+ /// <reference lib="dom" />
11
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ var desc = Object.getOwnPropertyDescriptor(m, k);
14
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
15
+ desc = { enumerable: true, get: function() { return m[k]; } };
16
+ }
17
+ Object.defineProperty(o, k2, desc);
18
+ }) : (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ o[k2] = m[k];
21
+ }));
22
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
23
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
24
+ }) : function(o, v) {
25
+ o["default"] = v;
26
+ });
27
+ var __importStar = (this && this.__importStar) || function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ var __importDefault = (this && this.__importDefault) || function (mod) {
35
+ return (mod && mod.__esModule) ? mod : { "default": mod };
36
+ };
37
+ Object.defineProperty(exports, "__esModule", { value: true });
38
+ exports.isWorkerRuntime = exports.getWorkerImplementation = exports.defaultPoolSize = void 0;
39
+ const node_events_1 = require("node:events");
40
+ const node_os_1 = require("node:os");
41
+ const path = __importStar(require("node:path"));
42
+ const node_url_1 = require("node:url");
43
+ const callsites_1 = __importDefault(require("callsites"));
44
+ let tsNodeAvailable;
45
+ exports.defaultPoolSize = (0, node_os_1.cpus)().length;
46
+ function detectTsNode() {
47
+ if (typeof __non_webpack_require__ === 'function') {
48
+ // Webpack build: => No ts-node required or possible
49
+ return false;
50
+ }
51
+ if (tsNodeAvailable) {
52
+ return tsNodeAvailable;
53
+ }
54
+ try {
55
+ eval('require').resolve('ts-node');
56
+ tsNodeAvailable = true;
57
+ }
58
+ catch (error) {
59
+ if (error && error.code === 'MODULE_NOT_FOUND') {
60
+ tsNodeAvailable = false;
61
+ }
62
+ else {
63
+ // Re-throw
64
+ throw error;
65
+ }
66
+ }
67
+ return tsNodeAvailable;
68
+ }
69
+ function createTsNodeModule(scriptPath) {
70
+ const content = `
71
+ require("ts-node/register/transpile-only");
72
+ require(${JSON.stringify(scriptPath)});
73
+ `;
74
+ return content;
75
+ }
76
+ function rebaseScriptPath(scriptPath, ignoreRegex) {
77
+ const parentCallSite = (0, callsites_1.default)().find((callsite) => {
78
+ const filename = callsite.getFileName();
79
+ return Boolean(filename && !filename.match(ignoreRegex) && !/[/\\]master[/\\]implementation/.test(filename) && !/^internal\/process/.test(filename));
80
+ });
81
+ const rawCallerPath = parentCallSite ? parentCallSite.getFileName() : null;
82
+ let callerPath = rawCallerPath ? rawCallerPath : null;
83
+ if (callerPath && callerPath.startsWith('file:')) {
84
+ callerPath = (0, node_url_1.fileURLToPath)(callerPath);
85
+ }
86
+ const rebasedScriptPath = callerPath ? path.join(path.dirname(callerPath), scriptPath) : scriptPath;
87
+ return rebasedScriptPath;
88
+ }
89
+ function resolveScriptPath(scriptPath, baseURL) {
90
+ const makeRelative = (filePath) => {
91
+ // eval() hack is also webpack-related
92
+ return path.isAbsolute(filePath) ? filePath : path.join(baseURL || eval('__dirname'), filePath);
93
+ };
94
+ const workerFilePath = typeof __non_webpack_require__ === 'function' ?
95
+ __non_webpack_require__.resolve(makeRelative(scriptPath))
96
+ : eval('require').resolve(makeRelative(rebaseScriptPath(scriptPath, /[/\\]worker_threads[/\\]/)));
97
+ return workerFilePath;
98
+ }
99
+ function initWorkerThreadsWorker() {
100
+ // Webpack hack
101
+ const NativeWorker = typeof __non_webpack_require__ === 'function' ? __non_webpack_require__('worker_threads').Worker : eval('require')('worker_threads').Worker;
102
+ let allWorkers = [];
103
+ class Worker extends NativeWorker {
104
+ mappedEventListeners;
105
+ constructor(scriptPath, options) {
106
+ const resolvedScriptPath = options && options.fromSource ? null : resolveScriptPath(scriptPath, (options || {})._baseURL);
107
+ if (!resolvedScriptPath) {
108
+ // `options.fromSource` is true
109
+ const sourceCode = scriptPath;
110
+ super(sourceCode, { ...options, eval: true });
111
+ }
112
+ else if (/\.tsx?$/i.test(resolvedScriptPath) && detectTsNode()) {
113
+ super(createTsNodeModule(resolvedScriptPath), { ...options, eval: true });
114
+ }
115
+ else if (/\.asar[/\\]/.test(resolvedScriptPath)) {
116
+ // See <https://github.com/andywer/threads-plugin/issues/17>
117
+ super(resolvedScriptPath.replace(/\.asar([/\\])/, '.asar.unpacked$1'), options);
118
+ }
119
+ else {
120
+ super(resolvedScriptPath, options);
121
+ }
122
+ this.mappedEventListeners = new WeakMap();
123
+ allWorkers.push(this);
124
+ }
125
+ addEventListener(eventName, rawListener) {
126
+ const listener = (message) => {
127
+ rawListener({ data: message });
128
+ };
129
+ this.mappedEventListeners.set(rawListener, listener);
130
+ this.on(eventName, listener);
131
+ }
132
+ removeEventListener(eventName, rawListener) {
133
+ const listener = this.mappedEventListeners.get(rawListener) || rawListener;
134
+ this.off(eventName, listener);
135
+ }
136
+ }
137
+ const terminateWorkersAndMaster = () => {
138
+ // we should terminate all workers and then gracefully shutdown self process
139
+ Promise.all(allWorkers.map((worker) => worker.terminate())).then(() => process.exit(0), () => process.exit(1));
140
+ allWorkers = [];
141
+ };
142
+ // Take care to not leave orphaned processes behind. See #147.
143
+ process.on('SIGINT', () => terminateWorkersAndMaster());
144
+ process.on('SIGTERM', () => terminateWorkersAndMaster());
145
+ class BlobWorker extends Worker {
146
+ constructor(blob, options) {
147
+ super(Buffer.from(blob).toString('utf-8'), { ...options, fromSource: true });
148
+ }
149
+ static fromText(source, options) {
150
+ return new Worker(source, { ...options, fromSource: true });
151
+ }
152
+ }
153
+ return {
154
+ blob: BlobWorker,
155
+ default: Worker,
156
+ };
157
+ }
158
+ function initTinyWorker() {
159
+ const TinyWorker = require('tiny-worker');
160
+ let allWorkers = [];
161
+ class Worker extends TinyWorker {
162
+ emitter;
163
+ constructor(scriptPath, options) {
164
+ // Need to apply a work-around for Windows or it will choke upon the absolute path
165
+ // (`Error [ERR_INVALID_PROTOCOL]: Protocol 'c:' not supported`)
166
+ const resolvedScriptPath = options && options.fromSource ? null
167
+ : process.platform === 'win32' ? `file:///${resolveScriptPath(scriptPath).replaceAll('\\', '/')}`
168
+ : resolveScriptPath(scriptPath);
169
+ if (!resolvedScriptPath) {
170
+ // `options.fromSource` is true
171
+ const sourceCode = scriptPath;
172
+ super(new Function(sourceCode), [], { esm: true });
173
+ }
174
+ else if (/\.tsx?$/i.test(resolvedScriptPath) && detectTsNode()) {
175
+ super(new Function(createTsNodeModule(resolveScriptPath(scriptPath))), [], { esm: true });
176
+ }
177
+ else if (/\.asar[/\\]/.test(resolvedScriptPath)) {
178
+ // See <https://github.com/andywer/threads-plugin/issues/17>
179
+ super(resolvedScriptPath.replace(/\.asar([/\\])/, '.asar.unpacked$1'), [], { esm: true });
180
+ }
181
+ else {
182
+ super(resolvedScriptPath, [], { esm: true });
183
+ }
184
+ allWorkers.push(this);
185
+ this.emitter = new node_events_1.EventEmitter();
186
+ this.onerror = (error) => this.emitter.emit('error', error);
187
+ this.onmessage = (message) => this.emitter.emit('message', message);
188
+ }
189
+ addEventListener(eventName, listener) {
190
+ this.emitter.addListener(eventName, listener);
191
+ }
192
+ removeEventListener(eventName, listener) {
193
+ this.emitter.removeListener(eventName, listener);
194
+ }
195
+ terminate() {
196
+ allWorkers = allWorkers.filter((worker) => worker !== this);
197
+ return super.terminate();
198
+ }
199
+ }
200
+ const terminateWorkersAndMaster = () => {
201
+ // we should terminate all workers and then gracefully shutdown self process
202
+ Promise.all(allWorkers.map((worker) => worker.terminate())).then(() => process.exit(0), () => process.exit(1));
203
+ allWorkers = [];
204
+ };
205
+ // Take care to not leave orphaned processes behind
206
+ // See <https://github.com/avoidwork/tiny-worker#faq>
207
+ process.on('SIGINT', () => terminateWorkersAndMaster());
208
+ process.on('SIGTERM', () => terminateWorkersAndMaster());
209
+ class BlobWorker extends Worker {
210
+ constructor(blob, options) {
211
+ super(Buffer.from(blob).toString('utf-8'), { ...options, fromSource: true });
212
+ }
213
+ static fromText(source, options) {
214
+ return new Worker(source, { ...options, fromSource: true });
215
+ }
216
+ }
217
+ return {
218
+ blob: BlobWorker,
219
+ default: Worker,
220
+ };
221
+ }
222
+ let implementation;
223
+ let isTinyWorker;
224
+ function selectWorkerImplementation() {
225
+ try {
226
+ isTinyWorker = false;
227
+ return initWorkerThreadsWorker();
228
+ }
229
+ catch {
230
+ // tslint:disable-next-line no-console
231
+ console.debug('Node worker_threads not available. Trying to fall back to tiny-worker polyfill...');
232
+ isTinyWorker = true;
233
+ return initTinyWorker();
234
+ }
235
+ }
236
+ function getWorkerImplementation() {
237
+ if (!implementation) {
238
+ implementation = selectWorkerImplementation();
239
+ }
240
+ return implementation;
241
+ }
242
+ exports.getWorkerImplementation = getWorkerImplementation;
243
+ function isWorkerRuntime() {
244
+ if (isTinyWorker) {
245
+ return self !== undefined && self['postMessage'] ? true : false;
246
+ }
247
+ else {
248
+ // Webpack hack
249
+ const isMainThread = typeof __non_webpack_require__ === 'function' ?
250
+ __non_webpack_require__('worker_threads').isMainThread
251
+ : eval('require')('worker_threads').isMainThread;
252
+ return !isMainThread;
253
+ }
254
+ }
255
+ exports.isWorkerRuntime = isWorkerRuntime;
@@ -0,0 +1,13 @@
1
+ import type { BlobWorker as BlobWorkerClass } from '../types/master';
2
+ import { Worker as WorkerType } from '../types/master';
3
+ export { FunctionThread, ModuleThread } from '../types/master';
4
+ export { Pool } from './pool';
5
+ export { spawn } from './spawn';
6
+ export { Thread } from './thread';
7
+ export type BlobWorker = typeof BlobWorkerClass;
8
+ export type Worker = WorkerType;
9
+ /** Separate class to spawn workers from source code blobs or strings. */
10
+ export declare const BlobWorker: typeof BlobWorkerClass;
11
+ /** Worker implementation. Either web worker or a node.js Worker class. */
12
+ export declare const Worker: typeof import("../types/master").WorkerImplementation;
13
+ export { isWorkerRuntime } from './implementation';
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isWorkerRuntime = exports.Worker = exports.BlobWorker = exports.Thread = exports.spawn = exports.Pool = void 0;
4
+ const implementation_1 = require("./implementation");
5
+ var pool_1 = require("./pool");
6
+ Object.defineProperty(exports, "Pool", { enumerable: true, get: function () { return pool_1.Pool; } });
7
+ var spawn_1 = require("./spawn");
8
+ Object.defineProperty(exports, "spawn", { enumerable: true, get: function () { return spawn_1.spawn; } });
9
+ var thread_1 = require("./thread");
10
+ Object.defineProperty(exports, "Thread", { enumerable: true, get: function () { return thread_1.Thread; } });
11
+ /** Separate class to spawn workers from source code blobs or strings. */
12
+ exports.BlobWorker = (0, implementation_1.getWorkerImplementation)().blob;
13
+ /** Worker implementation. Either web worker or a node.js Worker class. */
14
+ exports.Worker = (0, implementation_1.getWorkerImplementation)().default;
15
+ var implementation_2 = require("./implementation");
16
+ Object.defineProperty(exports, "isWorkerRuntime", { enumerable: true, get: function () { return implementation_2.isWorkerRuntime; } });
@@ -0,0 +1,3 @@
1
+ import { ModuleMethods, ModuleProxy, ProxyableFunction, Worker as WorkerType } from '../types/master';
2
+ export declare function createProxyFunction<Args extends any[], ReturnType>(worker: WorkerType, method?: string): ProxyableFunction<Args, ReturnType>;
3
+ export declare function createProxyModule<Methods extends ModuleMethods>(worker: WorkerType, methodNames: string[]): ModuleProxy<Methods>;
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ /* eslint-disable @typescript-eslint/no-explicit-any */
3
+ /*
4
+ * This source file contains the code for proxying calls in the master thread to calls in the workers
5
+ * by `.postMessage()`-ing.
6
+ *
7
+ * Keep in mind that this code can make or break the program's performance! Need to optimize more…
8
+ */
9
+ var __importDefault = (this && this.__importDefault) || function (mod) {
10
+ return (mod && mod.__esModule) ? mod : { "default": mod };
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.createProxyModule = exports.createProxyFunction = void 0;
14
+ const debug_1 = __importDefault(require("debug"));
15
+ const observable_fns_1 = require("observable-fns");
16
+ const common_1 = require("../common");
17
+ const observable_promise_1 = require("../observable-promise");
18
+ const transferable_1 = require("../transferable");
19
+ const messages_1 = require("../types/messages");
20
+ const debugMessages = (0, debug_1.default)('threads:master:messages');
21
+ let nextJobUID = 1;
22
+ const dedupe = (array) => [...new Set(array)];
23
+ const isJobErrorMessage = (data) => data && data.type === messages_1.WorkerMessageType.error;
24
+ const isJobResultMessage = (data) => data && data.type === messages_1.WorkerMessageType.result;
25
+ const isJobStartMessage = (data) => data && data.type === messages_1.WorkerMessageType.running;
26
+ function createObservableForJob(worker, jobUID) {
27
+ return new observable_fns_1.Observable((observer) => {
28
+ let asyncType;
29
+ const messageHandler = ((event) => {
30
+ debugMessages('Message from worker:', event.data);
31
+ if (!event.data || event.data.uid !== jobUID)
32
+ return;
33
+ if (isJobStartMessage(event.data)) {
34
+ asyncType = event.data.resultType;
35
+ }
36
+ else if (isJobResultMessage(event.data)) {
37
+ if (asyncType === 'promise') {
38
+ if (event.data.payload !== undefined) {
39
+ observer.next((0, common_1.deserialize)(event.data.payload));
40
+ }
41
+ observer.complete();
42
+ worker.removeEventListener('message', messageHandler);
43
+ }
44
+ else {
45
+ if (event.data.payload) {
46
+ observer.next((0, common_1.deserialize)(event.data.payload));
47
+ }
48
+ if (event.data.complete) {
49
+ observer.complete();
50
+ worker.removeEventListener('message', messageHandler);
51
+ }
52
+ }
53
+ }
54
+ else if (isJobErrorMessage(event.data)) {
55
+ const error = (0, common_1.deserialize)(event.data.error);
56
+ if (asyncType === 'promise' || !asyncType) {
57
+ observer.error(error);
58
+ }
59
+ else {
60
+ observer.error(error);
61
+ }
62
+ worker.removeEventListener('message', messageHandler);
63
+ }
64
+ });
65
+ worker.addEventListener('message', messageHandler);
66
+ return () => {
67
+ if (asyncType === 'observable' || !asyncType) {
68
+ const cancelMessage = {
69
+ type: messages_1.MasterMessageType.cancel,
70
+ uid: jobUID,
71
+ };
72
+ worker.postMessage(cancelMessage);
73
+ }
74
+ worker.removeEventListener('message', messageHandler);
75
+ };
76
+ });
77
+ }
78
+ function prepareArguments(rawArgs) {
79
+ if (rawArgs.length === 0) {
80
+ // Exit early if possible
81
+ return {
82
+ args: [],
83
+ transferables: [],
84
+ };
85
+ }
86
+ const args = [];
87
+ const transferables = [];
88
+ for (const arg of rawArgs) {
89
+ if ((0, transferable_1.isTransferDescriptor)(arg)) {
90
+ args.push((0, common_1.serialize)(arg.send));
91
+ transferables.push(...arg.transferables);
92
+ }
93
+ else {
94
+ args.push((0, common_1.serialize)(arg));
95
+ }
96
+ }
97
+ return {
98
+ args,
99
+ transferables: transferables.length === 0 ? transferables : dedupe(transferables),
100
+ };
101
+ }
102
+ function createProxyFunction(worker, method) {
103
+ return ((...rawArgs) => {
104
+ const uid = nextJobUID++;
105
+ const { args, transferables } = prepareArguments(rawArgs);
106
+ const runMessage = {
107
+ args,
108
+ method,
109
+ type: messages_1.MasterMessageType.run,
110
+ uid,
111
+ };
112
+ debugMessages('Sending command to run function to worker:', runMessage);
113
+ try {
114
+ worker.postMessage(runMessage, transferables);
115
+ }
116
+ catch (error) {
117
+ return observable_promise_1.ObservablePromise.from(Promise.reject(error));
118
+ }
119
+ return observable_promise_1.ObservablePromise.from((0, observable_fns_1.multicast)(createObservableForJob(worker, uid)));
120
+ });
121
+ }
122
+ exports.createProxyFunction = createProxyFunction;
123
+ function createProxyModule(worker, methodNames) {
124
+ const proxy = {};
125
+ for (const methodName of methodNames) {
126
+ proxy[methodName] = createProxyFunction(worker, methodName);
127
+ }
128
+ return proxy;
129
+ }
130
+ exports.createProxyModule = createProxyModule;
@@ -0,0 +1,65 @@
1
+ import { Thread } from './thread';
2
+ /** Pool event type. Specifies the type of each `PoolEvent`. */
3
+ export declare enum PoolEventType {
4
+ initialized = "initialized",
5
+ taskCanceled = "taskCanceled",
6
+ taskCompleted = "taskCompleted",
7
+ taskFailed = "taskFailed",
8
+ taskQueued = "taskQueued",
9
+ taskQueueDrained = "taskQueueDrained",
10
+ taskStart = "taskStart",
11
+ terminated = "terminated"
12
+ }
13
+ export type TaskRunFunction<ThreadType extends Thread, Return> = (worker: ThreadType) => Promise<Return>;
14
+ /** Pool event. Subscribe to those events using `pool.events()`. Useful for debugging. */
15
+ export type PoolEvent<ThreadType extends Thread> = {
16
+ type: PoolEventType.initialized;
17
+ size: number;
18
+ } | {
19
+ type: PoolEventType.taskQueued;
20
+ taskID: number;
21
+ } | {
22
+ type: PoolEventType.taskQueueDrained;
23
+ } | {
24
+ type: PoolEventType.taskStart;
25
+ taskID: number;
26
+ workerID: number;
27
+ } | {
28
+ type: PoolEventType.taskCompleted;
29
+ returnValue: any;
30
+ taskID: number;
31
+ workerID: number;
32
+ } | {
33
+ type: PoolEventType.taskFailed;
34
+ error: Error;
35
+ taskID: number;
36
+ workerID: number;
37
+ } | {
38
+ type: PoolEventType.taskCanceled;
39
+ taskID: number;
40
+ } | {
41
+ type: PoolEventType.terminated;
42
+ remainingQueue: Array<QueuedTask<ThreadType, any>>;
43
+ };
44
+ export interface WorkerDescriptor<ThreadType extends Thread> {
45
+ init: Promise<ThreadType>;
46
+ runningTasks: Array<Promise<any>>;
47
+ }
48
+ /**
49
+ * Task that has been `pool.queued()`-ed.
50
+ */
51
+ export interface QueuedTask<ThreadType extends Thread, Return> {
52
+ /** @private */
53
+ id: number;
54
+ /** @private */
55
+ run: TaskRunFunction<ThreadType, Return>;
56
+ /**
57
+ * Queued tasks can be cancelled until the pool starts running them on a worker thread.
58
+ */
59
+ cancel(): void;
60
+ /**
61
+ * `QueuedTask` is thenable, so you can `await` it.
62
+ * Resolves when the task has successfully been executed. Rejects if the task fails.
63
+ */
64
+ then: Promise<Return>['then'];
65
+ }
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PoolEventType = void 0;
4
+ /** Pool event type. Specifies the type of each `PoolEvent`. */
5
+ var PoolEventType;
6
+ (function (PoolEventType) {
7
+ PoolEventType["initialized"] = "initialized";
8
+ PoolEventType["taskCanceled"] = "taskCanceled";
9
+ PoolEventType["taskCompleted"] = "taskCompleted";
10
+ PoolEventType["taskFailed"] = "taskFailed";
11
+ PoolEventType["taskQueued"] = "taskQueued";
12
+ PoolEventType["taskQueueDrained"] = "taskQueueDrained";
13
+ PoolEventType["taskStart"] = "taskStart";
14
+ PoolEventType["terminated"] = "terminated";
15
+ })(PoolEventType || (exports.PoolEventType = PoolEventType = {}));
@@ -0,0 +1,90 @@
1
+ import { Observable } from 'observable-fns';
2
+ import { PoolEvent, PoolEventType, QueuedTask, TaskRunFunction } from './pool-types';
3
+ import { Thread } from './thread';
4
+ export declare namespace Pool {
5
+ type Event<ThreadType extends Thread = any> = PoolEvent<ThreadType>;
6
+ type EventType = PoolEventType;
7
+ }
8
+ /**
9
+ * Thread pool managing a set of worker threads.
10
+ * Use it to queue tasks that are run on those threads with limited
11
+ * concurrency.
12
+ */
13
+ export interface Pool<ThreadType extends Thread> {
14
+ /**
15
+ * Returns a promise that resolves once the task queue is emptied.
16
+ * Promise will be rejected if any task fails.
17
+ *
18
+ * @param allowResolvingImmediately Set to `true` to resolve immediately if task queue is currently empty.
19
+ */
20
+ completed(allowResolvingImmediately?: boolean): Promise<any>;
21
+ /**
22
+ * Returns a promise that resolves once the task queue is emptied.
23
+ * Failing tasks will not cause the promise to be rejected.
24
+ *
25
+ * @param allowResolvingImmediately Set to `true` to resolve immediately if task queue is currently empty.
26
+ */
27
+ settled(allowResolvingImmediately?: boolean): Promise<Error[]>;
28
+ /**
29
+ * Returns an observable that yields pool events.
30
+ */
31
+ events(): Observable<PoolEvent<ThreadType>>;
32
+ /**
33
+ * Queue a task and return a promise that resolves once the task has been dequeued,
34
+ * started and finished.
35
+ *
36
+ * @param task An async function that takes a thread instance and invokes it.
37
+ */
38
+ queue<Return>(task: TaskRunFunction<ThreadType, Return>): QueuedTask<ThreadType, Return>;
39
+ /**
40
+ * Terminate all pool threads.
41
+ *
42
+ * @param force Set to `true` to kill the thread even if it cannot be stopped gracefully.
43
+ */
44
+ terminate(force?: boolean): Promise<void>;
45
+ }
46
+ export interface PoolOptions {
47
+ /** Maximum no. of tasks to run on one worker thread at a time. Defaults to one. */
48
+ concurrency?: number;
49
+ /** Maximum no. of jobs to be queued for execution before throwing an error. */
50
+ maxQueuedJobs?: number;
51
+ /** Gives that pool a name to be used for debug logging, letting you distinguish between log output of different pools. */
52
+ name?: string;
53
+ /** No. of worker threads to spawn and to be managed by the pool. */
54
+ size?: number;
55
+ }
56
+ declare class WorkerPool<ThreadType extends Thread> implements Pool<ThreadType> {
57
+ static EventType: typeof PoolEventType;
58
+ private readonly debug;
59
+ private readonly eventObservable;
60
+ private readonly options;
61
+ private readonly workers;
62
+ private readonly eventSubject;
63
+ private initErrors;
64
+ private isClosing;
65
+ private nextTaskID;
66
+ private taskQueue;
67
+ constructor(spawnWorker: () => Promise<ThreadType>, optionsOrSize?: number | PoolOptions);
68
+ private findIdlingWorker;
69
+ private runPoolTask;
70
+ private run;
71
+ private scheduleWork;
72
+ private taskCompletion;
73
+ settled(allowResolvingImmediately?: boolean): Promise<Error[]>;
74
+ completed(allowResolvingImmediately?: boolean): Promise<void>;
75
+ events(): Observable<PoolEvent<ThreadType>>;
76
+ queue(taskFunction: TaskRunFunction<ThreadType, any>): QueuedTask<ThreadType, any>;
77
+ terminate(force?: boolean): Promise<void>;
78
+ }
79
+ /**
80
+ * Thread pool constructor. Creates a new pool and spawns its worker threads.
81
+ */
82
+ declare function PoolConstructor<ThreadType extends Thread>(spawnWorker: () => Promise<ThreadType>, optionsOrSize?: number | PoolOptions): WorkerPool<ThreadType>;
83
+ /**
84
+ * Thread pool constructor. Creates a new pool and spawns its worker threads.
85
+ */
86
+ export declare const Pool: typeof PoolConstructor & {
87
+ EventType: typeof PoolEventType;
88
+ };
89
+ export { PoolEvent, PoolEventType, QueuedTask } from './pool-types';
90
+ export { Thread } from './thread';