The threaded browser build (web/Dockerfile, ADR 64, ADR 66): the page may ask for fewer threads than the browser has cores, in the environment variable OPENSCAD_THREADS, which caps oneTBB for the whole run. And oneTBB's worker threads are ended and joined before main() returns, so none is left waiting in the shared memory the page gives the next instance (ADR 75). --- a/src/main_wrapper.cc +++ b/src/main_wrapper.cc @@ -1,5 +1,13 @@ #include "openscad.h" +#ifdef __EMSCRIPTEN__ +#include +#include +#ifdef ENABLE_TBB +#include +#endif +#endif + #ifndef OPENSCAD_NOGUI #include // Bring in Q_INIT_RESOURCE #endif @@ -18,7 +26,27 @@ Q_INIT_RESOURCE(mac); #endif #endif +#if defined(__EMSCRIPTEN__) && defined(ENABLE_TBB) + // TBB's worker threads, ended and joined before main() returns: the page + // gives this instance's memory to the next one, and a worker still + // waiting in it when its thread is terminated, which the browser does in + // its own time, may wake to the next instance's notifications and write + // into its heap. + oneapi::tbb::task_scheduler_handle scheduler{oneapi::tbb::attach{}}; + // At most OPENSCAD_THREADS threads, where the page asks for fewer. + std::optional threads; + if (const char *limit = std::getenv("OPENSCAD_THREADS")) { + if (const int n = std::atoi(limit); n > 0) { + threads.emplace(oneapi::tbb::global_control::max_allowed_parallelism, static_cast(n)); + } + } + const int status = openscad_main(argc, argv); + threads.reset(); + oneapi::tbb::finalize(scheduler, std::nothrow); + return status; +#else return openscad_main(argc, argv); +#endif } #ifdef _WIN32