From 3182290898f7ba9931e9ff46d0b43422dd55697d Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 3 Feb 2020 16:01:26 -0800 Subject: [PATCH 1/4] NEW [ci skip] --- src/library_pthread.js | 7 +- src/preamble.js | 4 +- src/settings.js | 8 +- .../test_pthread_hardware_concurrency.cpp | 87 +++++++++++++++++++ tests/test_browser.py | 5 ++ 5 files changed, 105 insertions(+), 6 deletions(-) create mode 100644 tests/pthread/test_pthread_hardware_concurrency.cpp diff --git a/src/library_pthread.js b/src/library_pthread.js index b920e442da2ad..161b1bd32dedb 100644 --- a/src/library_pthread.js +++ b/src/library_pthread.js @@ -31,9 +31,10 @@ var LibraryPThread = { initMainThreadBlock: function() { if (ENVIRONMENT_IS_PTHREAD) return; -#if PTHREAD_POOL_SIZE > 0 +#if PTHREAD_POOL_SIZE + var pthreadPoolSize = {{{ PTHREAD_POOL_SIZE }}}; // Start loading up the Worker pool, if requested. - for(var i = 0; i < {{{PTHREAD_POOL_SIZE}}}; ++i) { + for(var i = 0; i < pthreadPoolSize; ++i) { PThread.allocateUnusedWorker(); } #endif @@ -41,7 +42,7 @@ var LibraryPThread = { // In asm.js we do not need to wait for Wasm Module to compile on the main thread, so can load // up each Worker immediately. (in asm.js mode ignore PTHREAD_POOL_DELAY_LOAD altogether for // simplicity, as multithreading performance optimizations are not interesting there) -#if !WASM && PTHREAD_POOL_SIZE > 0 +#if !WASM && PTHREAD_POOL_SIZE addOnPreRun(function() { addRunDependency('pthreads'); }); var numWorkersToLoad = PThread.unusedWorkers.length; PThread.unusedWorkers.forEach(function(w) { PThread.loadWasmModuleToWorker(w, function() { diff --git a/src/preamble.js b/src/preamble.js index c5d3cee591ad5..3332b833c1eb0 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -924,7 +924,7 @@ function createWasm() { wasmModule = module; // Instantiation is synchronous in pthreads and we assert on run dependencies. if (!ENVIRONMENT_IS_PTHREAD) { -#if PTHREAD_POOL_SIZE > 0 +#if PTHREAD_POOL_SIZE var numWorkersToLoad = PThread.unusedWorkers.length; PThread.unusedWorkers.forEach(function(w) { PThread.loadWasmModuleToWorker(w, function() { #if !PTHREAD_POOL_DELAY_LOAD @@ -934,7 +934,7 @@ function createWasm() { #endif })}); #endif -#if PTHREAD_POOL_DELAY_LOAD || PTHREAD_POOL_SIZE == 0 +#if PTHREAD_POOL_DELAY_LOAD || !PTHREAD_POOL_SIZE // PTHREAD_POOL_DELAY_LOAD==1 (or no preloaded pool in use): do not wait up for the Workers to // instantiate the Wasm module, but proceed with main() immediately. removeRunDependency('wasm-instantiate'); diff --git a/src/settings.js b/src/settings.js index b684424493ffe..0cfe4647efb88 100644 --- a/src/settings.js +++ b/src/settings.js @@ -1391,8 +1391,14 @@ var USE_PTHREADS = 0; // in which case the specified number of Workers will be preloaded into a pool // before the application starts, and that many threads can then be available // for synchronous creation. +// Note that this setting is a string, and will be emitted in the JS code, so +// if you set it to '5' then 5 workers will be used in the pool, etc. The +// benefit of this being a string is that you can set it to something like +// 'nagivator.hardwareConcurrency' (which will use the number of cores the +// browser reports, and is how you can get exactly enough workers for a +// threadpool equal to the number of cores). // [link] - affects generated JS runtime code at link time -var PTHREAD_POOL_SIZE = 0; +var PTHREAD_POOL_SIZE = ''; // If your application does not need the ability to synchronously create // threads, but it would still like to opportunistically speed up initial thread diff --git a/tests/pthread/test_pthread_hardware_concurrency.cpp b/tests/pthread/test_pthread_hardware_concurrency.cpp new file mode 100644 index 0000000000000..4c4abb79966c7 --- /dev/null +++ b/tests/pthread/test_pthread_hardware_concurrency.cpp @@ -0,0 +1,87 @@ +// Copyright 2015 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 +#include + +#include + +struct Test +{ + int threadId; +}; + +void *ThreadMain(void *arg) +{ + EM_ASM(out('Thread ' + $0 + ' finished, exit()ing.'), ((Test*)arg)->threadId); + pthread_exit(0); +} + +void RunTest(int test) +{ + int NUM_THREADS = std::thread::hardware_concurrency(); + assert(NUM_THREADS > 0); + + EM_ASM(out('Main: Test ' + $0 + ' starting, with num cores: ' + $1), test, NUM_THREADS); + + struct Test t[NUM_THREADS]; + pthread_t thread[NUM_THREADS]; + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + pthread_attr_setstacksize(&attr, 4*1024); + + printf("Main thread has thread ID %d\n", (int)pthread_self()); + assert(pthread_self() != 0); + + EM_ASM(out('Main: Starting test ' + $0), test); + + for(int i = 0; i < NUM_THREADS; ++i) + { + t[i].threadId = i; + int rc = pthread_create(&thread[i], &attr, ThreadMain, &t[i]); + assert(rc == 0); + } + + pthread_attr_destroy(&attr); + + for(int i = 0; i < NUM_THREADS; ++i) + { + int status = 1; + int rc = pthread_join(thread[i], (void**)&status); + assert(rc == 0); + assert(status == 0); + } + + EM_ASM(out('Main: Test ' + $0 + ' finished.'), test); +} + +int main() +{ + if (!emscripten_has_threading_support()) + { +#ifdef REPORT_RESULT + REPORT_RESULT(0); +#endif + printf("Skipped: Threading is not supported.\n"); + return 0; + } + + // Do a bunch of joins, verifying the Worker pool works. + for(int i = 0; i < 7; ++i) + RunTest(i); + +#ifdef REPORT_RESULT + REPORT_RESULT(0); +#else + EM_ASM(out('Main: Test successfully finished.')); +#endif +} diff --git a/tests/test_browser.py b/tests/test_browser.py index 737ab2aa12b78..c8d42dc61026e 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -3628,6 +3628,11 @@ def test_pthread_64bit_cxx11_atomics(self): for pthreads in [[], ['-s', 'USE_PTHREADS=1']]: self.btest(path_from_root('tests', 'pthread', 'test_pthread_64bit_cxx11_atomics.cpp'), expected='0', args=opt + pthreads + ['-std=c++11']) + # Test c++ std::thread::hardware_concurrency() + @requires_threads + def test_pthread_hardware_concurrency(self): + self.btest(path_from_root('tests', 'pthread', 'test_pthread_hardware_concurrency.cpp'), expected='0', args=['-O2', '-s', 'USE_PTHREADS=1', '-s', 'PTHREAD_POOL_SIZE="navigator.hardwareConcurrency"']) + @parameterized({ 'join': ('join',), 'wait': ('wait',), From 39ed1a99262aecb2c2e54f7e7cc9024821a83e80 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 3 Feb 2020 16:04:23 -0800 Subject: [PATCH 2/4] nicer --- src/settings.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/settings.js b/src/settings.js index 0cfe4647efb88..5d1badfa87e14 100644 --- a/src/settings.js +++ b/src/settings.js @@ -1391,9 +1391,10 @@ var USE_PTHREADS = 0; // in which case the specified number of Workers will be preloaded into a pool // before the application starts, and that many threads can then be available // for synchronous creation. -// Note that this setting is a string, and will be emitted in the JS code, so -// if you set it to '5' then 5 workers will be used in the pool, etc. The -// benefit of this being a string is that you can set it to something like +// Note that this setting is a string, and will be emitted in the JS code +// (directly, with no extra quotes) so that if you set it to '5' then 5 workers +// will be used in the pool, and so forth. The benefit of this being a string +// is that you can set it to something like // 'nagivator.hardwareConcurrency' (which will use the number of cores the // browser reports, and is how you can get exactly enough workers for a // threadpool equal to the number of cores). From 8494cc3495612ab775810c7b999332b5d041ce3f Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 3 Feb 2020 17:27:24 -0800 Subject: [PATCH 3/4] fix typo [ci skip] --- src/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings.js b/src/settings.js index 5d1badfa87e14..d09f24b31f9fd 100644 --- a/src/settings.js +++ b/src/settings.js @@ -1395,7 +1395,7 @@ var USE_PTHREADS = 0; // (directly, with no extra quotes) so that if you set it to '5' then 5 workers // will be used in the pool, and so forth. The benefit of this being a string // is that you can set it to something like -// 'nagivator.hardwareConcurrency' (which will use the number of cores the +// 'navigator.hardwareConcurrency' (which will use the number of cores the // browser reports, and is how you can get exactly enough workers for a // threadpool equal to the number of cores). // [link] - affects generated JS runtime code at link time From 6012fc57d61f6864bd5dd533337e2588d8e5085e Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Tue, 4 Feb 2020 06:04:00 -0800 Subject: [PATCH 4/4] typo --- tests/pthread/test_pthread_hardware_concurrency.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/pthread/test_pthread_hardware_concurrency.cpp b/tests/pthread/test_pthread_hardware_concurrency.cpp index 4c4abb79966c7..96eb647440486 100644 --- a/tests/pthread/test_pthread_hardware_concurrency.cpp +++ b/tests/pthread/test_pthread_hardware_concurrency.cpp @@ -1,4 +1,4 @@ -// Copyright 2015 The Emscripten Authors. All rights reserved. +// 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.