Skip to main content

Using Workers (Threads)

QuickJS supports worker threads through the os.Worker API in quickjs-libc, enabling multi-threaded JavaScript execution with message passing between workers.
Worker support requires building with thread support (default when pthreads is available).

Worker Basics

Creating Workers

Workers run JavaScript in separate threads with isolated memory:

Worker Script

worker.js:

Memory Isolation

Each worker has its own:
  • JSRuntime - Separate heap and garbage collector
  • JSContext - Isolated global object
  • Memory limit - Independent from parent

Message Passing

Structured Clone

Messages are serialized/deserialized (structured clone algorithm):

SharedArrayBuffer

For true shared memory, use SharedArrayBuffer:

C API for Workers

Custom Runtime Factory

Override worker runtime creation:
Signature (from quickjs-libc.h):

Custom Context Factory

Signature:

Worker Lifecycle

Creating Workers

Terminating Workers

Workers don’t have a built-in terminate method. Design workers to exit gracefully via message passing.

Thread Safety

Runtime Isolation

Each worker has its own runtime - no shared runtime between threads:

Stack Top Updates

If moving a runtime between threads:
Signature:

Atomic Operations

Use Atomics for SharedArrayBuffer synchronization:

Thread Sanitizer

During development, use ThreadSanitizer to catch race conditions:
TSan will report data races if you accidentally share memory incorrectly.

Worker Pool Pattern

Debugging Workers

Enable Debug Output

Worker Errors

Best Practices

  1. Keep workers simple - One worker per task type
  2. Avoid creating too many workers - Typically 1-2x CPU core count
  3. Use SharedArrayBuffer sparingly - Message passing is safer
  4. Handle worker errors gracefully - Workers can crash independently
  5. Set memory limits - Prevent one worker from consuming all memory
  6. Profile carefully - Workers add overhead; ensure they’re worth it
  7. Test with TSan - Catch threading bugs early

Limitations

  • Workers cannot share JSContext or JSRuntime
  • No DOM or browser-specific APIs
  • Message serialization has overhead
  • Worker creation has startup cost
  • No built-in worker pool - implement your own
Functions used internally by worker implementation (from quickjs.h):

Next Steps