SpiderMonkey Internals: Thread Safety

Deprecated
This feature is no longer recommended. Though some browsers might still support it, it may have already been removed from the relevant web standards, may be in the process of being dropped, or may only be kept for compatibility purposes. Avoid using it, and update existing code if possible; see the compatibility table at the bottom of this page to guide your decision. Be aware that this feature may cease to work 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.