Visit Mozilla.org

JS THREADSAFE

From MDC

JS_THREADSAFE is a compile-time option that enables support for running multiple threads of JavaScript code concurrently and sharing objects between them.

Contents

[edit] When to use JS_THREADSAFE

Use JS_THREADSAFE for any application that allows multiple threads to execute JavaScript concurrently.

When SpiderMonkey is built without JS_THREADSAFE, it does no locking at all. Even if you use a separate JSRuntime per thread, at present you need JS_THREADSAFE because some global data structures are shared among all runtimes. (See JS_ShutDown and the global variables in jsdtoa.c.)

[edit] Building

To build SpiderMonkey with JS_THREADSAFE, you need the NSPR library, which is not included in the SpiderMonkey source code. One way to get it is to build Firefox. Then you can build SpiderMonkey using the following commands:

$ cd mozilla/js/src
$ make -f Makefile.ref JS_THREADSAFE=1 JS_DIST=../../objdir/dist

where objdir is the object directory where you have built Firefox. Link your application against both SpiderMonkey and NSPR.

[edit] Requests

In a JS_THREADSAFE build, the application must separate code that uses the JSAPI from code that performs blocking I/O or time-consuming calculations.

A request is a region of code that uses the JSAPI. Requests must be bracketed with calls to JS_BeginRequest() and JS_EndRequest().

JS_BeginRequest(cx);
/* ... do JSAPI stuff ... */
JS_EndRequest(cx);

A request is always associated with a specific JSContext and runs from start to finish on a single thread.

Most JSAPI functions require the caller to be in a request. In this reference, these JSAPI functions are marked with the words "Requires request", like this:

Name Type Description
cx JSContext * The context to use.

Requires request. (In a JS_THREADSAFE build, the caller must be in a request on this JSContext.)

Most JSAPI callback functions are always called from within a request. These callbacks are (unreliably!) documented with the words "Provides request", like this:

Name Type Description
cx JSContext * The context in which the event ocurred.

Provides request. (In JS_THREADSAFE builds, the JS engine calls this callback only from within an active request on cx. The callback does not need to call JS_BeginRequest.)

In particular, JSNative and JSFastNative callbacks provide a request. This means that any potentially long-running operation in a native must be bracketed with calls to JS_SuspendRequest() and JS_ResumeRequest().

JSBool socket_recv(JSContext *cx, uintN argc, jsval *vp)
{
    ...
    rc = JS_SuspendRequest(cx);
    read_size = recv(socket, buf, size, flags);
    JS_ResumeRequest(cx, rc);
    ...
}

[edit] Garbage collection

Requests help make garbage collection safe when multiple threads are using the JSAPI. For each thread that is in a request:

  • Almost any call into the JSAPI may trigger garbage collection; but
  • Garbage collection does not happen at any other time (such as, for example, at the moment before the return value of JS_NewObject is assigned to a rooted variable).

These are actually the same rules that apply to single-threaded JSAPI programs. But in multithreaded programs, if you break the rules, your program is more likely to crash. This is because in single-threaded programs, a random call into the JSAPI is actually pretty unlikely to trigger GC, especially if the calling thread has not been using up a lot of memory. In a multithreaded program, even if the calling thread has been idle, other threads may be active or may call JS_GC().

The above rules mean that at any given moment, there can be either (a) multiple threads in active requests, or (b) one thread doing GC and all requests suspended. During GC, a call to JS_BeginRequest or JS_ResumeRequest will block until the former becomes possible. Conversely, a call to JS_GC() will block until the latter becomes possible. This explains why code in a request must not perform blocking operations or time-consuming computations: doing so could delay garbage collection for a long time or even lead to deadlock. For plenty of implementation details, see SpiderMonkey Internals: Thread Safety.

[edit] Sharing objects among threads

Even in multithreaded applications, most objects are used only in the thread where they are created. Typically only a few objects are actually shared among multiple threads. So SpiderMonkey is optimized for this case. Property accesses are fast and lock-free for objects that are created and used on a single thread.

Therefore SpiderMonkey runs fastest when each thread uses a different global object. Otherwise there may be lock contention as many threads try to access the global object concurrently. (The primary drawback to using different global objects in different threads is that each global object will have its own copy of the standard classes. This has some surprising consequences. In particular, x instanceof Array is only true if x is an instance of the particular Array class in the current scope. It is false if x is an array created in a different global scope, using a different copy of Array! See bug 418264.)

The first time an object created in one thread is used in another, the second thread must wait for the first thread to leave its current request. To keep this wait time to a minimum, applications must avoid long-running requests. The recommended technique in SpiderMonkey 1.8 and later is to periodically call JS_YieldRequest from an operation callback. (In earlier versions, instead call JS_SuspendRequest and JS_ResumeRequest periodically from a branch callback.)

[edit] Sharing native functions and private data among threads

In a JS_THREADSAFE build, SpiderMonkey's internal data structures are thread-safe. In particular, all operations on strings, numbers, and booleans are trivially thread-safe because those values are all immutable; and objects are thread-safe because SpiderMonkey internally locks individual objects around property accesses.

However, SpiderMonkey does not protect the application's data structures. JSNatives and other callback functions can be called concurrently by multiple threads. Multiple threads can access private data or C/C++ global variables at the same time. It is up to the application to practice safe threading.

[edit] Sharing contexts among threads

Ordinarily, a JSContext is created, used, and destroyed by a single thread. This makes sense, as a context can only be used by one thread at a time. However, there are a few cases where an application might need to share contexts across threads. For example:

  • Many worker threads need to share a "pool" of reusable contexts, to avoid the performance cost of constantly creating and destroying contexts. (This is analogous to a database connection pool.)
  • The application has a JSContext that it needs to use each time some event happens. But the event could happen on any thread.

For such cases, use JS_ClearContextThread and JS_SetContextThread to transfer the context safely from one thread to another.

[edit] Further info

See also SpiderMonkey Internals: Thread Safety.