From 866676b3f3507738d902d3f150a8e8fb6cb5fad0 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Sat, 11 May 2019 13:38:34 -0700 Subject: [PATCH 01/25] horrible wip [ci skip] --- src/library_pthread.js | 9 +++++++ src/shell.js | 23 ++++++++++++++--- src/worker.js | 58 +++++++++++++++++++++++++++++++++++++----- 3 files changed, 81 insertions(+), 9 deletions(-) diff --git a/src/library_pthread.js b/src/library_pthread.js index 24f36596645e..7c42b2b82ca0 100644 --- a/src/library_pthread.js +++ b/src/library_pthread.js @@ -275,6 +275,7 @@ var LibraryPThread = { (function(worker) { worker.onmessage = function(e) { +console.log('parent got message ' + e); var d = e.data; // Sometimes we need to backproxy events to the calling thread (e.g. HTML5 DOM events handlers such as emscripten_set_mousemove_callback()), so keep track in a globally accessible variable about the thread that initiated the proxying. if (worker.pthread) PThread.currentProxiedOperationCallerThread = worker.pthread.threadInfoStruct; @@ -344,6 +345,14 @@ var LibraryPThread = { worker.onerror = function(e) { err('pthread sent an error! ' + e.filename + ':' + e.lineno + ': ' + e.message); }; + + if (ENVIRONMENT_IS_NODE) { +console.log('library_pthread worker on ' + [typeof worker.onmessage, typeof worker.onerror]); + worker.on('message', function(data) { + worker.onmessage({ data: data }); + }); + // note: adding onerror seems to only hurt on node.js + } }(worker)); // Allocate tempDoublePtr for the worker. This is done here on the worker's behalf, since we may need to do this statically diff --git a/src/shell.js b/src/shell.js index 4c73ccde75df..65f05d0b7bd9 100644 --- a/src/shell.js +++ b/src/shell.js @@ -82,6 +82,10 @@ if (Module['ENVIRONMENT']) { // In MODULARIZE mode _scriptDir needs to be captured already at the very top of the page immediately when the page is parsed, so it is generated there // before the page load. In non-MODULARIZE modes generate it here. var _scriptDir = (typeof document !== 'undefined' && document.currentScript) ? document.currentScript.src : undefined; +if (ENVIRONMENT_IS_NODE) { + _scriptDir = __filename; +} +console.log('_scriptDir: ' + _scriptDir); #endif // `/` should be present at the end if `scriptDirectory` is not empty @@ -164,6 +168,22 @@ if (ENVIRONMENT_IS_NODE) { }; Module['inspect'] = function () { return '[Emscripten Module object]'; }; + +#if USE_PTHREADS + var nodeWorkerThreads = require('worker_threads'); + Worker = nodeWorkerThreads.Worker; + + // Polyfill the performance object, which emscripten pthreads support + // depends on for good timing. + if (typeof performance === 'undefined') { + performance = { + now: function() { + return Date.now(); + } + }; + } +#endif + } else #endif // ENVIRONMENT_MAY_BE_NODE #if ENVIRONMENT_MAY_BE_SHELL @@ -343,9 +363,6 @@ assert(typeof Module['memoryInitializerPrefixURL'] === 'undefined', 'Module.memo assert(typeof Module['pthreadMainPrefixURL'] === 'undefined', 'Module.pthreadMainPrefixURL option was removed, use Module.locateFile instead'); assert(typeof Module['cdInitializerPrefixURL'] === 'undefined', 'Module.cdInitializerPrefixURL option was removed, use Module.locateFile instead'); assert(typeof Module['filePackagePrefixURL'] === 'undefined', 'Module.filePackagePrefixURL option was removed, use Module.locateFile instead'); -#if USE_PTHREADS -assert(ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER, 'Pthreads do not work in non-browser environments yet (need Web Workers, or an alternative to them)'); -#endif // USE_PTHREADS #endif // ASSERTIONS {{BODY}} diff --git a/src/worker.js b/src/worker.js index 7d35202ebd6f..2aadac548ce2 100644 --- a/src/worker.js +++ b/src/worker.js @@ -44,13 +44,16 @@ function assert(condition, text) { // When error objects propagate from Web Worker to main thread, they lose helpful call stack and thread ID information, so print out errors early here, // before that happens. -this.addEventListener('error', function(e) { - if (e.message.indexOf('SimulateInfiniteLoop') != -1) return e.preventDefault(); +// Note that addEventListener may not exist on some environments, like node. +if (typeof addEventListener === 'function') { + addEventListener('error', function(e) { + if (e.message.indexOf('SimulateInfiniteLoop') != -1) return e.preventDefault(); - var errorSource = ' in ' + e.filename + ':' + e.lineno + ':' + e.colno; - console.error('Pthread ' + selfThreadId + ' uncaught exception' + (e.filename || e.lineno || e.colno ? errorSource : "") + ': ' + e.message + '. Error object:'); - console.error(e.error); -}); + var errorSource = ' in ' + e.filename + ':' + e.lineno + ':' + e.colno; + console.error('Pthread ' + selfThreadId + ' uncaught exception' + (e.filename || e.lineno || e.colno ? errorSource : "") + ': ' + e.message + '. Error object:'); + console.error(e.error); + }); +} function threadPrint() { var text = Array.prototype.slice.call(arguments).join(' '); @@ -85,6 +88,7 @@ var wasmModule; var wasmMemory; this.onmessage = function(e) { + console.log('gottt ' + [e, JSON.stringify(e), typeof e.data.urlOrBlob, e.data.urlOrBlob]); try { if (e.data.cmd === 'load') { // Preload command that is called once per worker to parse and load the Emscripten code. // Initialize the thread-local field(s): @@ -230,3 +234,45 @@ this.onmessage = function(e) { throw e; } } + +// Node.js support +if (typeof require === 'function') { + // Create as web-worker-like an environment as we can. + self = { + location: { + href: __filename // XXX wat + } + }; + + var onmessage = this.onmessage; + + var nodeWorkerThreads = require('worker_threads'); + + Worker = nodeWorkerThreads.Worker; + + var parentPort = nodeWorkerThreads.parentPort; + + parentPort.on('message', function(data) { + onmessage({ data: data }); + }); + + var nodeFS = require('fs'); + + var nodeRead = function(filename) { + // TODO: convert to absolute path? + return nodeFS.readFileSync(filename).toString(); + }; + + function globalEval(x) { + eval.call(null, x); + } + + importScripts = function(f) { + globalEval(nodeRead(f)); + }; + + postMessage = function(msg) { + parentPort.postMessage(msg); + }; +} + From 5e80c1fe36d15ff295c2515865887f5ad1f0c88d Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Sat, 11 May 2019 15:33:32 -0700 Subject: [PATCH 02/25] bad wip exploration [ci skip] --- src/library_pthread.js | 25 ++++- src/postamble.js | 5 + src/shell.js | 2 +- src/worker.js | 27 ++++- tests/pthread/test_pthread_create.cpp | 156 ++++++++++++++------------ 5 files changed, 140 insertions(+), 75 deletions(-) diff --git a/src/library_pthread.js b/src/library_pthread.js index 7c42b2b82ca0..0aca06c47beb 100644 --- a/src/library_pthread.js +++ b/src/library_pthread.js @@ -275,7 +275,7 @@ var LibraryPThread = { (function(worker) { worker.onmessage = function(e) { -console.log('parent got message ' + e); +console.log('parent got message ' + [e, JSON.stringify(e)]); var d = e.data; // Sometimes we need to backproxy events to the calling thread (e.g. HTML5 DOM events handlers such as emscripten_set_mousemove_callback()), so keep track in a globally accessible variable about the thread that initiated the proxying. if (worker.pthread) PThread.currentProxiedOperationCallerThread = worker.pthread.threadInfoStruct; @@ -311,7 +311,9 @@ console.log('parent got message ' + e); delete worker.runPthread; } ++numWorkersLoaded; +console.log('loaded a warker'); if (numWorkersLoaded === numWorkers && onFinishedLoading) { +console.log('ALL WARKERS LADED! ' + onFinishedLoading); onFinishedLoading(); } } else if (d.cmd === 'print') { @@ -347,10 +349,23 @@ console.log('parent got message ' + e); }; if (ENVIRONMENT_IS_NODE) { + worker.ref(); // XXX FIXME wat console.log('library_pthread worker on ' + [typeof worker.onmessage, typeof worker.onerror]); worker.on('message', function(data) { worker.onmessage({ data: data }); }); + worker.on('close', function(data) { + console.log('worker closed ..!?'); + }); + worker.on('error', function(data) { + console.log('worker errored :('); + }); + worker.on('exit', function(data) { + console.log('werker exited'); + }); + worker.on('online', function(data) { + console.log('workerah is online!!!1!'); + }); // note: adding onerror seems to only hurt on node.js } }(worker)); @@ -434,6 +449,8 @@ console.log('library_pthread worker on ' + [typeof worker.onmessage, typeof work if (ENVIRONMENT_IS_PTHREAD) throw 'Internal Error! _spawn_thread() can only ever be called from main application thread!'; var worker = PThread.getNewWorker(); +console.log('spaewn ' + worker); + if (worker.pthread !== undefined) throw 'Internal error!'; if (!threadParams.pthread_ptr) throw 'Internal error, no pthread ptr!'; PThread.runningWorkers.push(worker); @@ -495,6 +512,7 @@ console.log('library_pthread worker on ' + [typeof worker.onmessage, typeof work worker.runPthread = function() { // Ask the worker to start executing its pthread entry point function. msg.time = performance.now(); +console.log('..tell child to run'); worker.postMessage(msg, threadParams.transferList); }; if (worker.loaded) { @@ -521,6 +539,7 @@ console.log('library_pthread worker on ' + [typeof worker.onmessage, typeof work pthread_create__deps: ['_spawn_thread', 'pthread_getschedparam', 'pthread_self'], pthread_create: function(pthread_ptr, attr, start_routine, arg) { +console.log('create a thradd'); if (typeof SharedArrayBuffer === 'undefined') { err('Current environment does not support SharedArrayBuffer, pthreads are not available!'); return {{{ cDefine('EAGAIN') }}}; @@ -623,6 +642,7 @@ console.log('library_pthread worker on ' + [typeof worker.onmessage, typeof work // If on the main thread, and accessing Canvas/OffscreenCanvas failed, abort with the detected error. if (error) return error; +console.log('..moar1'); var stackSize = 0; var stackBase = 0; @@ -733,6 +753,7 @@ console.log('library_pthread worker on ' + [typeof worker.onmessage, typeof work pthread_join__deps: ['_cleanup_thread', '_pthread_testcancel_js', 'emscripten_main_thread_process_queued_calls'], pthread_join: function(thread, status) { +console.log('joinn ' + [thread, status]); if (!thread) { err('pthread_join attempted on a null thread pointer!'); return ERRNO_CODES.ESRCH; @@ -756,6 +777,7 @@ console.log('library_pthread worker on ' + [typeof worker.onmessage, typeof work err('Attempted to join thread ' + thread + ', which was already detached!'); return ERRNO_CODES.EINVAL; // The thread is already detached, can no longer join it! } +console.log('3joinn loop'); for (;;) { var threadStatus = Atomics.load(HEAPU32, (thread + {{{ C_STRUCTS.pthread.threadStatus }}} ) >> 2); if (threadStatus == 1) { // Exited? @@ -765,6 +787,7 @@ console.log('library_pthread worker on ' + [typeof worker.onmessage, typeof work if (!ENVIRONMENT_IS_PTHREAD) __cleanup_thread(thread); else postMessage({ cmd: 'cleanupThread', thread: thread}); +console.log('4joinn stahp'); return 0; } // TODO HACK! Replace the _js variant with just _pthread_testcancel: diff --git a/src/postamble.js b/src/postamble.js index 1134ab744487..4026632f6760 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -164,6 +164,7 @@ dependenciesFulfilled = function runCaller() { #if HAS_MAIN Module['callMain'] = function callMain(args) { +console.log('callMain! ' + ENVIRONMENT_IS_NODE); #if ASSERTIONS assert(runDependencies == 0, 'cannot call main when async dependencies remain! (listen on Module["onRuntimeInitialized"])'); assert(__ATPRERUN__.length == 0, 'cannot call main when preRun functions remain to be called'); @@ -195,7 +196,9 @@ Module['callMain'] = function callMain(args) { // that will call the user's real main() for the application. var ret = Module['_proxy_main'](argc, argv, 0); #else +console.log('calle maienne! ' + ENVIRONMENT_IS_NODE); var ret = Module['_main'](argc, argv, 0); +console.log('called off'); #endif #if BENCHMARK @@ -244,6 +247,7 @@ Module['callMain'] = function callMain(args) { /** @type {function(Array=)} */ function run(args) { +console.log('run! ' + ENVIRONMENT_IS_NODE); args = args || Module['arguments']; if (runDependencies > 0) { @@ -263,6 +267,7 @@ function run(args) { if (Module['calledRun']) return; // run may have just been called through dependencies being fulfilled just in this very frame function doRun() { +console.log('doRun! ' + ENVIRONMENT_IS_NODE); if (Module['calledRun']) return; // run may have just been called while the async setStatus time below was happening Module['calledRun'] = true; diff --git a/src/shell.js b/src/shell.js index 65f05d0b7bd9..f7629c8561fb 100644 --- a/src/shell.js +++ b/src/shell.js @@ -82,10 +82,10 @@ if (Module['ENVIRONMENT']) { // In MODULARIZE mode _scriptDir needs to be captured already at the very top of the page immediately when the page is parsed, so it is generated there // before the page load. In non-MODULARIZE modes generate it here. var _scriptDir = (typeof document !== 'undefined' && document.currentScript) ? document.currentScript.src : undefined; + if (ENVIRONMENT_IS_NODE) { _scriptDir = __filename; } -console.log('_scriptDir: ' + _scriptDir); #endif // `/` should be present at the end if `scriptDirectory` is not empty diff --git a/src/worker.js b/src/worker.js index 2aadac548ce2..77fdab8f21f7 100644 --- a/src/worker.js +++ b/src/worker.js @@ -88,7 +88,7 @@ var wasmModule; var wasmMemory; this.onmessage = function(e) { - console.log('gottt ' + [e, JSON.stringify(e), typeof e.data.urlOrBlob, e.data.urlOrBlob]); + console.log('child gottt ' + [e, JSON.stringify(e), typeof e.data.urlOrBlob, e.data.urlOrBlob]); try { if (e.data.cmd === 'load') { // Preload command that is called once per worker to parse and load the Emscripten code. // Initialize the thread-local field(s): @@ -237,6 +237,15 @@ this.onmessage = function(e) { // Node.js support if (typeof require === 'function') { + + console.log('child wants to live forever ' + typeof setInterval); + setInterval(function() { + console.log('time in worker...'); + throw 55; + }, 2000); + + + // Create as web-worker-like an environment as we can. self = { location: { @@ -252,9 +261,24 @@ if (typeof require === 'function') { var parentPort = nodeWorkerThreads.parentPort; + parentPort.ref(); // stay alive XXX? +//process.exit(); XXX at the right time + parentPort.on('message', function(data) { onmessage({ data: data }); }); + parentPort.on('close', function(data) { + console.log('parentPort closed ..!?'); + }); + parentPort.on('error', function(data) { + console.log('parentPort errored :('); + }); + parentPort.on('exit', function(data) { + console.log('werker exited'); + }); + parentPort.on('online', function(data) { + console.log('parentPortah is online!!!1!'); + }); var nodeFS = require('fs'); @@ -275,4 +299,5 @@ if (typeof require === 'function') { parentPort.postMessage(msg); }; } +console.log('workers first event loop is going away now'); diff --git a/tests/pthread/test_pthread_create.cpp b/tests/pthread/test_pthread_create.cpp index e52f11d0ad6d..727b15786a17 100644 --- a/tests/pthread/test_pthread_create.cpp +++ b/tests/pthread/test_pthread_create.cpp @@ -10,49 +10,51 @@ #include #include -#define NUM_THREADS 8 +#define NUM_THREADS 1 int fib(int n) { - if (n <= 0) return 0; - if (n == 1) return 1; - return fib(n-1) + fib(n-2); + if (n <= 0) return 0; + if (n == 1) return 1; + return fib(n-1) + fib(n-2); } unsigned int global_shared_data[NUM_THREADS]; void *ThreadMain(void *arg) { - int idx = (int)arg; - unsigned int param = global_shared_data[idx]; +printf("ThreadMain! %d\n", (int)arg); + + int idx = (int)arg; + unsigned int param = global_shared_data[idx]; #define N 100 - EM_ASM(err('Thread idx '+$0+': sorting ' + $1 + ' numbers with param ' + $2 + '.'), idx, N, param); - - unsigned int n[N]; - for(unsigned int i = 0; i < N; ++i) - n[i] = (i + param) % N; // Create a shifted increasing sequence of numbers [0, N-1[ - - // Sort the sequence to ordered [0, N[ - for(unsigned int i = 0; i < N; ++i) - for(unsigned int j = i; j < N; ++j) - { - if (n[i] > n[j]) - { - unsigned int t = n[i]; - n[i] = n[j]; - n[j] = t; - } - } - // Ensure all elements are in place. - int numGood = 0; - for(unsigned int i = 0; i < N; ++i) - if (n[i] == i) ++numGood; - else EM_ASM(err('n['+$0+']='+$1), i, n[i]); - - EM_ASM(out('Thread idx ' + $0 + ' with param '+$1+': all done with result '+$2+'.'), idx, param, numGood); - pthread_exit((void*)numGood); + EM_ASM(err('Thread idx '+$0+': sorting ' + $1 + ' numbers with param ' + $2 + '.'), idx, N, param); + + unsigned int n[N]; + for(unsigned int i = 0; i < N; ++i) + n[i] = (i + param) % N; // Create a shifted increasing sequence of numbers [0, N-1[ + + // Sort the sequence to ordered [0, N[ + for(unsigned int i = 0; i < N; ++i) + for(unsigned int j = i; j < N; ++j) + { + if (n[i] > n[j]) + { + unsigned int t = n[i]; + n[i] = n[j]; + n[j] = t; + } + } + // Ensure all elements are in place. + int numGood = 0; + for(unsigned int i = 0; i < N; ++i) + if (n[i] == i) ++numGood; + else EM_ASM(err('n['+$0+']='+$1), i, n[i]); + + EM_ASM(out('Thread idx ' + $0 + ' with param '+$1+': all done with result '+$2+'.'), idx, param, numGood); + pthread_exit((void*)numGood); } pthread_t thread[NUM_THREADS]; @@ -61,54 +63,64 @@ int numThreadsToCreate = 1000; void CreateThread(int i) { - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); - static int counter = 1; - global_shared_data[i] = (counter++ * 12141231) & 0x7FFFFFFF; // Arbitrary random'ish data for perturbing the sort for this thread task. -// EM_ASM(out('Main: Creating thread idx ' + $0 + ' (param ' + $1 + ')'), i, global_shared_data[i]); - int rc = pthread_create(&thread[i], &attr, ThreadMain, (void*)i); - assert(rc == 0); - pthread_attr_destroy(&attr); + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + static int counter = 1; + global_shared_data[i] = (counter++ * 12141231) & 0x7FFFFFFF; // Arbitrary random'ish data for perturbing the sort for this thread task. +// EM_ASM(out('Main: Creating thread idx ' + $0 + ' (param ' + $1 + ')'), i, global_shared_data[i]); + int rc = pthread_create(&thread[i], &attr, ThreadMain, (void*)i); + assert(rc == 0); + pthread_attr_destroy(&attr); } int main() { - if (!emscripten_has_threading_support()) - { +printf("mainne1\n"); + if (!emscripten_has_threading_support()) + { #ifdef REPORT_RESULT - REPORT_RESULT(0); + REPORT_RESULT(0); #endif - printf("Skipped: Threading is not supported.\n"); - return 0; - } - - // Create initial threads. - for(int i = 0; i < NUM_THREADS; ++i) - CreateThread(i); - - // Join all threads and create more. - while (numThreadsToCreate > 0) + printf("Skipped: Threading is not supported.\n"); + return 0; + } +printf("mainne2\n"); + + // Create initial threads. + for(int i = 0; i < NUM_THREADS; ++i) { +printf("mainne3\n"); + CreateThread(i); + } +printf("mainne4\n"); + + // Join all threads and create more. + while (numThreadsToCreate > 0) + { +printf("mainne5\n"); + for(int i = 0; i < NUM_THREADS; ++i) + { +printf("mainne6\n"); + if (thread[i]) + { +printf("mainne7\n"); + int status; +// synchronous blocking here maybe is what prevents node from sending the thread creation postMessages :( + int rc = pthread_join(thread[i], (void**)&status); +printf("mainne8\n"); + assert(rc == 0); + EM_ASM(err('Main: Joined thread idx ' + $0 + ' (param ' + $1 + ') with status ' + $2), i, global_shared_data[i], (int)status); + assert(status == N); + thread[i] = 0; + if (numThreadsToCreate > 0) { - for(int i = 0; i < NUM_THREADS; ++i) - { - if (thread[i]) - { - int status; - int rc = pthread_join(thread[i], (void**)&status); - assert(rc == 0); - EM_ASM(err('Main: Joined thread idx ' + $0 + ' (param ' + $1 + ') with status ' + $2), i, global_shared_data[i], (int)status); - assert(status == N); - thread[i] = 0; - if (numThreadsToCreate > 0) - { - --numThreadsToCreate; - CreateThread(i); - } - } - } - } + --numThreadsToCreate; + CreateThread(i); + } + } + } + } #ifdef REPORT_RESULT - REPORT_RESULT(0); + REPORT_RESULT(0); #endif } From 6c427c7ba615f258f9501c5a513bd4cc3cd85d5b Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Sat, 11 May 2019 19:31:39 -0700 Subject: [PATCH 03/25] note the issue [ci skip] --- src/library_pthread.js | 3 --- src/worker.js | 8 ++++++ tests/pthread/test_pthread_create.cpp | 37 ++++++--------------------- 3 files changed, 16 insertions(+), 32 deletions(-) diff --git a/src/library_pthread.js b/src/library_pthread.js index 0aca06c47beb..c80caddafd4e 100644 --- a/src/library_pthread.js +++ b/src/library_pthread.js @@ -357,9 +357,6 @@ console.log('library_pthread worker on ' + [typeof worker.onmessage, typeof work worker.on('close', function(data) { console.log('worker closed ..!?'); }); - worker.on('error', function(data) { - console.log('worker errored :('); - }); worker.on('exit', function(data) { console.log('werker exited'); }); diff --git a/src/worker.js b/src/worker.js index 77fdab8f21f7..f403edc07925 100644 --- a/src/worker.js +++ b/src/worker.js @@ -298,6 +298,14 @@ if (typeof require === 'function') { postMessage = function(msg) { parentPort.postMessage(msg); }; + + if (typeof performance === 'undefined') { + performance = { + now: function() { + return Date.now(); + } + }; + } } console.log('workers first event loop is going away now'); diff --git a/tests/pthread/test_pthread_create.cpp b/tests/pthread/test_pthread_create.cpp index 727b15786a17..a123ac28a472 100644 --- a/tests/pthread/test_pthread_create.cpp +++ b/tests/pthread/test_pthread_create.cpp @@ -74,6 +74,10 @@ void CreateThread(int i) pthread_attr_destroy(&attr); } +void mainn() { + printf("main iter\n"); +} + int main() { printf("mainne1\n"); @@ -94,33 +98,8 @@ printf("mainne3\n"); } printf("mainne4\n"); - // Join all threads and create more. - while (numThreadsToCreate > 0) - { -printf("mainne5\n"); - for(int i = 0; i < NUM_THREADS; ++i) - { -printf("mainne6\n"); - if (thread[i]) - { -printf("mainne7\n"); - int status; -// synchronous blocking here maybe is what prevents node from sending the thread creation postMessages :( - int rc = pthread_join(thread[i], (void**)&status); -printf("mainne8\n"); - assert(rc == 0); - EM_ASM(err('Main: Joined thread idx ' + $0 + ' (param ' + $1 + ') with status ' + $2), i, global_shared_data[i], (int)status); - assert(status == N); - thread[i] = 0; - if (numThreadsToCreate > 0) - { - --numThreadsToCreate; - CreateThread(i); - } - } - } - } -#ifdef REPORT_RESULT - REPORT_RESULT(0); -#endif + // synchronous here would not let the worker start - + // the postMessage is only sent at the end of the event + // loop it seems :( + emscripten_set_main_loop(mainn, 1, 1); } From 3604163d363dd767738f614eb1551439086fcf54 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 8 Jul 2019 11:26:42 -0700 Subject: [PATCH 04/25] wip [ci skip] --- src/library_pthread.js | 11 ----------- src/postamble.js | 5 ----- 2 files changed, 16 deletions(-) diff --git a/src/library_pthread.js b/src/library_pthread.js index c80caddafd4e..87a1311c417b 100644 --- a/src/library_pthread.js +++ b/src/library_pthread.js @@ -275,7 +275,6 @@ var LibraryPThread = { (function(worker) { worker.onmessage = function(e) { -console.log('parent got message ' + [e, JSON.stringify(e)]); var d = e.data; // Sometimes we need to backproxy events to the calling thread (e.g. HTML5 DOM events handlers such as emscripten_set_mousemove_callback()), so keep track in a globally accessible variable about the thread that initiated the proxying. if (worker.pthread) PThread.currentProxiedOperationCallerThread = worker.pthread.threadInfoStruct; @@ -311,9 +310,7 @@ console.log('parent got message ' + [e, JSON.stringify(e)]); delete worker.runPthread; } ++numWorkersLoaded; -console.log('loaded a warker'); if (numWorkersLoaded === numWorkers && onFinishedLoading) { -console.log('ALL WARKERS LADED! ' + onFinishedLoading); onFinishedLoading(); } } else if (d.cmd === 'print') { @@ -350,7 +347,6 @@ console.log('ALL WARKERS LADED! ' + onFinishedLoading); if (ENVIRONMENT_IS_NODE) { worker.ref(); // XXX FIXME wat -console.log('library_pthread worker on ' + [typeof worker.onmessage, typeof worker.onerror]); worker.on('message', function(data) { worker.onmessage({ data: data }); }); @@ -446,7 +442,6 @@ console.log('library_pthread worker on ' + [typeof worker.onmessage, typeof work if (ENVIRONMENT_IS_PTHREAD) throw 'Internal Error! _spawn_thread() can only ever be called from main application thread!'; var worker = PThread.getNewWorker(); -console.log('spaewn ' + worker); if (worker.pthread !== undefined) throw 'Internal error!'; if (!threadParams.pthread_ptr) throw 'Internal error, no pthread ptr!'; @@ -509,7 +504,6 @@ console.log('spaewn ' + worker); worker.runPthread = function() { // Ask the worker to start executing its pthread entry point function. msg.time = performance.now(); -console.log('..tell child to run'); worker.postMessage(msg, threadParams.transferList); }; if (worker.loaded) { @@ -536,7 +530,6 @@ console.log('..tell child to run'); pthread_create__deps: ['_spawn_thread', 'pthread_getschedparam', 'pthread_self'], pthread_create: function(pthread_ptr, attr, start_routine, arg) { -console.log('create a thradd'); if (typeof SharedArrayBuffer === 'undefined') { err('Current environment does not support SharedArrayBuffer, pthreads are not available!'); return {{{ cDefine('EAGAIN') }}}; @@ -639,7 +632,6 @@ console.log('create a thradd'); // If on the main thread, and accessing Canvas/OffscreenCanvas failed, abort with the detected error. if (error) return error; -console.log('..moar1'); var stackSize = 0; var stackBase = 0; @@ -750,7 +742,6 @@ console.log('..moar1'); pthread_join__deps: ['_cleanup_thread', '_pthread_testcancel_js', 'emscripten_main_thread_process_queued_calls'], pthread_join: function(thread, status) { -console.log('joinn ' + [thread, status]); if (!thread) { err('pthread_join attempted on a null thread pointer!'); return ERRNO_CODES.ESRCH; @@ -774,7 +765,6 @@ console.log('joinn ' + [thread, status]); err('Attempted to join thread ' + thread + ', which was already detached!'); return ERRNO_CODES.EINVAL; // The thread is already detached, can no longer join it! } -console.log('3joinn loop'); for (;;) { var threadStatus = Atomics.load(HEAPU32, (thread + {{{ C_STRUCTS.pthread.threadStatus }}} ) >> 2); if (threadStatus == 1) { // Exited? @@ -784,7 +774,6 @@ console.log('3joinn loop'); if (!ENVIRONMENT_IS_PTHREAD) __cleanup_thread(thread); else postMessage({ cmd: 'cleanupThread', thread: thread}); -console.log('4joinn stahp'); return 0; } // TODO HACK! Replace the _js variant with just _pthread_testcancel: diff --git a/src/postamble.js b/src/postamble.js index 4026632f6760..1134ab744487 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -164,7 +164,6 @@ dependenciesFulfilled = function runCaller() { #if HAS_MAIN Module['callMain'] = function callMain(args) { -console.log('callMain! ' + ENVIRONMENT_IS_NODE); #if ASSERTIONS assert(runDependencies == 0, 'cannot call main when async dependencies remain! (listen on Module["onRuntimeInitialized"])'); assert(__ATPRERUN__.length == 0, 'cannot call main when preRun functions remain to be called'); @@ -196,9 +195,7 @@ console.log('callMain! ' + ENVIRONMENT_IS_NODE); // that will call the user's real main() for the application. var ret = Module['_proxy_main'](argc, argv, 0); #else -console.log('calle maienne! ' + ENVIRONMENT_IS_NODE); var ret = Module['_main'](argc, argv, 0); -console.log('called off'); #endif #if BENCHMARK @@ -247,7 +244,6 @@ console.log('called off'); /** @type {function(Array=)} */ function run(args) { -console.log('run! ' + ENVIRONMENT_IS_NODE); args = args || Module['arguments']; if (runDependencies > 0) { @@ -267,7 +263,6 @@ console.log('run! ' + ENVIRONMENT_IS_NODE); if (Module['calledRun']) return; // run may have just been called through dependencies being fulfilled just in this very frame function doRun() { -console.log('doRun! ' + ENVIRONMENT_IS_NODE); if (Module['calledRun']) return; // run may have just been called while the async setStatus time below was happening Module['calledRun'] = true; From a90fc1b6509dc86972a663ff17aa7271b61b3ab6 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 24 Sep 2019 14:06:13 -0700 Subject: [PATCH 05/25] wip [ci skip] --- src/node_shell_read.js | 30 ++++++++++ src/runtime_init_memory.js | 1 + src/shell.js | 100 ++++---------------------------- src/web_or_worker_shell_read.js | 63 ++++++++++++++++++++ 4 files changed, 105 insertions(+), 89 deletions(-) create mode 100644 src/node_shell_read.js create mode 100644 src/web_or_worker_shell_read.js diff --git a/src/node_shell_read.js b/src/node_shell_read.js new file mode 100644 index 000000000000..6045597c43e6 --- /dev/null +++ b/src/node_shell_read.js @@ -0,0 +1,30 @@ + // Expose functionality in the same simple way that the shells work + // Note that we pollute the global namespace here, otherwise we break in node + var nodeFS; + var nodePath; + + read_ = function shell_read(filename, binary) { + var ret; +#if SUPPORT_BASE64_EMBEDDING + ret = tryParseAsDataURI(filename); + if (!ret) { +#endif + if (!nodeFS) nodeFS = require('fs'); + if (!nodePath) nodePath = require('path'); + filename = nodePath['normalize'](filename); + ret = nodeFS['readFileSync'](filename); +#if SUPPORT_BASE64_EMBEDDING + } +#endif + return binary ? ret : ret.toString(); + }; + + readBinary = function readBinary(filename) { + var ret = read_(filename, true); + if (!ret.buffer) { + ret = new Uint8Array(ret); + } + assert(ret.buffer); + return ret; + }; + diff --git a/src/runtime_init_memory.js b/src/runtime_init_memory.js index 0289bbae47b7..a157114c6220 100644 --- a/src/runtime_init_memory.js +++ b/src/runtime_init_memory.js @@ -36,6 +36,7 @@ if (ENVIRONMENT_IS_PTHREAD) { }); #if USE_PTHREADS assert(wasmMemory.buffer instanceof SharedArrayBuffer, 'requested a shared WebAssembly.Memory but the returned buffer is not a SharedArrayBuffer, indicating that while the browser has SharedArrayBuffer it does not have WebAssembly threads support - you may need to set a flag'); + // e.g. on node: --experimental-wasm-threads --experimental-wasm-bulk-memory #endif } diff --git a/src/shell.js b/src/shell.js index 2e654a861fa2..5bb5065e3cfd 100644 --- a/src/shell.js +++ b/src/shell.js @@ -128,35 +128,7 @@ if (ENVIRONMENT_IS_NODE) { #endif scriptDirectory = __dirname + '/'; - // Expose functionality in the same simple way that the shells work - // Note that we pollute the global namespace here, otherwise we break in node - var nodeFS; - var nodePath; - - read_ = function shell_read(filename, binary) { - var ret; -#if SUPPORT_BASE64_EMBEDDING - ret = tryParseAsDataURI(filename); - if (!ret) { -#endif - if (!nodeFS) nodeFS = require('fs'); - if (!nodePath) nodePath = require('path'); - filename = nodePath['normalize'](filename); - ret = nodeFS['readFileSync'](filename); -#if SUPPORT_BASE64_EMBEDDING - } -#endif - return binary ? ret : ret.toString(); - }; - - readBinary = function readBinary(filename) { - var ret = read_(filename, true); - if (!ret.buffer) { - ret = new Uint8Array(ret); - } - assert(ret.buffer); - return ret; - }; +#include "node_shell_read.js" if (process['argv'].length > 1) { thisProgram = process['argv'][1].replace(/\\/g, '/'); @@ -295,68 +267,18 @@ if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { #endif #endif - read_ = function shell_read(url) { -#if SUPPORT_BASE64_EMBEDDING - try { -#endif - var xhr = new XMLHttpRequest(); - xhr.open('GET', url, false); - xhr.send(null); - return xhr.responseText; -#if SUPPORT_BASE64_EMBEDDING - } catch (err) { - var data = tryParseAsDataURI(url); - if (data) { - return intArrayToString(data); - } - throw err; - } -#endif - }; + // Differentiate the Web Worker from the Node Worker case, as reading must + // be done differently. + if (!ENVIRONMENT_HAS_NODE) { + +#include "web_or_worker_shell_read.js" + + } else { + +#include "node_shell_read.js" - if (ENVIRONMENT_IS_WORKER) { - readBinary = function readBinary(url) { -#if SUPPORT_BASE64_EMBEDDING - try { -#endif - var xhr = new XMLHttpRequest(); - xhr.open('GET', url, false); - xhr.responseType = 'arraybuffer'; - xhr.send(null); - return new Uint8Array(xhr.response); -#if SUPPORT_BASE64_EMBEDDING - } catch (err) { - var data = tryParseAsDataURI(url); - if (data) { - return data; - } - throw err; - } -#endif - }; } - readAsync = function readAsync(url, onload, onerror) { - var xhr = new XMLHttpRequest(); - xhr.open('GET', url, true); - xhr.responseType = 'arraybuffer'; - xhr.onload = function xhr_onload() { - if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { // file URLs can return 0 - onload(xhr.response); - return; - } -#if SUPPORT_BASE64_EMBEDDING - var data = tryParseAsDataURI(url); - if (data) { - onload(data.buffer); - return; - } -#endif - onerror(); - }; - xhr.onerror = onerror; - xhr.send(null); - }; setWindowTitle = function(title) { document.title = title }; } else @@ -407,7 +329,7 @@ assert(typeof Module['setWindowTitle'] === 'undefined', 'Module.setWindowTitle o // TODO: add when SDL2 is fixed {{{ makeRemovedModuleAPIAssert('setWindowTitle') }}} #if USE_PTHREADS -assert(ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER, 'Pthreads do not work in non-browser environments yet (need Web Workers, or an alternative to them)'); +assert(ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER || ENVIRONMENT_IS_NODE, 'Pthreads do not work in this environment yet (need Web Workers, or an alternative to them)'); #endif // USE_PTHREADS #endif // ASSERTIONS diff --git a/src/web_or_worker_shell_read.js b/src/web_or_worker_shell_read.js new file mode 100644 index 000000000000..6c7c0560aa26 --- /dev/null +++ b/src/web_or_worker_shell_read.js @@ -0,0 +1,63 @@ + read_ = function shell_read(url) { +#if SUPPORT_BASE64_EMBEDDING + try { +#endif + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, false); + xhr.send(null); + return xhr.responseText; +#if SUPPORT_BASE64_EMBEDDING + } catch (err) { + var data = tryParseAsDataURI(url); + if (data) { + return intArrayToString(data); + } + throw err; + } +#endif + }; + + if (ENVIRONMENT_IS_WORKER) { + readBinary = function readBinary(url) { +#if SUPPORT_BASE64_EMBEDDING + try { +#endif + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, false); + xhr.responseType = 'arraybuffer'; + xhr.send(null); + return new Uint8Array(xhr.response); +#if SUPPORT_BASE64_EMBEDDING + } catch (err) { + var data = tryParseAsDataURI(url); + if (data) { + return data; + } + throw err; + } +#endif + }; + } + + readAsync = function readAsync(url, onload, onerror) { + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, true); + xhr.responseType = 'arraybuffer'; + xhr.onload = function xhr_onload() { + if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { // file URLs can return 0 + onload(xhr.response); + return; + } +#if SUPPORT_BASE64_EMBEDDING + var data = tryParseAsDataURI(url); + if (data) { + onload(data.buffer); + return; + } +#endif + onerror(); + }; + xhr.onerror = onerror; + xhr.send(null); + }; + From 7a3716d7ccbbc472282016a7df6a2aa1636b8ff0 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 26 Sep 2019 15:23:28 -0700 Subject: [PATCH 06/25] more logging [ci skip] --- src/library_pthread.js | 3 +++ src/worker.js | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/src/library_pthread.js b/src/library_pthread.js index d226e19fe36f..419d6e03d3ac 100644 --- a/src/library_pthread.js +++ b/src/library_pthread.js @@ -506,10 +506,13 @@ var LibraryPThread = { offscreenCanvases: threadParams.offscreenCanvases, #endif }; +console.log('parent ready to run pthread'); worker.runPthread = function() { // Ask the worker to start executing its pthread entry point function. +console.log(' posting to run pthread'); msg.time = performance.now(); worker.postMessage(msg, threadParams.transferList); +console.log(' postedded'); }; if (worker.loaded) { worker.runPthread(); diff --git a/src/worker.js b/src/worker.js index 77ffefb4f2b4..a5c7876fa2e2 100644 --- a/src/worker.js +++ b/src/worker.js @@ -119,6 +119,7 @@ var wasmOffsetData; this.onmessage = function(e) { console.log('child gottt ' + [e, JSON.stringify(e), typeof e.data.urlOrBlob, e.data.urlOrBlob]); try { +console.log('child command:', e.data.cmd); if (e.data.cmd === 'load') { // Preload command that is called once per worker to parse and load the Emscripten code. #if !WASM_BACKEND // Initialize the thread-local field(s): @@ -183,7 +184,9 @@ this.onmessage = function(e) { }); #else if (typeof e.data.urlOrBlob === 'string') { +console.log('child loading scripts'); importScripts(e.data.urlOrBlob); +console.log('child loaded scripts'); } else { var objectUrl = URL.createObjectURL(e.data.urlOrBlob); importScripts(objectUrl); @@ -365,6 +368,7 @@ if (typeof require === 'function') { }; function globalEval(x) { + global.require = require; eval.call(null, x); } From 257b02baeb077e978856d930e283295438abe6a1 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 26 Sep 2019 15:43:43 -0700 Subject: [PATCH 07/25] more [ci skip] --- src/settings.js | 5 +++++ src/worker.js | 1 + 2 files changed, 6 insertions(+) diff --git a/src/settings.js b/src/settings.js index c0313efffcd4..8e824a1480bd 100644 --- a/src/settings.js +++ b/src/settings.js @@ -1662,3 +1662,8 @@ var LEGACY_SETTINGS = [ ['ERROR_ON_MISSING_LIBRARIES', [1], 'missing libraries are always an error now'], ['EMITTING_JS', [1], 'The new STANDALONE_WASM flag replaces this (replace EMITTING_JS=0 with STANDALONE_WASM=1)'], ]; + + +// ./emcc tests/pthread/test_pthread_create.cpp -s USE_PTHREADS -s PTHREAD_POOL_SIZE=1 -g +// ~/Downloads/node-v13.0.0-nightly20190924e078e482c5-linux-x64/bin/node --experimental-wasm-threads --experimental-wasm-bulk-memory a.out.js + diff --git a/src/worker.js b/src/worker.js index a5c7876fa2e2..66ea883f6141 100644 --- a/src/worker.js +++ b/src/worker.js @@ -369,6 +369,7 @@ if (typeof require === 'function') { function globalEval(x) { global.require = require; + global.Module = Module; eval.call(null, x); } From 5a8c86db5d955d45ff54b6b73d7d02e79b314f95 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 30 Oct 2019 14:27:22 -0700 Subject: [PATCH 08/25] exit pthread properly on node [ci skip] --- src/library_pthread.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/library_pthread.js b/src/library_pthread.js index aa67261e500c..571a3c3f1b45 100644 --- a/src/library_pthread.js +++ b/src/library_pthread.js @@ -937,6 +937,11 @@ console.log(' postedded'); else PThread.threadExit(status); #if WASM_BACKEND // pthread_exit is marked noReturn, so we must not return from it. + if (ENVIRONMENT_HAS_NODE) { + // exit the pthread properly on node, as a normal JS exception will halt + // the entire application. + process.exit(status); + } throw 'pthread_exit'; #endif }, From 4120e71bdb44936be19a2e88d6673d8667089d92 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 30 Oct 2019 14:45:06 -0700 Subject: [PATCH 09/25] frist test --- tests/core/pthread/create.cpp | 62 +++++++++++++++++++++++++++++++++++ tests/core/pthread/create.txt | 1 + tests/test_core.py | 12 +++++++ 3 files changed, 75 insertions(+) create mode 100644 tests/core/pthread/create.cpp create mode 100644 tests/core/pthread/create.txt diff --git a/tests/core/pthread/create.cpp b/tests/core/pthread/create.cpp new file mode 100644 index 000000000000..0cfc1a4eb60e --- /dev/null +++ b/tests/core/pthread/create.cpp @@ -0,0 +1,62 @@ +// Copyright 2019 The Emscripten Authors. All rights reserved. +// Emscripten is available under two separate licenses, the MIT license and the +// University of Illinois/NCSA Open Source License. Both these licenses can be +// found in the LICENSE file. + +#include +#include +#include +#include +#include + +#include + +#define NUM_THREADS 2 +#define TOTAL 100 + +static std::atomic sum; + +void *ThreadMain(void *arg) { + for (int i = 0; i < TOTAL; i++) { + sum++; + // wait for a change, so we see interleaved processing. + int last = sum.load(); + while (sum.load() == last) {} + } + pthread_exit((void*)TOTAL); +} + +pthread_t thread[NUM_THREADS]; + +void CreateThread(int i) +{ + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + static int counter = 1; + int rc = pthread_create(&thread[i], &attr, ThreadMain, (void*)i); + assert(rc == 0); + pthread_attr_destroy(&attr); +} + +void mainn() { + static int main_adds = 0; + int worker_adds = sum.load() - main_adds; + sum++; + main_adds++; + printf("main iter %d : %d\n", main_adds, worker_adds); + if (worker_adds == NUM_THREADS * TOTAL) { + emscripten_cancel_main_loop(); + printf("done!\n"); + } +} + +int main() { + // Create initial threads. + for(int i = 0; i < NUM_THREADS; ++i) { + CreateThread(i); + } + + // Node.js requires the event loop to be reached for the worker to start up. + emscripten_set_main_loop(mainn, 0, 0); +} diff --git a/tests/core/pthread/create.txt b/tests/core/pthread/create.txt new file mode 100644 index 000000000000..3b17e24adf23 --- /dev/null +++ b/tests/core/pthread/create.txt @@ -0,0 +1 @@ +done! diff --git a/tests/test_core.py b/tests/test_core.py index f79efeebada3..89f27293b830 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -150,6 +150,13 @@ def decorated(self): return decorated +def node_pthreads(f): + def decorated(self): + self.set_setting('USE_PTHREADS', 1) + f(self, js_engines=[NODE_JS + ['--experimental-wasm-threads', '--experimental-wasm-bulk-memory']]) + return decorated + + # A simple check whether the compiler arguments cause optimization. def is_optimizing(args): return '-O' in str(args) and '-O0' not in args @@ -8364,6 +8371,11 @@ def test_fpic_static(self): self.emcc_args.remove('-Werror') self.do_run_in_out_file_test('tests', 'core', 'test_hello_world') + @node_pthreads + def test_pthreads_hello(self, js_engines): + self.do_run_in_out_file_test('tests', 'core', 'pthread', 'create', + js_engines=js_engines) + # Generate tests for everything def make_run(name, emcc_args, settings=None, env=None): From 1d85ac00ef7a504512cb8b931267caa514323a98 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 30 Oct 2019 14:45:44 -0700 Subject: [PATCH 10/25] TODO [ci skip] --- src/library_pthread.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/library_pthread.js b/src/library_pthread.js index 571a3c3f1b45..8dca6fdcae89 100644 --- a/src/library_pthread.js +++ b/src/library_pthread.js @@ -434,7 +434,7 @@ var LibraryPThread = { }; if (ENVIRONMENT_IS_NODE) { - worker.ref(); // XXX FIXME wat + worker.ref(); // XXX FIXME wat? worker.on('message', function(data) { worker.onmessage({ data: data }); }); From b6003c2e9fd21ecc1a2a74150944411ebb786d69 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 30 Oct 2019 14:47:26 -0700 Subject: [PATCH 11/25] testing --- tests/test_core.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_core.py b/tests/test_core.py index 89f27293b830..24768cccf8de 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -153,6 +153,10 @@ def decorated(self): def node_pthreads(f): def decorated(self): self.set_setting('USE_PTHREADS', 1) + if not self.is_wasm_backend(): + self.skipTest('node pthreads only supported on wasm backend') + if not self.get_setting('WASM'): + self.skipTest("pthreads doesn't work in non-wasm yet") f(self, js_engines=[NODE_JS + ['--experimental-wasm-threads', '--experimental-wasm-bulk-memory']]) return decorated From 9619833b794eeff620eaa52357f3a79e2b3cfa53 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 30 Oct 2019 14:54:53 -0700 Subject: [PATCH 12/25] cleanups --- src/library_pthread.js | 17 ++--------------- src/runtime_init_memory.js | 9 +++++++-- src/settings.js | 5 ----- src/worker.js | 23 +---------------------- 4 files changed, 10 insertions(+), 44 deletions(-) diff --git a/src/library_pthread.js b/src/library_pthread.js index 8dca6fdcae89..a46249d9330a 100644 --- a/src/library_pthread.js +++ b/src/library_pthread.js @@ -433,21 +433,11 @@ var LibraryPThread = { err('pthread sent an error! ' + e.filename + ':' + e.lineno + ': ' + e.message); }; - if (ENVIRONMENT_IS_NODE) { - worker.ref(); // XXX FIXME wat? + if (ENVIRONMENT_HAS_NODE) { + //worker.ref(); // TODO: investigate when we need this worker.on('message', function(data) { worker.onmessage({ data: data }); }); - worker.on('close', function(data) { - console.log('worker closed ..!?'); - }); - worker.on('exit', function(data) { - console.log('werker exited'); - }); - worker.on('online', function(data) { - console.log('workerah is online!!!1!'); - }); - // note: adding onerror seems to only hurt on node.js } }(worker)); } // for each worker @@ -581,13 +571,10 @@ var LibraryPThread = { 'offscreenCanvases': threadParams.offscreenCanvases, #endif }; -console.log('parent ready to run pthread'); worker.runPthread = function() { // Ask the worker to start executing its pthread entry point function. -console.log(' posting to run pthread'); msg.time = performance.now(); worker.postMessage(msg, threadParams.transferList); -console.log(' postedded'); }; if (worker.loaded) { worker.runPthread(); diff --git a/src/runtime_init_memory.js b/src/runtime_init_memory.js index d9645417d8ad..dfbcd92c46d2 100644 --- a/src/runtime_init_memory.js +++ b/src/runtime_init_memory.js @@ -31,8 +31,13 @@ if (ENVIRONMENT_IS_PTHREAD) { #endif }); #if USE_PTHREADS - assert(wasmMemory.buffer instanceof SharedArrayBuffer, 'requested a shared WebAssembly.Memory but the returned buffer is not a SharedArrayBuffer, indicating that while the browser has SharedArrayBuffer it does not have WebAssembly threads support - you may need to set a flag'); - // e.g. on node: --experimental-wasm-threads --experimental-wasm-bulk-memory + if (!(wasmMemory.buffer instanceof SharedArrayBuffer)) { + err('requested a shared WebAssembly.Memory but the returned buffer is not a SharedArrayBuffer, indicating that while the browser has SharedArrayBuffer it does not have WebAssembly threads support - you may need to set a flag'); + if (ENVIRONMENT_HAS_NODE) { + console.log('(on node you may need: --experimental-wasm-threads --experimental-wasm-bulk-memory and also use a recent version)'); + } + throw Error('bad memory'); + } #endif } diff --git a/src/settings.js b/src/settings.js index a8f509d94f44..6401f0d5df96 100644 --- a/src/settings.js +++ b/src/settings.js @@ -1653,8 +1653,3 @@ var LEGACY_SETTINGS = [ ['EMITTING_JS', [1], 'The new STANDALONE_WASM flag replaces this (replace EMITTING_JS=0 with STANDALONE_WASM=1)'], ['SKIP_STACK_IN_SMALL', [0, 1], 'SKIP_STACK_IN_SMALL is no longer needed as the backend can optimize it directly'], ]; - - -// ./emcc tests/pthread/test_pthread_create.cpp -s USE_PTHREADS -s PTHREAD_POOL_SIZE=1 -g -// ~/Downloads/node-v13.0.0-nightly20190924e078e482c5-linux-x64/bin/node --experimental-wasm-threads --experimental-wasm-bulk-memory a.out.js - diff --git a/src/worker.js b/src/worker.js index 155c52fb8e13..5c08c502f02c 100644 --- a/src/worker.js +++ b/src/worker.js @@ -39,19 +39,6 @@ function assert(condition, text) { } #endif -// When error objects propagate from Web Worker to main thread, they lose helpful call stack and thread ID information, so print out errors early here, -// before that happens. -// Note that addEventListener may not exist on some environments, like node. -if (typeof addEventListener === 'function') { - addEventListener('error', function(e) { - if (e.message.indexOf('SimulateInfiniteLoop') != -1) return e.preventDefault(); - - var errorSource = ' in ' + e.filename + ':' + e.lineno + ':' + e.colno; - console.error('Pthread ' + selfThreadId + ' uncaught exception' + (e.filename || e.lineno || e.colno ? errorSource : "") + ': ' + e.message + '. Error object:'); - console.error(e.error); - }); -} - function threadPrintErr() { var text = Array.prototype.slice.call(arguments).join(' '); console.error(text); @@ -105,7 +92,6 @@ var wasmOffsetData; this.onmessage = function(e) { console.log('child gottt ' + [e, JSON.stringify(e), typeof e.data.urlOrBlob, e.data.urlOrBlob]); try { -console.log('child command:', e.data.cmd); if (e.data.cmd === 'load') { // Preload command that is called once per worker to parse and load the Emscripten code. #if !WASM_BACKEND // Initialize the thread-local field(s): @@ -158,9 +144,7 @@ console.log('child command:', e.data.cmd); }); #else if (typeof e.data.urlOrBlob === 'string') { -console.log('child loading scripts'); importScripts(e.data.urlOrBlob); -console.log('child loaded scripts'); } else { var objectUrl = URL.createObjectURL(e.data.urlOrBlob); importScripts(objectUrl); @@ -283,16 +267,13 @@ console.log('child loaded scripts'); }; // Node.js support -if (typeof require === 'function') { - +if (typeof process === 'object' && typeof process.versions === 'object' && typeof process.versions.node === 'string') { console.log('child wants to live forever ' + typeof setInterval); setInterval(function() { console.log('time in worker...'); throw 55; }, 2000); - - // Create as web-worker-like an environment as we can. self = { location: { @@ -356,5 +337,3 @@ if (typeof require === 'function') { }; } } -console.log('workers first event loop is going away now'); - From fb605f0677dcc03f46a97f9ec8fad286dbf5a14a Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 30 Oct 2019 14:56:47 -0700 Subject: [PATCH 13/25] more cleanup --- src/library_pthread.js | 2 +- src/worker.js | 15 +-------------- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/src/library_pthread.js b/src/library_pthread.js index a46249d9330a..49e769830cff 100644 --- a/src/library_pthread.js +++ b/src/library_pthread.js @@ -434,7 +434,7 @@ var LibraryPThread = { }; if (ENVIRONMENT_HAS_NODE) { - //worker.ref(); // TODO: investigate when we need this + worker.ref(); // TODO: investigate when we need this worker.on('message', function(data) { worker.onmessage({ data: data }); }); diff --git a/src/worker.js b/src/worker.js index 5c08c502f02c..83faad96f2c7 100644 --- a/src/worker.js +++ b/src/worker.js @@ -289,24 +289,11 @@ if (typeof process === 'object' && typeof process.versions === 'object' && typeo var parentPort = nodeWorkerThreads.parentPort; - parentPort.ref(); // stay alive XXX? -//process.exit(); XXX at the right time + parentPort.ref(); // TODO: investigate when we need this parentPort.on('message', function(data) { onmessage({ data: data }); }); - parentPort.on('close', function(data) { - console.log('parentPort closed ..!?'); - }); - parentPort.on('error', function(data) { - console.log('parentPort errored :('); - }); - parentPort.on('exit', function(data) { - console.log('werker exited'); - }); - parentPort.on('online', function(data) { - console.log('parentPortah is online!!!1!'); - }); var nodeFS = require('fs'); From 1e2e871c6f736dbc6236ee1d17749106208222d1 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 30 Oct 2019 14:57:47 -0700 Subject: [PATCH 14/25] revert --- tests/pthread/test_pthread_create.cpp | 141 ++++++++++++++------------ 1 file changed, 75 insertions(+), 66 deletions(-) diff --git a/tests/pthread/test_pthread_create.cpp b/tests/pthread/test_pthread_create.cpp index a123ac28a472..e52f11d0ad6d 100644 --- a/tests/pthread/test_pthread_create.cpp +++ b/tests/pthread/test_pthread_create.cpp @@ -10,51 +10,49 @@ #include #include -#define NUM_THREADS 1 +#define NUM_THREADS 8 int fib(int n) { - if (n <= 0) return 0; - if (n == 1) return 1; - return fib(n-1) + fib(n-2); + if (n <= 0) return 0; + if (n == 1) return 1; + return fib(n-1) + fib(n-2); } unsigned int global_shared_data[NUM_THREADS]; void *ThreadMain(void *arg) { -printf("ThreadMain! %d\n", (int)arg); - - int idx = (int)arg; - unsigned int param = global_shared_data[idx]; + int idx = (int)arg; + unsigned int param = global_shared_data[idx]; #define N 100 - EM_ASM(err('Thread idx '+$0+': sorting ' + $1 + ' numbers with param ' + $2 + '.'), idx, N, param); - - unsigned int n[N]; - for(unsigned int i = 0; i < N; ++i) - n[i] = (i + param) % N; // Create a shifted increasing sequence of numbers [0, N-1[ - - // Sort the sequence to ordered [0, N[ - for(unsigned int i = 0; i < N; ++i) - for(unsigned int j = i; j < N; ++j) - { - if (n[i] > n[j]) - { - unsigned int t = n[i]; - n[i] = n[j]; - n[j] = t; - } - } - // Ensure all elements are in place. - int numGood = 0; - for(unsigned int i = 0; i < N; ++i) - if (n[i] == i) ++numGood; - else EM_ASM(err('n['+$0+']='+$1), i, n[i]); - - EM_ASM(out('Thread idx ' + $0 + ' with param '+$1+': all done with result '+$2+'.'), idx, param, numGood); - pthread_exit((void*)numGood); + EM_ASM(err('Thread idx '+$0+': sorting ' + $1 + ' numbers with param ' + $2 + '.'), idx, N, param); + + unsigned int n[N]; + for(unsigned int i = 0; i < N; ++i) + n[i] = (i + param) % N; // Create a shifted increasing sequence of numbers [0, N-1[ + + // Sort the sequence to ordered [0, N[ + for(unsigned int i = 0; i < N; ++i) + for(unsigned int j = i; j < N; ++j) + { + if (n[i] > n[j]) + { + unsigned int t = n[i]; + n[i] = n[j]; + n[j] = t; + } + } + // Ensure all elements are in place. + int numGood = 0; + for(unsigned int i = 0; i < N; ++i) + if (n[i] == i) ++numGood; + else EM_ASM(err('n['+$0+']='+$1), i, n[i]); + + EM_ASM(out('Thread idx ' + $0 + ' with param '+$1+': all done with result '+$2+'.'), idx, param, numGood); + pthread_exit((void*)numGood); } pthread_t thread[NUM_THREADS]; @@ -63,43 +61,54 @@ int numThreadsToCreate = 1000; void CreateThread(int i) { - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); - static int counter = 1; - global_shared_data[i] = (counter++ * 12141231) & 0x7FFFFFFF; // Arbitrary random'ish data for perturbing the sort for this thread task. -// EM_ASM(out('Main: Creating thread idx ' + $0 + ' (param ' + $1 + ')'), i, global_shared_data[i]); - int rc = pthread_create(&thread[i], &attr, ThreadMain, (void*)i); - assert(rc == 0); - pthread_attr_destroy(&attr); -} - -void mainn() { - printf("main iter\n"); + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + static int counter = 1; + global_shared_data[i] = (counter++ * 12141231) & 0x7FFFFFFF; // Arbitrary random'ish data for perturbing the sort for this thread task. +// EM_ASM(out('Main: Creating thread idx ' + $0 + ' (param ' + $1 + ')'), i, global_shared_data[i]); + int rc = pthread_create(&thread[i], &attr, ThreadMain, (void*)i); + assert(rc == 0); + pthread_attr_destroy(&attr); } int main() { -printf("mainne1\n"); - if (!emscripten_has_threading_support()) - { + if (!emscripten_has_threading_support()) + { +#ifdef REPORT_RESULT + REPORT_RESULT(0); +#endif + printf("Skipped: Threading is not supported.\n"); + return 0; + } + + // Create initial threads. + for(int i = 0; i < NUM_THREADS; ++i) + CreateThread(i); + + // Join all threads and create more. + while (numThreadsToCreate > 0) + { + for(int i = 0; i < NUM_THREADS; ++i) + { + if (thread[i]) + { + int status; + int rc = pthread_join(thread[i], (void**)&status); + assert(rc == 0); + EM_ASM(err('Main: Joined thread idx ' + $0 + ' (param ' + $1 + ') with status ' + $2), i, global_shared_data[i], (int)status); + assert(status == N); + thread[i] = 0; + if (numThreadsToCreate > 0) + { + --numThreadsToCreate; + CreateThread(i); + } + } + } + } #ifdef REPORT_RESULT - REPORT_RESULT(0); + REPORT_RESULT(0); #endif - printf("Skipped: Threading is not supported.\n"); - return 0; - } -printf("mainne2\n"); - - // Create initial threads. - for(int i = 0; i < NUM_THREADS; ++i) { -printf("mainne3\n"); - CreateThread(i); - } -printf("mainne4\n"); - - // synchronous here would not let the worker start - - // the postMessage is only sent at the end of the event - // loop it seems :( - emscripten_set_main_loop(mainn, 1, 1); } From fed61a364e69fd1253654b225909ab338729d5df Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 30 Oct 2019 16:04:45 -0700 Subject: [PATCH 15/25] fix closure --- src/node_shell_read.js | 5 ----- src/shell.js | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/node_shell_read.js b/src/node_shell_read.js index 6045597c43e6..6a1c07872a54 100644 --- a/src/node_shell_read.js +++ b/src/node_shell_read.js @@ -1,8 +1,3 @@ - // Expose functionality in the same simple way that the shells work - // Note that we pollute the global namespace here, otherwise we break in node - var nodeFS; - var nodePath; - read_ = function shell_read(filename, binary) { var ret; #if SUPPORT_BASE64_EMBEDDING diff --git a/src/shell.js b/src/shell.js index 2e5bbdb2609d..9008720d2e2b 100644 --- a/src/shell.js +++ b/src/shell.js @@ -121,6 +121,11 @@ var read_, readBinary, setWindowTitle; +#if ENVIRONMENT_MAY_BE_NODE +var nodeFS; +var nodePath; +#endif + #if ENVIRONMENT_MAY_BE_NODE if (ENVIRONMENT_IS_NODE) { #if ENVIRONMENT From 399e9ca7a0f81d1714a396e3727544dd301b02b5 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 30 Oct 2019 16:48:50 -0700 Subject: [PATCH 16/25] fix --- src/shell.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/shell.js b/src/shell.js index 9008720d2e2b..b4baa9371209 100644 --- a/src/shell.js +++ b/src/shell.js @@ -276,17 +276,19 @@ if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { // Differentiate the Web Worker from the Node Worker case, as reading must // be done differently. - if (!ENVIRONMENT_HAS_NODE) { +#if USE_PTHREADS + if (ENVIRONMENT_HAS_NODE) { -#include "web_or_worker_shell_read.js" +#include "node_shell_read.js" - } else { + } else +#endif + { -#include "node_shell_read.js" +#include "web_or_worker_shell_read.js" } - setWindowTitle = function(title) { document.title = title }; } else #endif // ENVIRONMENT_MAY_BE_WEB || ENVIRONMENT_MAY_BE_WORKER From b5245d0026f4f5a5339f5b0fe7c1c681e156eb29 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 30 Oct 2019 17:11:01 -0700 Subject: [PATCH 17/25] fix --- src/compiler.js | 7 ++++++- src/shell.js | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/compiler.js b/src/compiler.js index 253336ec87b8..99f942bca29a 100644 --- a/src/compiler.js +++ b/src/compiler.js @@ -193,10 +193,15 @@ Runtime.QUANTUM_SIZE = 4; var ENVIRONMENTS = ENVIRONMENT.split(','); ENVIRONMENT_MAY_BE_WEB = !ENVIRONMENT || ENVIRONMENTS.indexOf('web') >= 0; -ENVIRONMENT_MAY_BE_WORKER = !ENVIRONMENT || ENVIRONMENTS.indexOf('worker') >= 0; ENVIRONMENT_MAY_BE_NODE = !ENVIRONMENT || ENVIRONMENTS.indexOf('node') >= 0; ENVIRONMENT_MAY_BE_SHELL = !ENVIRONMENT || ENVIRONMENTS.indexOf('shell') >= 0; +// The worker case also includes Node.js workers when pthreads are +// enabled and Node.js is being built for. Node.js workers are detected as +// a combination of ENVIRONMENT_IS_WORKER and ENVIRONMENT_HAS_NODE. +ENVIRONMENT_MAY_BE_WORKER = !ENVIRONMENT || ENVIRONMENTS.indexOf('worker') >= 0 || + (ENVIRONMENT_MAY_BE_NODE && USE_PTHREADS); + if (ENVIRONMENT && !(ENVIRONMENT_MAY_BE_WEB || ENVIRONMENT_MAY_BE_WORKER || ENVIRONMENT_MAY_BE_NODE || ENVIRONMENT_MAY_BE_SHELL)) { throw 'Invalid environment specified in "ENVIRONMENT": ' + ENVIRONMENT + '. Should be one of: web, worker, node, shell.'; } diff --git a/src/shell.js b/src/shell.js index b4baa9371209..9c41d68b5997 100644 --- a/src/shell.js +++ b/src/shell.js @@ -244,6 +244,10 @@ if (ENVIRONMENT_IS_SHELL) { } } else #endif // ENVIRONMENT_MAY_BE_SHELL + +// Note that this includes Node.js workers when relevant (pthreads is enabled). +// Node.js workers are detected as a combination of ENVIRONMENT_IS_WORKER and +// ENVIRONMENT_HAS_NODE. #if ENVIRONMENT_MAY_BE_WEB || ENVIRONMENT_MAY_BE_WORKER if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { if (ENVIRONMENT_IS_WORKER) { // Check worker, not web, since window could be polyfilled From 43c180a8b0f873dd2212aa718acef79b8d9a266b Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 30 Oct 2019 17:23:27 -0700 Subject: [PATCH 18/25] cleanup --- src/worker.js | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/worker.js b/src/worker.js index 83faad96f2c7..6ecba1b8f159 100644 --- a/src/worker.js +++ b/src/worker.js @@ -90,7 +90,6 @@ var wasmOffsetData; #endif this.onmessage = function(e) { - console.log('child gottt ' + [e, JSON.stringify(e), typeof e.data.urlOrBlob, e.data.urlOrBlob]); try { if (e.data.cmd === 'load') { // Preload command that is called once per worker to parse and load the Emscripten code. #if !WASM_BACKEND @@ -266,18 +265,13 @@ this.onmessage = function(e) { } }; +#if ENVIRONMENT_MAY_BE_NODE // Node.js support if (typeof process === 'object' && typeof process.versions === 'object' && typeof process.versions.node === 'string') { - console.log('child wants to live forever ' + typeof setInterval); - setInterval(function() { - console.log('time in worker...'); - throw 55; - }, 2000); - // Create as web-worker-like an environment as we can. self = { location: { - href: __filename // XXX wat + href: __filename } }; @@ -298,7 +292,6 @@ if (typeof process === 'object' && typeof process.versions === 'object' && typeo var nodeFS = require('fs'); var nodeRead = function(filename) { - // TODO: convert to absolute path? return nodeFS.readFileSync(filename).toString(); }; @@ -324,3 +317,4 @@ if (typeof process === 'object' && typeof process.versions === 'object' && typeo }; } } +#endif // ENVIRONMENT_MAY_BE_NODE From da9d89eb8668d608023c41def1cfd41204e96980 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 31 Oct 2019 15:20:52 -0700 Subject: [PATCH 19/25] feedback --- ChangeLog.md | 1 + src/library_pthread.js | 1 - src/worker.js | 2 -- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 605820c5e28a..304c4b2463ba 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -24,6 +24,7 @@ Current Trunk `ALLOW_BLOCKING_ON_MAIN_THREAD` is unset then the warning is an error. - Add `pthread_tryjoin_np`, which is a POSIX API similar to `pthread_join` but without blocking. + - Add support for pthreads in Node.js, using Node Workers. See #9745 v1.39.1: 10/30/2019 ------------------- diff --git a/src/library_pthread.js b/src/library_pthread.js index 8cc0b23e3cf3..6e18e64d0f2b 100644 --- a/src/library_pthread.js +++ b/src/library_pthread.js @@ -434,7 +434,6 @@ var LibraryPThread = { }; if (ENVIRONMENT_HAS_NODE) { - worker.ref(); // TODO: investigate when we need this worker.on('message', function(data) { worker.onmessage({ data: data }); }); diff --git a/src/worker.js b/src/worker.js index 6ecba1b8f159..48f6f0a55437 100644 --- a/src/worker.js +++ b/src/worker.js @@ -283,8 +283,6 @@ if (typeof process === 'object' && typeof process.versions === 'object' && typeo var parentPort = nodeWorkerThreads.parentPort; - parentPort.ref(); // TODO: investigate when we need this - parentPort.on('message', function(data) { onmessage({ data: data }); }); From ec991f8256d0fb5dbdce7327d92b44dcc49c37bc Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 31 Oct 2019 15:33:08 -0700 Subject: [PATCH 20/25] Nicer warning --- src/shell.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/shell.js b/src/shell.js index 9c41d68b5997..6ae65ede4028 100644 --- a/src/shell.js +++ b/src/shell.js @@ -171,7 +171,13 @@ if (ENVIRONMENT_IS_NODE) { Module['inspect'] = function () { return '[Emscripten Module object]'; }; #if USE_PTHREADS - var nodeWorkerThreads = require('worker_threads'); + var nodeWorkerThreads; + try { + nodeWorkerThreads = require('worker_threads'); + } catch (e) { + console.error('The "worker_threads" module is not supported in this node.js build - perhaps a newer version is needed?'); + throw e; + } Worker = nodeWorkerThreads.Worker; // Polyfill the performance object, which emscripten pthreads support From b6cbb22e9415d2fd4e7c41869664fffe160069d3 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 31 Oct 2019 15:39:23 -0700 Subject: [PATCH 21/25] More event handling --- src/library_pthread.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/library_pthread.js b/src/library_pthread.js index 6e18e64d0f2b..522914e47c29 100644 --- a/src/library_pthread.js +++ b/src/library_pthread.js @@ -437,6 +437,12 @@ var LibraryPThread = { worker.on('message', function(data) { worker.onmessage({ data: data }); }); + worker.on('error', function(data) { + worker.onerror(data.err); + }); + worker.on('exit', function(data) { + console.log('worker exited - TODO: update the worker queue?'); + }); } }(worker)); } // for each worker From 1073fa595b01f11db84727a114f98ac057799da9 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 31 Oct 2019 16:29:31 -0700 Subject: [PATCH 22/25] feedback --- src/shell.js | 2 -- tests/core/pthread/create.cpp | 12 ++++++++++-- tests/test_core.py | 12 +++++++++--- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/shell.js b/src/shell.js index 6ae65ede4028..fe772f9551b6 100644 --- a/src/shell.js +++ b/src/shell.js @@ -124,9 +124,7 @@ var read_, #if ENVIRONMENT_MAY_BE_NODE var nodeFS; var nodePath; -#endif -#if ENVIRONMENT_MAY_BE_NODE if (ENVIRONMENT_IS_NODE) { #if ENVIRONMENT #if ASSERTIONS diff --git a/tests/core/pthread/create.cpp b/tests/core/pthread/create.cpp index 0cfc1a4eb60e..b65de1e560e4 100644 --- a/tests/core/pthread/create.cpp +++ b/tests/core/pthread/create.cpp @@ -46,8 +46,12 @@ void mainn() { main_adds++; printf("main iter %d : %d\n", main_adds, worker_adds); if (worker_adds == NUM_THREADS * TOTAL) { - emscripten_cancel_main_loop(); printf("done!\n"); +#ifndef POOL + emscripten_cancel_main_loop(); +#else + exit(0); +#endif } } @@ -57,6 +61,10 @@ int main() { CreateThread(i); } - // Node.js requires the event loop to be reached for the worker to start up. + // Without a pool, the event loop must be reached for the worker to start up. +#ifndef POOL emscripten_set_main_loop(mainn, 0, 0); +#else + while (1) mainn(); +#endif } diff --git a/tests/test_core.py b/tests/test_core.py index 00ee08f353e7..93ff610888b3 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -8392,10 +8392,16 @@ def test_fpic_static(self): self.do_run_in_out_file_test('tests', 'core', 'test_hello_world') @node_pthreads - def test_pthreads_hello(self, js_engines): - self.do_run_in_out_file_test('tests', 'core', 'pthread', 'create', - js_engines=js_engines) + def test_pthreads_create(self, js_engines): + def test(): + self.do_run_in_out_file_test('tests', 'core', 'pthread', 'create', + js_engines=js_engines) + test() + # with a pool, we can synchronously depend on workers being available + self.set_setting('PTHREAD_POOL_SIZE', '2') + self.emcc_args += ['-DPOOL'] + test() # Generate tests for everything def make_run(name, emcc_args, settings=None, env=None): From 8354dff73d08d1e32ce1c600fd71d6b2d6f7fbf8 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 31 Oct 2019 16:51:43 -0700 Subject: [PATCH 23/25] flake8 --- tests/test_core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_core.py b/tests/test_core.py index 93ff610888b3..53bfd7261da0 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -8403,6 +8403,7 @@ def test(): self.emcc_args += ['-DPOOL'] test() + # Generate tests for everything def make_run(name, emcc_args, settings=None, env=None): if env is None: From fe6edc9b86d165338e3083ac8f145f9e737fc33b Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 1 Nov 2019 13:39:31 -0700 Subject: [PATCH 24/25] text --- src/compiler.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/compiler.js b/src/compiler.js index 99f942bca29a..3babfb7b99e7 100644 --- a/src/compiler.js +++ b/src/compiler.js @@ -197,8 +197,9 @@ ENVIRONMENT_MAY_BE_NODE = !ENVIRONMENT || ENVIRONMENTS.indexOf('node') >= 0; ENVIRONMENT_MAY_BE_SHELL = !ENVIRONMENT || ENVIRONMENTS.indexOf('shell') >= 0; // The worker case also includes Node.js workers when pthreads are -// enabled and Node.js is being built for. Node.js workers are detected as -// a combination of ENVIRONMENT_IS_WORKER and ENVIRONMENT_HAS_NODE. +// enabled and Node.js is one of the supported environments for the build to +// run on. Node.js workers are detected as a combination of +// ENVIRONMENT_IS_WORKER and ENVIRONMENT_HAS_NODE. ENVIRONMENT_MAY_BE_WORKER = !ENVIRONMENT || ENVIRONMENTS.indexOf('worker') >= 0 || (ENVIRONMENT_MAY_BE_NODE && USE_PTHREADS); From 485832442b9556077b89df012f99c699e3a50ba1 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 1 Nov 2019 13:39:49 -0700 Subject: [PATCH 25/25] use perf_hooks.performance, and use it for all threads --- src/shell.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/shell.js b/src/shell.js index fe772f9551b6..75a81a683dcc 100644 --- a/src/shell.js +++ b/src/shell.js @@ -177,16 +177,6 @@ if (ENVIRONMENT_IS_NODE) { throw e; } Worker = nodeWorkerThreads.Worker; - - // Polyfill the performance object, which emscripten pthreads support - // depends on for good timing. - if (typeof performance === 'undefined') { - performance = { - now: function() { - return Date.now(); - } - }; - } #endif } else @@ -306,6 +296,16 @@ if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { #endif // ASSERTIONS } +#if ENVIRONMENT_MAY_BE_NODE && USE_PTHREADS +if (ENVIRONMENT_HAS_NODE) { + // Polyfill the performance object, which emscripten pthreads support + // depends on for good timing. + if (typeof performance === 'undefined') { + performance = require('perf_hooks').performance; + } +} +#endif + // Set up the out() and err() hooks, which are how we can print to stdout or // stderr, respectively. {{{ makeModuleReceiveWithVar('out', 'print', 'console.log.bind(console)', true) }}}