New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Threading support #237
Comments
Dear pyodide devs, I'd like to start experimenting with the threading support -- our target browser is Chrome anyway (we need web-bluetooth support). Before I delve into the pyodide codebase: How hard should I expect enabling the threading to be? |
@pbazant I don't have a good understand of much work it would be, but it would be certainly worth trying to build with threading support following emscripten docs and reporting here if you run into any issues :) Though we likely won't be able to merge it in pyodide until support has been enabled in firefox by default, and it doesn't look like it happened yet. |
Looks like this fails in that Emscripten won't allow us to use dynamic loading and PTHREADS in the same build: emscripten-core/emscripten#3494 |
I actually managed to build Pyodide with threads semi-working. I had to change several compilation options. This caused emscripten to remove many symbols, so I had to force it to keep them by listing them all. Maybe there is a cleaner way. |
That’s neat - would you be able to push up a branch for me to poke at? |
@pbazant I see you mentioned web-bluetooth above. I have a use case where I am just starting to consider that in combination with Pyodide. Have you gotten anywhere? |
It's a company project. I think web-bluetooth is usable only from the main thread, so if you run Pyodide in a worker thread (probably a good idea), you must use some communication mechanism between the threads. |
We postponed making the threads work and use async/await + asyncio in Pyodide as a temporary solution to concurrency. To use asyncio, we inherited from asyncio.BaseEventLoop and did some tweaking (one has to provide a custom selector implementation). |
Good news is that Firefox 79 will apparently support WebAssembly threads. |
Quick update: Firefox 79 has been released and it supports WebAssembly threads and SharedArrayBuffer. There are some minor restriction regarding
The headers in question are the newly introduced
|
By @oeway in #958 (comment)
Yes, we can certainly start investigating. Maybe under a feature flag for a start.
There are certainly early adopters but there are actually more users coming from some kind of teaching related platforms (e.g. https://notebook.basthon.fr or https://buildingai.elementsofai.com/), and there 5 month of Firefox support is very short for end users. Also Safari/WebKit based browsers currently have some issues (#721) but there is hope of fixing it, and as far as I can tell threads won't work there yet (Fyrd/caniuse#5200, https://bugs.webkit.org/show_bug.cgi?id=218944) Ideally we could have deployed it as a separate path e.g. |
Normally I would say we should first upgrade to latest emscripten with llvm backend, but llvm-backend emscripten is pretty broken for us, so I'm not sure
|
It looks like all major browsers support threading by now. However this would mean dropping support for Safari <14.1, so maybe we could start using it in a long lived feature branch #1932 |
Update: Safari 15.2 has enabled |
Still no sign of Atomics in Safari though. Pthreads needs Atomics too. |
Safari does support Atomics now, it was enabled along with SharedArrayBuffer in 15.2 but the release notes didn't mention it (likely because it was behind the same feature flag as SharedArrayBuffer) and caniuse wasn't updated. I've submitted a PR to update the caniuse data: mdn/browser-compat-data#14528 and that PR has the details of the testing I've done. I've not checked whether wasm threads/atomics is supported yet. |
Anyone working on this? Threading works in all browsers, and there's now dynamic linking support so that dynamically linked table things are shared on thread start. Means module load won't work in a thread, but would be a start, and make a lot of other stuff easier (networking, porting common things like requests are the two that come to mind immediately). |
Hi @joemarshall , Not as far as I know. The latest report is in #237 (comment), if you are interested in exploring it, it would be great. Having a branch that builds and checking what tests pass would already move this forward a lot.
Well, it works in the latest versions of desktop browsers. If documentation stats are of any indication, we have around 16% of Safari users who use Safari <15.1 which won't have Atomics. Also, I'm not sure how good is mobile support. Since are aiming for Pyodide to run on arbitrary websites, I feel it might be a bit early to enable it by default. However we could very well start experimenting on a separate branch (and deploy it to the CDN #237 (comment)) then if things look good, make it the default by the end of year of so. |
Also see the related comment by @hoodmane in https://mail.python.org/archives/list/pyodide@python.org/message/DI3PJ5J6P4EPGIB7UR6QMVZ7CLPX7LYZ/ about implications for the type translation code. |
I had a bit of a look at this - firstly it needs the most recent emscripten. Then I can get it to build, but it gives a load of errors about undefined symbols which I can't quite work out why they are happening. The EXPORTED_FUNCTIONS message is a red herring, because the build is linkable, which exports everything by default. See errors below:
|
I had a thought on this - when threading does work in pyodide, it would be useful to have an init time flag to disable or enable threads. That way the default build could have threading built in, but in non thread environments it could fall back to a stub implementation of pthreads |
Speaking of threads and performance after compilation to WASM, how does
`nogil` CPython fork performance compare?
Have Heimes' patches for CPython WASM been merged into nogil branch? Does
it compile w/ pyodide; and is the nogil branch faster?
https://github.com/colesbury/nogil
(edit) Here's a simple test case for threading / GIL support: https://github.com/colesbury/nogil#example
…On Sat, Jul 9, 2022, 6:19 AM Joe Marshall ***@***.***> wrote:
I had a thought on this - when threading does work in pyodide, it would be
useful to have an init time flag to disable or enable threads. That way the
default build could have threading built in, but in non thread environments
it could fall back to a stub implementation of pthreads
—
Reply to this email directly, view it on GitHub
<#237 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAAMNSY55BAQ5CLSZYYAGJTVTFG2LANCNFSM4GALKYQQ>
.
You are receiving this because you are subscribed to this thread.Message
ID: ***@***.***>
|
Looks like browser support is a lot better now (as expected, ~9 months later). Overall support (including mobile devices) for Atomics is 90% which is quite good (consider that I'm guessing the main limiting factor now is just the willingness/time of a contributor to implement this? Or are there still potentially some technical roadblocks? It would be great if someone could make a comment with a few dot points indicating technical blockers, if there are any. Threading support seems like it would be a really big deal! |
From https://web.dev/webassembly-threads/#c :
WebAssembly threads & atomics spec: https://github.com/WebAssembly/threads/blob/master/proposals/threads/Overview.md "Wasm threads are now available in all browsers" (2021) |
From @josephrocca:
|
This seems more or an annoyance than a technical roadblock though, right? But yeah, I agree this is far less than ideal. That said, I think the Chrome team is working on the spec to make the COEP/COOP/etc. situation easier for developers (e.g. this is not directly relevant to our problem here, but is part of their project to simplify things), so hopefully this is less of a problem over time.
BTW, there's a cheeky workaround using Service Workers to add the COEP/COOP headers which makes Github Pages cross-origin isolated. Just include that script in your
It seems like your suspicions are correct:
But this doesn't seem like a big deal - the
I don't think it'd need to be all packages though, right? You'd just have threaded builds for some packages where it makes sense, and then load them if the runtime supports it. If the user tries to load a package that only has a threaded build on a Unless you're saying that all the non-threaded packages that we currently have won't work properly in a threaded runtime? That would be surprising and very annoying if true. |
As I see it big technical roadblock is that threads don't work in site environments where cross site isolation isn't enabled. Which annoyingly includes GitHub pages. The pain with threads is that it's a build time choice in emscripten to enable threading or not, and I'm not sure what happens if you build with threading support and then run without isolation? There's a risk that it would mean all pyodide packages would need two builds, threads or no threads. Everything else is just a matter of time to hack things in. Having said that, I just took a look at emscripten, and pthreads appears to mostly be implemented in JavaScript, which I think might just mean that it is safe to compile packages in pthreads mode even if you have the main pyodide module in two flavours (threads and no threads)? |
(lol in case anyone is confused, my earlier comment was in response to a now-deleted comment from joe that was mostly the same as the one above)
Nice if true! |
Hi, I'm porting errorPythonError: Traceback (most recent call last): File "/lib/python311.zip/_pyodide/_base.py", line 558, in eval_code_async await CodeRunner( File "/lib/python311.zip/_pyodide/_base.py", line 381, in run_async coroutine = eval(self.code, globals, locals) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "", line 10, in File "/lib/python3.11/site-packages/pymongo/mongo_client.py", line 837, in __init__ self._get_topology() File "/lib/python3.11/site-packages/pymongo/mongo_client.py", line 1214, in _get_topology self._topology.open() File "/lib/python3.11/site-packages/pymongo/topology.py", line 192, in open self._ensure_opened() File "/lib/python3.11/site-packages/pymongo/topology.py", line 596, in _ensure_opened self._update_servers() File "/lib/python3.11/site-packages/pymongo/topology.py", line 747, in _update_servers server.open() File "/lib/python3.11/site-packages/pymongo/server.py", line 49, in open self._monitor.open() File "/lib/python3.11/site-packages/pymongo/monitor.py", line 79, in open self._executor.open() File "/lib/python3.11/site-packages/pymongo/periodic_executor.py", line 87, in open thread.start() File "/lib/python311.zip/threading.py", line 957, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread
type: 'RuntimeError', |
It is not supported in Pyodide yet, and AFAIK there's not much progress. I think threading support would require a lot of work both on CPython side and Emscripten side. So, unfortunately, it's unlikely to be supported quickly.
You'll need to find a way to run the package with threading disabled. |
I think Emscripten's dynamic linking will need to become a lot more stable before Pyodide could add support for threading. Dynamic linking does work, however, it gets very flaky if you start using multiple shared modules and mixing in threading/asyncify.
@ryanking13 how much is there to do on the CPython side exactly? The only challenge I can see is packaging the compiled modules for both multi-threaded and single-threaded builds... unless perhaps the threading module uses some low-level pthread features that Emscripten does not support? |
It's actually hard to tell until someone tries it for themselves. I am just saying based on my experience. If you're lucky, CPython's threading implementation will be so compatible with Emscripten that it will work without you doing anything, but based on my experience, this is usually not the case. |
Perhaps I can give it a go. I already have a multi-threaded build of Pyodide but I haven't tried to enable the |
For me, a very simple test with Python threads is working, however, I am using a custom runtime so it would be good if someone could test this with the default JS runtime that Emscripten generates. These are roughly the steps that need to be followed:
diff --git a/packages/sqlite3/meta.yaml b/packages/sqlite3/meta.yaml
index 5976fbc..d5d26fc 100644
--- a/packages/sqlite3/meta.yaml
+++ b/packages/sqlite3/meta.yaml
@@ -24,15 +24,15 @@ build:
"Modules/_sqlite/util.c"
)
- embuilder build sqlite3 --pic
+ embuilder build sqlite3-mt --pic
for file in "${FILES[@]}"; do
emcc $STDLIB_MODULE_CFLAGS -c "${file}" -o "${file/.c/.o}" \
-sUSE_SQLITE3 -DMODULENAME=sqlite
done
OBJECT_FILES=$(find Modules/_sqlite/ -name "*.o")
emcc $OBJECT_FILES -o $DISTDIR/_sqlite3.so $SIDE_MODULE_LDFLAGS \
- -sUSE_SQLITE3 -lsqlite3
+ -sUSE_SQLITE3 -lsqlite3-mt
cd Lib && tar --exclude=test -cf - sqlite3 | tar -C $DISTDIR -xf - With this, I was able to run the following Python code and saw interleaved execution of the two functions: import threading
import sys
import time
def print_numbers():
for i in range(1, 6):
time.sleep(0.1)
print('Number:', i)
sys.stdout.flush()
def print_letters():
for letter in 'abcde':
time.sleep(0.1)
print('Letter:', letter)
sys.stdout.flush()
thread1 = threading.Thread(target=print_numbers)
thread2 = threading.Thread(target=print_letters)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print('Threads are done!')
sys.stdout.flush()
time.sleep(1) Note that I have quite a different configuration at this point with Pyodide statically linked to several libraries and use Asyncify. I have no idea whether the changes I listed above will work with the latest Pyodide. My configuration is roughly based on the following: Emscripten version: 3.1.39 |
@allsey87 I couldn't reproduce. I followed your instructions, but I've got Any chance that there is some missing step? |
Just an issue to track the status on the threading support.
Emscripten has Pthreads support via
SharedArrayBuffer
capability in browsers. The later got disabled due to Spectre etc vulnerabilities, however,SharedArrayBuffer
is available in Firefox since 57 via a config option, but that is disabled by default. The switch to enabled by default can be tracked in https://bugzilla.mozilla.org/show_bug.cgi?id=1477743There also has been some activity in emscripten on this subject lately.
Because of,
I imagine we won't be able to do much until both Firefox and Chrome have it enabled by default in a stable release. Still it might be worth trying to make it work in a WIP PR to see what are the difficulties that could arise. Opening this issue for future reference.
This would also very partially address #144
The text was updated successfully, but these errors were encountered: