SpiderMonkey Internals: Thread Safety

Deprecated
This feature has been removed from the Web standards. Though some browsers may still support it, it is in the process of being dropped. Do not use it in old or new projects. Pages or Web apps using it may break at any time.

Note: Starting in Gecko 12.0, JSRuntime is single-threaded. You must only use it from one thread.

This page describes implementation details of the SpiderMonkey JavaScript engine. It is mainly of interest to people working on SpiderMonkey itself. See JS_THREADSAFE for a gentler introduction to using SpiderMonkey in a multi-threaded application.

General background

SpiderMonkey has a top-level struct, JSRuntime, that acts as a container for everything else. A program typically has only one JSRuntime, even if it has many threads.

Each runtime has one or more JSCompartments. The JSCompartment is the universe in which JS objects live. They can't travel to other JSCompartments. Crucially, any given compartment may only be accessed by one thread at a time. This means that objects cannot be shared across compartments.

All JS code and most JSAPI calls run within a JSContext. The JSContext can be thought of as a machine that knows how to run JavaScript code, or as an abstraction of the notion of a thread. Exception handling, for example, is per-JSContext. Each JSContext must be used by only one thread at a time.

A single JSContext can run work with code and objects in multiple compartments as long as no other thread is accessing those compartments. Objects may be shared among JSContexts within a JSCompartment. There's no fixed association between an object and a context.

Thread-safety in SpiderMonkey is turned on by compiling with -DJS_THREADSAFE. In a JS_THREADSAFE build, these operations are handled specially:

  • access to JSRuntime data structures
  • garbage collection

Accesses to JSRuntime data structures are serialized with a few mutexes. The treatment of GC requires more explanation.

Making GC thread-safe

With JS_THREADSAFE, the API changes slightly. The program must group JSAPI calls into "requests":

   JS_SetContextThread(cx);
   JS_BeginRequest(cx);
   /* ... do stuff ... */
   JS_EndRequest(cx);
   JS_ClearContextThread(cx);

It isn't a bottleneck; multiple threads are allowed to be in requests on the same JSRuntime at once. See JS_BeginRequest.

The most obvious effect of a request is: at any given moment there can either be multiple threads in active requests, or one thread doing GC and all requests suspended. A call to JS_GC() will block until the latter becomes possible. In other words, GC waits until each other thread is either outside JSAPI (in which case we don't care what it's doing) or else in JSAPI, but blocked, waiting for GC to finish.

Threads must not do anything that would affect GC while outside a request. And obviously you shouldn't block or otherwise dilly-dally while in a request; it prohibits GC.

As an optimization, each thread has its own size-classified freelists containing chunks of GC-managed memory ready to be allocated. This allows allocation to avoid locking most of the time (a significant speed win). A thread needs to lock on allocation only when the relevant per-thread freelist is empty. When this happens, the thread also refills that freelist from the JSRuntime-wide GC allocator while it's in the lock.

Document Tags and Contributors

 Last updated by: fscholz,