@atlaspack/workers 2.12.1-canary.3354

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 (52) hide show
  1. package/LICENSE +201 -0
  2. package/index.d.ts +23 -0
  3. package/lib/Handle.js +45 -0
  4. package/lib/Worker.js +188 -0
  5. package/lib/WorkerFarm.js +563 -0
  6. package/lib/backend.js +34 -0
  7. package/lib/bus.js +31 -0
  8. package/lib/child.js +294 -0
  9. package/lib/childState.js +14 -0
  10. package/lib/core-worker.browser.js +4 -0
  11. package/lib/core-worker.js +4 -0
  12. package/lib/cpuCount.js +79 -0
  13. package/lib/index.js +75 -0
  14. package/lib/process/ProcessChild.js +58 -0
  15. package/lib/process/ProcessWorker.js +83 -0
  16. package/lib/threads/ThreadsChild.js +49 -0
  17. package/lib/threads/ThreadsWorker.js +61 -0
  18. package/lib/types.js +1 -0
  19. package/lib/web/WebChild.js +44 -0
  20. package/lib/web/WebWorker.js +85 -0
  21. package/package.json +36 -0
  22. package/src/Handle.js +48 -0
  23. package/src/Worker.js +227 -0
  24. package/src/WorkerFarm.js +707 -0
  25. package/src/backend.js +33 -0
  26. package/src/bus.js +26 -0
  27. package/src/child.js +322 -0
  28. package/src/childState.js +10 -0
  29. package/src/core-worker.browser.js +3 -0
  30. package/src/core-worker.js +2 -0
  31. package/src/cpuCount.js +75 -0
  32. package/src/index.js +43 -0
  33. package/src/process/ProcessChild.js +56 -0
  34. package/src/process/ProcessWorker.js +91 -0
  35. package/src/threads/ThreadsChild.js +42 -0
  36. package/src/threads/ThreadsWorker.js +66 -0
  37. package/src/types.js +68 -0
  38. package/src/web/WebChild.js +53 -0
  39. package/src/web/WebWorker.js +88 -0
  40. package/test/cpuCount.test.js +19 -0
  41. package/test/integration/workerfarm/console.js +15 -0
  42. package/test/integration/workerfarm/echo.js +5 -0
  43. package/test/integration/workerfarm/ipc-pid.js +18 -0
  44. package/test/integration/workerfarm/ipc.js +10 -0
  45. package/test/integration/workerfarm/logging.js +19 -0
  46. package/test/integration/workerfarm/master-process-id.js +3 -0
  47. package/test/integration/workerfarm/master-sum.js +3 -0
  48. package/test/integration/workerfarm/ping.js +5 -0
  49. package/test/integration/workerfarm/resolve-shared-reference.js +5 -0
  50. package/test/integration/workerfarm/reverse-handle.js +5 -0
  51. package/test/integration/workerfarm/shared-reference.js +6 -0
  52. package/test/workerfarm.js +362 -0
@@ -0,0 +1,42 @@
1
+ // @flow
2
+
3
+ import type {
4
+ ChildImpl,
5
+ MessageHandler,
6
+ ExitHandler,
7
+ WorkerMessage,
8
+ } from '../types';
9
+ import {isMainThread, parentPort} from 'worker_threads';
10
+ import nullthrows from 'nullthrows';
11
+ import {setChild} from '../childState';
12
+ import {Child} from '../child';
13
+ import {
14
+ prepareForSerialization,
15
+ restoreDeserializedObject,
16
+ } from '@atlaspack/core';
17
+
18
+ export default class ThreadsChild implements ChildImpl {
19
+ onMessage: MessageHandler;
20
+ onExit: ExitHandler;
21
+
22
+ constructor(onMessage: MessageHandler, onExit: ExitHandler) {
23
+ if (isMainThread || !parentPort) {
24
+ throw new Error('Only create ThreadsChild instances in a worker!');
25
+ }
26
+
27
+ this.onMessage = onMessage;
28
+ this.onExit = onExit;
29
+ parentPort.on('message', data => this.handleMessage(data));
30
+ parentPort.on('close', this.onExit);
31
+ }
32
+
33
+ handleMessage(data: WorkerMessage) {
34
+ this.onMessage(restoreDeserializedObject(data));
35
+ }
36
+
37
+ send(data: WorkerMessage) {
38
+ nullthrows(parentPort).postMessage(prepareForSerialization(data));
39
+ }
40
+ }
41
+
42
+ setChild(new Child(ThreadsChild));
@@ -0,0 +1,66 @@
1
+ // @flow
2
+
3
+ import type {
4
+ WorkerImpl,
5
+ MessageHandler,
6
+ ErrorHandler,
7
+ ExitHandler,
8
+ WorkerMessage,
9
+ } from '../types';
10
+ import {Worker} from 'worker_threads';
11
+ import path from 'path';
12
+ import {
13
+ prepareForSerialization,
14
+ restoreDeserializedObject,
15
+ } from '@atlaspack/core';
16
+
17
+ const WORKER_PATH = path.join(__dirname, 'ThreadsChild.js');
18
+
19
+ export default class ThreadsWorker implements WorkerImpl {
20
+ execArgv: Object;
21
+ onMessage: MessageHandler;
22
+ onError: ErrorHandler;
23
+ onExit: ExitHandler;
24
+ worker: Worker;
25
+
26
+ constructor(
27
+ execArgv: Object,
28
+ onMessage: MessageHandler,
29
+ onError: ErrorHandler,
30
+ onExit: ExitHandler,
31
+ ) {
32
+ this.execArgv = execArgv;
33
+ this.onMessage = onMessage;
34
+ this.onError = onError;
35
+ this.onExit = onExit;
36
+ }
37
+
38
+ start(): Promise<void> {
39
+ this.worker = new Worker(WORKER_PATH, {
40
+ execArgv: this.execArgv,
41
+ env: process.env,
42
+ });
43
+
44
+ this.worker.on('message', data => this.handleMessage(data));
45
+ this.worker.on('error', this.onError);
46
+ this.worker.on('exit', this.onExit);
47
+
48
+ return new Promise<void>(resolve => {
49
+ this.worker.on('online', resolve);
50
+ });
51
+ }
52
+
53
+ stop(): Promise<void> {
54
+ // In node 12, this returns a promise, but previously it accepted a callback
55
+ // TODO: Pass a callback in earlier versions of Node
56
+ return Promise.resolve(this.worker.terminate());
57
+ }
58
+
59
+ handleMessage(data: WorkerMessage) {
60
+ this.onMessage(restoreDeserializedObject(data));
61
+ }
62
+
63
+ send(data: WorkerMessage) {
64
+ this.worker.postMessage(prepareForSerialization(data));
65
+ }
66
+ }
package/src/types.js ADDED
@@ -0,0 +1,68 @@
1
+ // @flow
2
+ import type {Diagnostic} from '@atlaspack/diagnostic';
3
+ import type {FilePath} from '@atlaspack/types-internal';
4
+
5
+ export type LocationCallRequest = {|
6
+ args: $ReadOnlyArray<mixed>,
7
+ location: string,
8
+ method?: string,
9
+ |};
10
+
11
+ export type HandleCallRequest = {|
12
+ args: $ReadOnlyArray<mixed>,
13
+ handle: number,
14
+ |};
15
+
16
+ export type CallRequest = LocationCallRequest | HandleCallRequest;
17
+
18
+ export type WorkerRequest = {|
19
+ args: $ReadOnlyArray<any>,
20
+ awaitResponse?: boolean,
21
+ child?: ?number,
22
+ idx?: number,
23
+ location?: FilePath,
24
+ method?: ?string,
25
+ type: 'request',
26
+ handle?: number,
27
+ |};
28
+
29
+ export type WorkerDataResponse = {|
30
+ idx?: number,
31
+ child?: number,
32
+ type: 'response',
33
+ contentType: 'data',
34
+ content: string,
35
+ |};
36
+
37
+ export type WorkerErrorResponse = {|
38
+ idx?: number,
39
+ child?: number,
40
+ type: 'response',
41
+ contentType: 'error',
42
+ content: Diagnostic | Array<Diagnostic>,
43
+ |};
44
+
45
+ export type WorkerResponse = WorkerDataResponse | WorkerErrorResponse;
46
+ export type WorkerMessage = WorkerRequest | WorkerResponse;
47
+
48
+ export type MessageHandler = (data: WorkerMessage) => void;
49
+ export type ErrorHandler = (err: Error) => void;
50
+ export type ExitHandler = (code: number) => void;
51
+ export interface WorkerImpl {
52
+ constructor(
53
+ execArgv: Object,
54
+ onMessage: MessageHandler,
55
+ onError: ErrorHandler,
56
+ onExit: ExitHandler,
57
+ ): void;
58
+ start(): Promise<void>;
59
+ stop(): Promise<void>;
60
+ send(data: WorkerMessage): void;
61
+ }
62
+
63
+ export interface ChildImpl {
64
+ constructor(onMessage: MessageHandler, onExit: ExitHandler): void;
65
+ send(data: WorkerMessage): void;
66
+ }
67
+
68
+ export type BackendType = 'threads' | 'process' | 'web';
@@ -0,0 +1,53 @@
1
+ // @flow
2
+ /* eslint-env worker*/
3
+
4
+ import type {
5
+ ChildImpl,
6
+ MessageHandler,
7
+ ExitHandler,
8
+ WorkerMessage,
9
+ } from '../types';
10
+ import {setChild} from '../childState';
11
+ import {Child} from '../child';
12
+ import {
13
+ prepareForSerialization,
14
+ restoreDeserializedObject,
15
+ } from '@atlaspack/core';
16
+
17
+ export default class WebChild implements ChildImpl {
18
+ onMessage: MessageHandler;
19
+ onExit: ExitHandler;
20
+
21
+ constructor(onMessage: MessageHandler, onExit: ExitHandler) {
22
+ if (
23
+ !(
24
+ typeof WorkerGlobalScope !== 'undefined' &&
25
+ self instanceof WorkerGlobalScope
26
+ )
27
+ ) {
28
+ throw new Error('Only create WebChild instances in a worker!');
29
+ }
30
+
31
+ this.onMessage = onMessage;
32
+ this.onExit = onExit;
33
+ self.addEventListener('message', ({data}: MessageEvent) => {
34
+ if (data === 'stop') {
35
+ this.onExit(0);
36
+ self.postMessage('stopped');
37
+ }
38
+ // $FlowFixMe assume WorkerMessage as data
39
+ this.handleMessage(data);
40
+ });
41
+ self.postMessage('online');
42
+ }
43
+
44
+ handleMessage(data: WorkerMessage) {
45
+ this.onMessage(restoreDeserializedObject(data));
46
+ }
47
+
48
+ send(data: WorkerMessage) {
49
+ self.postMessage(prepareForSerialization(data));
50
+ }
51
+ }
52
+
53
+ setChild(new Child(WebChild));
@@ -0,0 +1,88 @@
1
+ // @flow
2
+
3
+ import type {
4
+ WorkerImpl,
5
+ MessageHandler,
6
+ ErrorHandler,
7
+ ExitHandler,
8
+ WorkerMessage,
9
+ } from '../types';
10
+ import {
11
+ prepareForSerialization,
12
+ restoreDeserializedObject,
13
+ } from '@atlaspack/core';
14
+ import {makeDeferredWithPromise} from '@atlaspack/utils';
15
+
16
+ let id = 0;
17
+
18
+ export default class WebWorker implements WorkerImpl {
19
+ execArgv: Object;
20
+ onMessage: MessageHandler;
21
+ onError: ErrorHandler;
22
+ onExit: ExitHandler;
23
+ worker: Worker;
24
+ stopping: ?Promise<void>;
25
+
26
+ constructor(
27
+ execArgv: Object,
28
+ onMessage: MessageHandler,
29
+ onError: ErrorHandler,
30
+ onExit: ExitHandler,
31
+ ) {
32
+ this.execArgv = execArgv;
33
+ this.onMessage = onMessage;
34
+ this.onError = onError;
35
+ this.onExit = onExit;
36
+ }
37
+
38
+ start(): Promise<void> {
39
+ // $FlowFixMe[incompatible-call]
40
+ this.worker = new Worker(new URL('./WebChild.js', import.meta.url), {
41
+ name: `Atlaspack Worker ${id++}`,
42
+ type: 'module',
43
+ });
44
+
45
+ let {deferred, promise} = makeDeferredWithPromise();
46
+
47
+ this.worker.onmessage = ({data}) => {
48
+ if (data === 'online') {
49
+ deferred.resolve();
50
+ return;
51
+ }
52
+
53
+ // $FlowFixMe assume WorkerMessage as data
54
+ this.handleMessage(data);
55
+ };
56
+ this.worker.onerror = this.onError;
57
+ // Web workers can't crash or intentionally stop on their own, apart from stop() below
58
+ // this.worker.on('exit', this.onExit);
59
+
60
+ return promise;
61
+ }
62
+
63
+ stop(): Promise<void> {
64
+ if (!this.stopping) {
65
+ this.stopping = (async () => {
66
+ this.worker.postMessage('stop');
67
+ let {deferred, promise} = makeDeferredWithPromise();
68
+ this.worker.addEventListener('message', ({data}: MessageEvent) => {
69
+ if (data === 'stopped') {
70
+ deferred.resolve();
71
+ }
72
+ });
73
+ await promise;
74
+ this.worker.terminate();
75
+ this.onExit(0);
76
+ })();
77
+ }
78
+ return this.stopping;
79
+ }
80
+
81
+ handleMessage(data: WorkerMessage) {
82
+ this.onMessage(restoreDeserializedObject(data));
83
+ }
84
+
85
+ send(data: WorkerMessage) {
86
+ this.worker.postMessage(prepareForSerialization(data));
87
+ }
88
+ }
@@ -0,0 +1,19 @@
1
+ import assert from 'assert';
2
+ import os from 'os';
3
+
4
+ import getCores, {detectRealCores} from '../src/cpuCount';
5
+
6
+ describe('cpuCount', function () {
7
+ it('Should be able to detect real cpu count', () => {
8
+ // Windows not supported as getting the cpu count takes a couple seconds...
9
+ if (os.platform() === 'win32') return;
10
+
11
+ let cores = detectRealCores();
12
+ assert(cores > 0);
13
+ });
14
+
15
+ it('getCores should return more than 0', () => {
16
+ let cores = getCores(true);
17
+ assert(cores > 0);
18
+ });
19
+ });
@@ -0,0 +1,15 @@
1
+ const WorkerFarm = require('../../../src/WorkerFarm').default;
2
+
3
+ function run() {
4
+ if (WorkerFarm.isWorker()) {
5
+ // Only test this behavior in workers. Logging in the main process will
6
+ // always work.
7
+ console.log('one');
8
+ console.info('two');
9
+ console.warn('three');
10
+ console.error('four');
11
+ console.debug('five');
12
+ }
13
+ }
14
+
15
+ exports.run = run;
@@ -0,0 +1,5 @@
1
+ function run(_, data) {
2
+ return data;
3
+ }
4
+
5
+ exports.run = run;
@@ -0,0 +1,18 @@
1
+ const WorkerFarm = require('../../../src/WorkerFarm').default;
2
+
3
+ function run(api) {
4
+ let result = [process.pid];
5
+ return new Promise((resolve, reject) => {
6
+ api.callMaster({
7
+ location: require.resolve('./master-process-id.js'),
8
+ args: []
9
+ })
10
+ .then(pid => {
11
+ result.push(pid);
12
+ resolve(result);
13
+ })
14
+ .catch(reject);
15
+ });
16
+ }
17
+
18
+ exports.run = run;
@@ -0,0 +1,10 @@
1
+ const WorkerFarm = require('../../../src/WorkerFarm').default;
2
+
3
+ function run(api, a, b) {
4
+ return api.callMaster({
5
+ location: require.resolve('./master-sum.js'),
6
+ args: [a, b]
7
+ });
8
+ }
9
+
10
+ exports.run = run;
@@ -0,0 +1,19 @@
1
+ const WorkerFarm = require('../../../src/WorkerFarm').default;
2
+ const Logger = require('@atlaspack/logger').default;
3
+
4
+ function run() {
5
+ if (WorkerFarm.isWorker()) {
6
+ // Only test this behavior in workers. Logging in the main process will
7
+ // always work.
8
+ Logger.info({
9
+ origin: 'logging-worker',
10
+ message: 'omg it works'
11
+ });
12
+ Logger.error({
13
+ origin: 'logging-worker',
14
+ message: 'errors objects dont work yet'
15
+ });
16
+ }
17
+ }
18
+
19
+ exports.run = run;
@@ -0,0 +1,3 @@
1
+ module.exports = function() {
2
+ return process.pid;
3
+ };
@@ -0,0 +1,3 @@
1
+ module.exports = function(a, b) {
2
+ return a + b;
3
+ };
@@ -0,0 +1,5 @@
1
+ function run() {
2
+ return 'pong';
3
+ }
4
+
5
+ exports.run = run;
@@ -0,0 +1,5 @@
1
+ function run(workerApi, ref) {
2
+ return ref === workerApi.resolveSharedReference(workerApi.getSharedReference(ref));
3
+ }
4
+
5
+ exports.run = run;
@@ -0,0 +1,5 @@
1
+ function run(workerApi, handle) {
2
+ return workerApi.runHandle(handle, []);
3
+ }
4
+
5
+ exports.run = run;
@@ -0,0 +1,6 @@
1
+ function run(workerApi, ref) {
2
+ let sharedReference = workerApi.getSharedReference(ref);
3
+ return sharedReference || 'Shared reference does not exist';
4
+ }
5
+
6
+ exports.run = run;