Skip to content

Commit c431770

Browse files
authored
Enable --proxy-to-worker under node. (#19582)
At least enough to run hello world. The idea here is not so much that folks really want use `--proxy-to-worker` under node, but more for testing, so we can run tests where the module is instantiated on a worker. In particular we can write tests for issues such as #19577 that run under node. Also, because `proxyClient.js` now goes through the preprocessor we can use the normal `#include` mechanism to include `IDBStore.js`.
1 parent 4554600 commit c431770

File tree

7 files changed

+88
-71
lines changed

7 files changed

+88
-71
lines changed

emcc.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4150,12 +4150,11 @@ def generate_html(target, options, js_target, target_basename,
41504150

41514151

41524152
def generate_worker_js(target, js_target, target_basename):
4153-
# compiler output is embedded as base64
41544153
if settings.SINGLE_FILE:
4154+
# compiler output is embedded as base64
41554155
proxy_worker_filename = get_subresource_location(js_target)
4156-
4157-
# compiler output goes in .worker.js file
41584156
else:
4157+
# compiler output goes in .worker.js file
41594158
move_file(js_target, shared.replace_suffix(js_target, '.worker.js'))
41604159
worker_target_basename = target_basename + '.worker'
41614160
proxy_worker_filename = (settings.PROXY_TO_WORKER_FILENAME or worker_target_basename) + '.js'
@@ -4166,10 +4165,10 @@ def generate_worker_js(target, js_target, target_basename):
41664165

41674166
def worker_js_script(proxy_worker_filename):
41684167
web_gl_client_src = read_file(utils.path_from_root('src/webGLClient.js'))
4169-
idb_store_src = read_file(utils.path_from_root('src/IDBStore.js'))
4170-
proxy_client_src = read_file(utils.path_from_root('src/proxyClient.js'))
4171-
proxy_client_src = do_replace(proxy_client_src, '{{{ filename }}}', proxy_worker_filename)
4172-
proxy_client_src = do_replace(proxy_client_src, '{{{ IDBStore.js }}}', idb_store_src)
4168+
proxy_client_src = shared.read_and_preprocess(utils.path_from_root('src/proxyClient.js'), expand_macros=True)
4169+
if not os.path.dirname(proxy_worker_filename):
4170+
proxy_worker_filename = './' + proxy_worker_filename
4171+
proxy_client_src = do_replace(proxy_client_src, '<<< filename >>>', proxy_worker_filename)
41734172
return web_gl_client_src + '\n' + proxy_client_src
41744173

41754174

src/IDBStore.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* SPDX-License-Identifier: MIT
55
*/
66

7-
{
7+
var IDBStore = {
88
indexedDB: function() {
99
if (typeof indexedDB != 'undefined') return indexedDB;
1010
var ret = null;
@@ -102,5 +102,4 @@
102102
req.onerror = (error) => callback(error);
103103
});
104104
},
105-
}
106-
105+
};

src/library_idbstore.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44
* SPDX-License-Identifier: MIT
55
*/
66

7+
#include "IDBStore.js"
8+
79
var LibraryIDBStore = {
810
// A simple IDB-backed storage mechanism. Suitable for saving and loading large files asynchronously. This does
911
// *NOT* use the emscripten filesystem, intentionally, to avoid overhead. It lets you application define whatever
1012
// filesystem-like layer you want, with the overhead 100% controlled by you. At the extremes, you could either
1113
// just store large files, with almost no extra code; or you could implement a file b-tree using posix-compliant
1214
// filesystem on top.
13-
$IDBStore:
14-
#include IDBStore.js
15-
,
15+
$IDBStore: IDBStore,
1616
emscripten_idb_async_load__deps: ['$UTF8ToString', 'malloc', 'free'],
1717
emscripten_idb_async_load: function(db, id, arg, onload, onerror) {
1818
IDBStore.getFile(UTF8ToString(db), UTF8ToString(id), function(error, byteArray) {

src/proxyClient.js

Lines changed: 56 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,36 @@
66

77
// proxy to/from worker
88

9+
#if ENVIRONMENT_MAY_BE_NODE
10+
var ENVIRONMENT_IS_NODE = typeof process == 'object' && typeof process.versions == 'object' && typeof process.versions.node == 'string';
11+
if (ENVIRONMENT_IS_NODE) {
12+
let nodeWorkerThreads;
13+
try {
14+
nodeWorkerThreads = require('worker_threads');
15+
} catch (e) {
16+
console.error('The "worker_threads" module is not supported in this node.js build - perhaps a newer version is needed?');
17+
throw e;
18+
}
19+
global.Worker = nodeWorkerThreads.Worker;
20+
var Module = Module || {}
21+
} else
22+
#endif
923
if (typeof Module == 'undefined') {
1024
console.warn('no Module object defined - cannot proxy canvas rendering and input events, etc.');
1125
Module = {
1226
canvas: {
13-
addEventListener: function() {},
14-
getBoundingClientRect: function() { return { bottom: 0, height: 0, left: 0, right: 0, top: 0, width: 0 } },
27+
addEventListener: () => {},
28+
getBoundingClientRect: () => { return { bottom: 0, height: 0, left: 0, right: 0, top: 0, width: 0 }; },
1529
},
1630
};
1731
}
1832

1933
if (!Module.hasOwnProperty('print')) {
20-
Module['print'] = function(x) {
21-
console.log(x);
22-
};
34+
Module['print'] = (x) => console.log(x);
2335
}
2436

2537
if (!Module.hasOwnProperty('printErr')) {
26-
Module['printErr'] = function(x) {
27-
console.error(x);
28-
};
38+
Module['printErr'] = (x) => console.error(x);
2939
}
3040

3141
// utils
@@ -34,7 +44,7 @@ function FPSTracker(text) {
3444
var last = 0;
3545
var mean = 0;
3646
var counter = 0;
37-
this.tick = function() {
47+
this.tick = () => {
3848
var now = Date.now();
3949
if (last > 0) {
4050
var diff = now - last;
@@ -52,7 +62,7 @@ function FPSTracker(text) {
5262
function GenericTracker(text) {
5363
var mean = 0;
5464
var counter = 0;
55-
this.tick = function(value) {
65+
this.tick = (value) => {
5666
mean = 0.99*mean + 0.01*value;
5767
if (counter++ === 60) {
5868
counter = 0;
@@ -79,16 +89,18 @@ function renderFrame() {
7989
renderFrameData = null;
8090
}
8191

82-
window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
83-
window.webkitRequestAnimationFrame || window.msRequestAnimationFrame ||
84-
renderFrame;
92+
if (typeof window != 'undefined') {
93+
window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
94+
window.webkitRequestAnimationFrame || window.msRequestAnimationFrame ||
95+
renderFrame;
96+
}
8597

8698
/*
8799
(function() {
88100
var trueRAF = window.requestAnimationFrame;
89101
var tracker = new FPSTracker('client');
90-
window.requestAnimationFrame = function(func) {
91-
trueRAF(function() {
102+
window.requestAnimationFrame = (func) => {
103+
trueRAF(() => {
92104
tracker.tick();
93105
func();
94106
});
@@ -100,7 +112,7 @@ window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequest
100112

101113
// IDBStore
102114

103-
var IDBStore = {{{ IDBStore.js }}};
115+
#include "IDBStore.js"
104116

105117
// Frame throttling
106118

@@ -114,7 +126,7 @@ var SUPPORT_BASE64_EMBEDDING;
114126

115127
var filename;
116128
if (!filename) {
117-
filename = '{{{ filename }}}';
129+
filename = '<<< filename >>>';
118130
}
119131

120132
var workerURL = filename;
@@ -126,9 +138,14 @@ if (SUPPORT_BASE64_EMBEDDING) {
126138
}
127139
var worker = new Worker(workerURL);
128140

141+
#if ENVIRONMENT_MAY_BE_NODE
142+
if (ENVIRONMENT_IS_NODE) {
143+
worker.postMessage({target: 'worker-init'});
144+
} else {
145+
#endif
129146
WebGLClient.prefetch();
130147

131-
setTimeout(function() {
148+
setTimeout(() => {
132149
worker.postMessage({
133150
target: 'worker-init',
134151
width: Module.canvas.width,
@@ -138,10 +155,13 @@ setTimeout(function() {
138155
currentScriptUrl: filename,
139156
preMain: true });
140157
}, 0); // delay til next frame, to make sure html is ready
158+
#if ENVIRONMENT_MAY_BE_NODE
159+
}
160+
#endif
141161

142162
var workerResponded = false;
143163

144-
worker.onmessage = function worker_onmessage(event) {
164+
worker.onmessage = (event) => {
145165
//dump('\nclient got ' + JSON.stringify(event.data).substr(0, 150) + '\n');
146166
if (!workerResponded) {
147167
workerResponded = true;
@@ -230,7 +250,7 @@ worker.onmessage = function worker_onmessage(event) {
230250
case 'IDBStore': {
231251
switch (data.method) {
232252
case 'loadBlob': {
233-
IDBStore.getFile(data.db, data.id, function(error, blob) {
253+
IDBStore.getFile(data.db, data.id, (error, blob) => {
234254
worker.postMessage({
235255
target: 'IDBStore',
236256
method: 'response',
@@ -240,7 +260,7 @@ worker.onmessage = function worker_onmessage(event) {
240260
break;
241261
}
242262
case 'storeBlob': {
243-
IDBStore.setFile(data.db, data.id, data.blob, function(error) {
263+
IDBStore.setFile(data.db, data.id, data.blob, (error) => {
244264
worker.postMessage({
245265
target: 'IDBStore',
246266
method: 'response',
@@ -282,6 +302,10 @@ function cloneObject(event) {
282302
return ret;
283303
};
284304

305+
#if ENVIRONMENT_MAY_BE_NODE
306+
if (!ENVIRONMENT_IS_NODE) {
307+
#endif
308+
285309
// Only prevent default on backspace/tab because we don't want unexpected navigation.
286310
// Do not prevent default on the rest as we need the keypress event.
287311
function shouldPreventDefault(event) {
@@ -292,26 +316,30 @@ function shouldPreventDefault(event) {
292316
}
293317
};
294318

295-
['keydown', 'keyup', 'keypress', 'blur', 'visibilitychange'].forEach(function(event) {
296-
document.addEventListener(event, function(event) {
319+
320+
['keydown', 'keyup', 'keypress', 'blur', 'visibilitychange'].forEach((event) => {
321+
document.addEventListener(event, (event) => {
297322
worker.postMessage({ target: 'document', event: cloneObject(event) });
298-
323+
299324
if (shouldPreventDefault(event)) {
300325
event.preventDefault();
301326
}
302327
});
303328
});
304329

305-
['unload'].forEach(function(event) {
306-
window.addEventListener(event, function(event) {
330+
['unload'].forEach((event) => {
331+
window.addEventListener(event, (event) => {
307332
worker.postMessage({ target: 'window', event: cloneObject(event) });
308333
});
309334
});
310335

311-
['mousedown', 'mouseup', 'mousemove', 'DOMMouseScroll', 'mousewheel', 'mouseout'].forEach(function(event) {
312-
Module.canvas.addEventListener(event, function(event) {
336+
['mousedown', 'mouseup', 'mousemove', 'DOMMouseScroll', 'mousewheel', 'mouseout'].forEach((event) => {
337+
Module.canvas.addEventListener(event, (event) => {
313338
worker.postMessage({ target: 'canvas', event: cloneObject(event) });
314339
event.preventDefault();
315340
}, true);
316341
});
317342

343+
#if ENVIRONMENT_MAY_BE_NODE
344+
}
345+
#endif

src/proxyWorker.js

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,6 @@
44
* SPDX-License-Identifier: MIT
55
*/
66

7-
if (typeof console == 'undefined') {
8-
// we can't call Module.printErr because that might be circular
9-
var console = {
10-
log: function(x) {
11-
if (typeof dump == 'function') dump(`log: ${x}\n`);
12-
},
13-
debug: function(x) {
14-
if (typeof dump == 'function') dump(`debug: ${x}\n`);
15-
},
16-
info: function(x) {
17-
if (typeof dump == 'function') dump(`info: ${x}\n`);
18-
},
19-
warn: function(x) {
20-
if (typeof dump == 'function') dump(`warn: ${x}\n`);
21-
},
22-
error: function(x) {
23-
if (typeof dump == 'function') dump(`error: ${x}\n`);
24-
},
25-
};
26-
}
27-
287
/*
298
function proxify(object, nick) {
309
return new Proxy(object, {
@@ -37,6 +16,10 @@ function proxify(object, nick) {
3716
}
3817
*/
3918

19+
#if ENVIRONMENT_MAY_BE_NODE
20+
if (!ENVIRONMENT_IS_NODE) {
21+
#endif
22+
4023
function FPSTracker(text) {
4124
var last = 0;
4225
var mean = 0;
@@ -354,13 +337,13 @@ var screen = {
354337

355338
Module.canvas = document.createElement('canvas');
356339

357-
Module.setStatus = function(){};
340+
Module.setStatus = () => {};
358341

359-
out = function Module_print(x) {
342+
out = function(x) {
360343
//dump('OUT: ' + x + '\n');
361344
postMessage({ target: 'stdout', content: x });
362345
};
363-
err = function Module_printErr(x) {
346+
err = function(x) {
364347
//dump('ERR: ' + x + '\n');
365348
postMessage({ target: 'stderr', content: x });
366349
};
@@ -389,6 +372,10 @@ if (!ENVIRONMENT_IS_PTHREAD) {
389372
}
390373
#endif
391374

375+
#if ENVIRONMENT_MAY_BE_NODE
376+
}
377+
#endif
378+
392379
// buffer messages until the program starts to run
393380

394381
var messageBuffer = null;
@@ -479,6 +466,9 @@ function onMessageFromMainEmscriptenThread(message) {
479466
screen.width = Module.canvas.width_ = message.data.width;
480467
screen.height = Module.canvas.height_ = message.data.height;
481468
Module.canvas.boundingClientRect = message.data.boundingClientRect;
469+
#if ENVIRONMENT_MAY_BE_NODE
470+
if (ENVIRONMENT_IS_NODE)
471+
#endif
482472
document.URL = message.data.URL;
483473
#if PTHREADS
484474
currentScriptUrl = message.data.currentScriptUrl;

test/test_browser.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1759,14 +1759,12 @@ def test_glgears_pthreads(self, extra_args=[]): # noqa
17591759

17601760
@requires_graphics_hardware
17611761
@parameterized({
1762-
'': ([False],),
1762+
'': ([],),
17631763
# Enabling FULL_ES3 also enables ES2 automatically
1764-
'proxy': ([True],)
1764+
'proxy': (['--proxy-to-worker'],)
17651765
})
1766-
def test_glgears_long(self, proxy):
1767-
args = ['-DHAVE_BUILTIN_SINCOS', '-DLONGTEST', '-lGL', '-lglut', '-DANIMATE']
1768-
if proxy:
1769-
args += ['--proxy-to-worker']
1766+
def test_glgears_long(self, args):
1767+
args += ['-DHAVE_BUILTIN_SINCOS', '-DLONGTEST', '-lGL', '-lglut', '-DANIMATE']
17701768
self.btest('hello_world_gles.c', expected='0', args=args)
17711769

17721770
@requires_graphics_hardware

test/test_other.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13534,3 +13534,6 @@ def test_preload_module(self, args):
1353413534
def test_standalone_whole_archive(self):
1353513535
self.emcc_args += ['-sSTANDALONE_WASM', '-pthread', '-Wl,--whole-archive', '-lbulkmemory', '-lstandalonewasm', '-Wl,--no-whole-archive']
1353613536
self.do_runf(test_file('hello_world.c'))
13537+
13538+
def test_proxy_to_worker(self):
13539+
self.do_runf(test_file('hello_world.c'), emcc_args=['--proxy-to-worker'])

0 commit comments

Comments
 (0)