1.. Pthreads support:
2
3==============================
4Pthreads support
5==============================
6
7Emscripten has support for multithreading using SharedArrayBuffer in browsers. That API allows sharing memory between the main thread and web workers as well as atomic operations for synchronization, which enables Emscripten to implement support for the Pthreads (POSIX threads) API. This support is considered stable in Emscripten.
8
9.. note:: As of Sep 2019, some browsers have disabled SharedArrayBuffer due to
10          the Spectre set of vulnerabilities. Until it is restored you can still
11          experiment with it if you flip a pref in those browsers. In other
12          browsers (like Chrome on desktop), SharedArrayBuffer is fully enabled
13          by default and you don't need to flip any flags.
14
15Compiling with pthreads enabled
16===============================
17
18By default, support for pthreads is not enabled. To enable code generation for pthreads, the following command line flags exist:
19
20- Pass the compiler flag ``-s USE_PTHREADS=1`` when compiling any .c/.cpp files, AND when linking to generate the final output .js file.
21- Optionally, pass the linker flag ``-s PTHREAD_POOL_SIZE=<integer>`` to specify a predefined pool of web workers to populate at page preRun time before application main() is called. This is important because if the workers do not already exist then we may need to wait for the next browser event iteration for certain things, see below.
22
23There should be no other changes required. In C/C++ code, the preprocessor check ``#ifdef __EMSCRIPTEN_PTHREADS__`` can be used to detect whether Emscripten is currently targeting pthreads.
24
25.. note:: It is not possible to build one binary that would be able to leverage
26    multithreading when available and fall back to single threaded when not. The
27    best you can do is two separate builds, one with and one
28    without threads, and pick between them at runtime.
29
30Additional flags
31================
32
33- ``-s PROXY_TO_PTHREAD``: In this mode your original ``main()`` is replaced by
34  a new one that creates a pthread and runs the original ``main()`` on it. As a
35  result, your application's ``main()`` is run off the browser main (UI) thread,
36  which is good for responsiveness. The browser main thread does still run code
37  when things are proxied to it, for example to handle events, rendering, etc.
38
39Note that Emscripten has the
40``--proxy-to-worker`` :ref:`linker flag <proxy-to-worker>` which sounds similar
41but is unrelated. That flag does not use pthreads or SharedArrayBuffer, and
42instead uses a plain Web Worker to run your main program (and postMessage to
43proxy messages back and forth).
44
45Proxying
46========
47
48The Web allows certain operations to only happen from the main browser thread,
49like interacting with the DOM. As a result, various operations are proxied to
50the main browser thread if they are called on a background thread. See
51`bug 3495 <https://github.com/emscripten-core/emscripten/issues/3495>`_ for
52more information and how to try to work around this until then. To check which
53operations are proxied, you can look for the function's implementation in
54the JS library (``src/library_*``) and see if it is annotated with
55``__proxy: 'sync'`` or ``__proxy: 'async'``; however, note that the browser
56itself proxies certain things (like some GL operations), so there is no
57general way to be safe here (aside from not blocking on the main browser
58thread).
59
60In addition, Emscripten currently has a simple model of file I/O only happening
61on the main application thread (as we support JS plugin filesystems, which
62cannot share memory); this is another set of operations that are proxied.
63
64Proxying can cause problems in certain cases, see the section on blocking below.
65
66Blocking on the main browser thread
67===================================
68
69Note that in most cases the "main browser thread" is the same as the "main
70application thread". The main browser thread is where web pages start to run
71JavaScript, and where JavaScript can access the DOM (a page can also create a Web
72Worker, which would no longer be on the main thread). The main application
73thread is the one on which you started up the application (by loading the main
74JS file emitted by Emscripten). If you started it on the main browser thread -
75by it being a normal HTML page - then the two are identical. However, you can
76also start a multithreaded application in a worker; in that case the main
77application thread is that worker, and there is no access to the main browser
78thread.
79
80The Web API for atomics does not allow blocking on the main thread
81(specifically, ``Atomics.wait`` doesn't work there). Such blocking is
82necessary in APIs like ``pthread_join`` and anything that uses a futex wait
83under the hood, like ``usleep()``, ``emscripten_futex_wait()``, or
84``pthread_mutex_lock()``. To make them work, we use a busy-wait on the main
85browser thread, which can make the browser tab unresponsive, and also wastes
86power. (On a pthread, this isn't a problem as it runs in a Web Worker, where
87we don't need to busy-wait.)
88
89Busy-waiting on the main browser thread in general will work despite the
90downsides just mentioned, for things like waiting on a lightly-contended mutex.
91However, things like ``pthread_join`` and ``pthread_cond_wait``
92are often intended to block for long periods of time, and if that
93happens on the main browser thread, and while other threads expect it to
94respond, it can cause a surprising deadlock. That can happen because of
95proxying, see the previous section. If the main thread blocks while a worker
96attempts to proxy to it, a deadlock can occur.
97
98The bottom line is that on the Web it is bad for the main browser thread to
99wait on anything else. Therefore by default Emscripten warns if
100``pthread_join`` and ``pthread_cond_wait`` happen on the main browser thread,
101and will throw an error if ``ALLOW_BLOCKING_ON_MAIN_THREAD`` is zero
102(whose message will point to here).
103
104To avoid these problems, you can use ``PROXY_TO_PTHREAD``, which as
105mentioned earlier moves your ``main()`` function to a pthread, which leaves
106the main browser thread to focus only on receiving proxied events. This is
107recommended in general, but may take some porting work, if the application
108assumed ``main()`` was on the main browser thread.
109
110Another option is to replace blocking calls with nonblocking ones. For example
111you can replace ``pthread_join`` with ``pthread_tryjoin_np``. This may require
112your application to be refactored to use asynchronous events, perhaps through
113:c:func:`emscripten_set_main_loop` or :ref:`Asyncify`.
114
115Special considerations
116======================
117
118The Emscripten implementation for the pthreads API should follow the POSIX standard closely, but some behavioral differences do exist:
119
120- When the linker flag ``-s PTHREAD_POOL_SIZE=<integer>`` is not specified and ``pthread_create()`` is called, the new thread will not start until control is yielded back to the browser's main event loop, because the web worker cannot be created while JS or wasm code is running. This is a violation of POSIX behavior and will break common code which creates a thread and immediately joins it or otherwise synchronously waits to observe an effect such as a memory write. Using a pool creates the web workers before main is called, allowing thread creation to be synchronous.
121
122- The Emscripten implementation does not support `POSIX signals <http://man7.org/linux/man-pages/man7/signal.7.html>`_, which are sometimes used in conjunction with pthreads. This is because it is not possible to send signals to web workers and pre-empt their execution. The only exception to this is pthread_kill() which can be used as normal to forcibly terminate a running thread.
123
124- The Emscripten implementation does also not support multiprocessing via ``fork()`` and ``join()``.
125
126- For web security purposes, there exists a fixed limit (by default 20) of threads that can be spawned when running in Firefox Nightly. `#1052398 <https://bugzilla.mozilla.org/show_bug.cgi?id=1052398>`_. To adjust the limit, navigate to about:config and change the value of the pref "dom.workers.maxPerDomain".
127
128- Some of the features in the pthreads specification are unsupported since the upstream musl library that Emscripten utilizes does not support them, or they are marked optional and a conformant implementation need not support them. Such unsupported features in Emscripten include prioritization of threads, and pthread_rwlock_unlock() is not performed in thread priority order. The functions pthread_mutexattr_set/getprotocol(), pthread_mutexattr_set/getprioceiling() and pthread_attr_set/getscope() are no-ops.
129
130- One particular note to pay attention to when porting is that sometimes in existing codebases the callback function pointers to pthread_create() and pthread_cleanup_push() omit the void* argument, which strictly speaking is undefined behavior in C/C++, but works in several x86 calling conventions. Doing this in Emscripten will issue a compiler warning, and can abort at runtime when attempting to call a function pointer with incorrect signature, so in the presence of such errors, it is good to check the signatures of the thread callback functions.
131
132- Note that the function emscripten_num_logical_cores() will always return the value of navigator.hardwareConcurrency, i.e. the number of logical cores on the system, even when shared memory is not supported. This means that it is possible for emscripten_num_logical_cores() to return a value greater than 1, while at the same time emscripten_has_threading_support() can return false. The return value of emscripten_has_threading_support() denotes whether the browser has shared memory support available.
133
134- Pthreads + memory growth (``ALLOW_MEMORY_GROWTH``) is especially tricky, see `wasm design issue #1271 <https://github.com/WebAssembly/design/issues/1271>`_. This currently causes JS accessing the wasm memory to be slow - but this will likely only be noticeable if the JS does large amounts of memory reads and writes (wasm runs at full speed, so moving work over can fix this). This also requires that your JS be aware that the HEAP* views may need to be updated - JS code embedded with ``--js-library`` etc will automatically be transformed to use the ``GROWABLE_HEAP_*`` helper functions where ``HEAP*`` are used, but external code that uses ``Module.HEAP*`` directly may encounter problems with views being smaller than memory.
135
136Also note that when compiling code that uses pthreads, an additional JavaScript file ``NAME.worker.js`` is generated alongside the output .js file (where ``NAME`` is the basename of the main file being emitted). That file must be deployed with the rest of the generated code files. By default, ``NAME.worker.js`` will be loaded relative to the main HTML page URL. If it is desirable to load the file from a different location e.g. in a CDN environment, then one can define the ``Module.locateFile(filename)`` function in the main HTML ``Module`` object to return the URL of the target location of the ``NAME.worker.js`` entry point. If this function is not defined in ``Module``, then the default location relative to the main HTML file is used.
137
138Running code and tests
139======================
140
141Any code that is compiled with pthreads support enabled will currently only work in the Firefox Nightly channel, since the SharedArrayBuffer specification is still in an experimental research stage before standardization. There exists two test suites that can be used to verify the behavior of the pthreads API implementation in Emscripten:
142
143- The Emscripten unit test suite contains several pthreads-specific tests in the "browser." suite. Run any of the tests named browser.test_pthread_*.
144
145- An Emscripten-specialized version of the `Open POSIX Test Suite <http://posixtest.sourceforge.net/>`_ is available at `juj/posixtestsuite <https://github.com/juj/posixtestsuite>`_ GitHub repository. This suite contains about 300 tests for pthreads conformance. To run this suite, the pref dom.workers.maxPerDomain should first be increased to at least 50.
146
147Please check these first in case of any issues. Bugs can be reported to the Emscripten bug tracker as usual.
148