Skip to content

Commit 5124547

Browse files
committed
Experimentally implement shared workers
1 parent ba4f426 commit 5124547

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+1656
-43
lines changed

ava.config.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
const skipTests = [];
2-
if (process.versions.node < '12.14.0') {
3-
skipTests.push('!test/configurable-module-format/module.js');
2+
if (process.versions.node < '12.17.0') {
3+
skipTests.push(
4+
'!test/configurable-module-format/module.js',
5+
'!test/shared-workers/!(requires-newish-node)/**'
6+
);
47
}
58

69
export default {

lib/api.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const RunStatus = require('./run-status');
1717
const fork = require('./fork');
1818
const serializeError = require('./serialize-error');
1919
const {getApplicableLineNumbers} = require('./line-numbers');
20+
const sharedWorkers = require('./plugin-support/shared-workers');
2021

2122
function resolveModules(modules) {
2223
return arrify(modules).map(name => {
@@ -206,6 +207,8 @@ class Api extends Emittery {
206207
concurrency = 1;
207208
}
208209

210+
const deregisteredSharedWorkers = [];
211+
209212
// Try and run each file, limited by `concurrency`.
210213
await pMap(selectedFiles, async file => {
211214
// No new files should be run once a test has timed out or failed,
@@ -231,15 +234,19 @@ class Api extends Emittery {
231234

232235
const worker = fork(file, options, apiOptions.nodeArguments);
233236
runStatus.observeWorker(worker, file, {selectingLines: lineNumbers.length > 0});
237+
deregisteredSharedWorkers.push(sharedWorkers.observeWorkerProcess(worker, runStatus));
234238

235239
pendingWorkers.add(worker);
236240
worker.promise.then(() => {
237241
pendingWorkers.delete(worker);
238242
});
239243
restartTimer();
240244

241-
return worker.promise;
245+
await worker.promise;
242246
}, {concurrency, stopOnError: false});
247+
248+
// Allow shared workers to clean up before the run ends.
249+
await Promise.all(deregisteredSharedWorkers);
243250
} catch (error) {
244251
if (error && error.name === 'AggregateError') {
245252
for (const err of error) {

lib/fork.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,55 @@ if (fs.realpathSync(__filename) !== __filename) {
1414
const AVA_PATH = path.resolve(__dirname, '..');
1515
const WORKER_PATH = require.resolve('./worker/subprocess');
1616

17+
class SharedWorkerChannel extends Emittery {
18+
constructor({channelId, filename, initialData}, sendToFork) {
19+
super();
20+
21+
this.id = channelId;
22+
this.filename = filename;
23+
this.initialData = initialData;
24+
this.sendToFork = sendToFork;
25+
}
26+
27+
signalReady() {
28+
this.sendToFork({
29+
type: 'shared-worker-ready',
30+
channelId: this.id
31+
});
32+
}
33+
34+
signalError() {
35+
this.sendToFork({
36+
type: 'shared-worker-error',
37+
channelId: this.id
38+
});
39+
}
40+
41+
emitMessage({messageId, replyTo, serializedData}) {
42+
this.emit('message', {
43+
messageId,
44+
replyTo,
45+
serializedData
46+
});
47+
}
48+
49+
forwardMessageToFork({messageId, replyTo, serializedData}) {
50+
this.sendToFork({
51+
type: 'shared-worker-message',
52+
channelId: this.id,
53+
messageId,
54+
replyTo,
55+
serializedData
56+
});
57+
}
58+
}
59+
60+
let forkCounter = 0;
61+
1762
module.exports = (file, options, execArgv = process.execArgv) => {
63+
const forkId = `fork/${++forkCounter}`;
64+
const sharedWorkerChannels = new Map();
65+
1866
let finished = false;
1967

2068
const emitter = new Emittery();
@@ -27,6 +75,7 @@ module.exports = (file, options, execArgv = process.execArgv) => {
2775
options = {
2876
baseDir: process.cwd(),
2977
file,
78+
forkId,
3079
...options
3180
};
3281

@@ -69,6 +118,16 @@ module.exports = (file, options, execArgv = process.execArgv) => {
69118
case 'ready-for-options':
70119
send({type: 'options', options});
71120
break;
121+
case 'shared-worker-connect': {
122+
const channel = new SharedWorkerChannel(message.ava, send);
123+
sharedWorkerChannels.set(channel.id, channel);
124+
emitter.emit('connectSharedWorker', channel);
125+
break;
126+
}
127+
128+
case 'shared-worker-message':
129+
sharedWorkerChannels.get(message.ava.channelId).emitMessage(message.ava);
130+
break;
72131
case 'ping':
73132
send({type: 'pong'});
74133
break;
@@ -99,6 +158,7 @@ module.exports = (file, options, execArgv = process.execArgv) => {
99158

100159
return {
101160
file,
161+
forkId,
102162
promise,
103163

104164
exit() {
@@ -110,6 +170,10 @@ module.exports = (file, options, execArgv = process.execArgv) => {
110170
send({type: 'peer-failed'});
111171
},
112172

173+
onConnectSharedWorker(listener) {
174+
return emitter.on('connectSharedWorker', listener);
175+
},
176+
113177
onStateChange(listener) {
114178
return emitter.on('stateChange', listener);
115179
}

lib/load-config.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,13 @@ const pkgConf = require('pkg-conf');
77

88
const NO_SUCH_FILE = Symbol('no ava.config.js file');
99
const MISSING_DEFAULT_EXPORT = Symbol('missing default export');
10-
const EXPERIMENTS = new Set(['configurableModuleFormat', 'disableNullExpectations', 'disableSnapshotsInHooks', 'reverseTeardowns']);
10+
const EXPERIMENTS = new Set([
11+
'configurableModuleFormat',
12+
'disableNullExpectations',
13+
'disableSnapshotsInHooks',
14+
'reverseTeardowns',
15+
'sharedWorkers'
16+
]);
1117

1218
// *Very* rudimentary support for loading ava.config.js files containing an `export default` statement.
1319
const evaluateJsConfig = configFile => {

0 commit comments

Comments
 (0)