Morten Johan Sørvig

Qt for WebAssembly: Multithreading

Published Wednesday June 26th, 2019
10 Comments on Qt for WebAssembly: Multithreading
Posted in Qt

As of the 5.13 release, Qt for WebAssembly now has experimental support for multithreading. This has much of the same benefits as threads on other platforms: the application can offload work to secondary threads to keep the main thread responsive, and make use of multiple CPU cores.

Secondary threads on the web platform has one additional benefit in that they can block without unwinding the stack. This is not true for main thread, which must return control to the browser after processing an event or risk having the browser show the “page is unresponsive” notification.

WebAssembly multithreading has been possible but disabled by default for some time. Browsers have now begun enabling it again, first out is Chrome 67.

Old New Thing

These days it’s not too often that we get to introduce threads as a feature, so I took the opportunity to touch up Qt’s classic Mandelbrot threading example.

mandelbrot

Upgrades include now using all CPU cores instead of just one (using QThread::idealThreadCount() to control the amount of worker threads). A quick peek at the task manager confirms this:

mandelbrot-cpuusage

Rest assured, CPU usage does return to 0% when the application is idle.

An example build of the demo and source code are available. The build should run on desktop Chrome, and also on Firefox if you enable javascript.options.shared_memory in about:config page.

Practicalities

Next, lets look at some of the practicalities of working with multithreaded builds of Qt with Emscripten. If you have experience in this area you’d like to share, please chime in in the comments section below.

Emscripten is doing most of the heavy lifting here, and provides support for the pthreads API, implemented using Web Workers and SharedArrayBuffer. Qt then reuses its existing unix QThread implementation.

Enabling The default Qt for WebAssembly build disables threads by default. To enable, build from source and configure with the -feature-thread flag. We’ve found that emscripten 1.38.30 works well for threaded builds.

Threading-enabled binaries will not run on browsers with SharedArrayBuffer disabled; you may see error messages such as:

CompileError: wasm validation error: at offset 5711: shared memory is disabled

Error: WebAssembly.Module doesn’t parse at byte 5705: can’t parse resizable limits flags”

Main thread deadlocks Calling QThread::wait (or pthread_join) on the main thread may deadlock, since the browser will be blocked from servicing application requests such as starting a new Web Worker for the thread we are waiting on. Possible workarounds include:

  • Pre-allocate Web Workers using QMAKE_WASM_PTHREAD_POOL_SIZE (maps to emscripten PTHREAD_POOL_SIZE)
  • Don’t join worker threads when not needed (e.g. app exit). Earlier versions of the Mandelbrot demo was freezing the tab on exit; solved by disabling application cleanup for the Qt build which powers it.

Fixed Memory Size: Emscripten will normally increase the heap size as needed, starting from small initial allocation. This is not yet supported for multithreaded builds and a fixed memory size must be set. We’ve empirically determined that most (desktop) browsers limit this initial allocation to 1GB, and this is the size Qt sets by default. You can change the value by setting QMAKE_WASM_PTHREAD_POOL_SIZE (maps to emscripten PTHREAD_POOL_SIZE)

See the documentation or Wiki page for further info on Qt for WebAssembly, or ask below.

Do you like this? Share it
Share on LinkedInGoogle+Share on FacebookTweet about this on Twitter

Posted in Qt

10 comments

Lorn Potter Lorn Potter says:

To speed up wasm compilation on Chrome, you can enable #enable-webassembly-baseline from chrome://flags
For more information, see this blog post https://v8.dev/blog/liftoff

Talkless says:

I get random errors, different after refresh:
CompileError: wasm validation error: at offset 71600: unrecognized opcode: fe 17
or
CompileError: wasm validation error: at offset 81901: unrecognized opcode: fe 25

Firefox 60.7.1esr on Debian 9 Stretch amd64.

Morten Johan Sørvig Morten Johan Sørvig says:

Have you enabled javascript.options.shared_memory? This could be a variant of the “Wasm multithreading is not supported” error message.

Talkless says:

Yes, just checked again, shared memory is enabled. Tanks! demo works as before, other wasm demos too.

A says:

still no working manual about how to setup this webasm qt. cool

Morten Johan Sørvig Morten Johan Sørvig says:

I think https://doc.qt.io/qt-5/wasm.html has the instructions you are looking for. If you get stuck we could clarify them or add a note on the wiki (https://wiki.qt.io/Qt_for_WebAssembly).

(I would start with Emscripten first: compile a “hello world” example, and get that working in the browser. Then try Qt.)

DJ says:

Is there any news regarding build time under android ? I tried with the Hello Window example from here https://www.qt.io/qt-examples-for-webassembly and it still take considerable time to build.

Morten Johan Sørvig Morten Johan Sørvig says:

The Chrome team are the ones to ask for this one, but it looks like we are waiting for ARM support in the Liftoff compiler. See https://v8.dev/blog/liftoff .

Edit: Also see Lorn’s answer above.

DJ says:

I see thank you for your answer !

Vadim P says:

Excellent to see progress on this – thanks!

Commenting closed.

Get started today with Qt Download now