SpiderMonkey Internals: Thread Safety
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.
JS_THREADSAFE for a gentler introduction to using SpiderMonkey in a multi-threaded application.
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
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 must be used by only one thread at a time.
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
- garbage collection
JSRuntime data structures are serialized with a few mutexes. The treatment of GC requires more explanation.
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.