The following links (together) give a decent sense of how threading
works in the SpiderMonkey C API:

https://developer.mozilla.org/en/SpiderMonkey/JSAPI_Reference/JS_THREADSAFE
https://developer.mozilla.org/en/SpiderMonkey/JSAPI_Reference/JS_BeginRequest
https://developer.mozilla.org/en/SpiderMonkey/JSAPI_Reference/JS_SuspendRequest
https://developer.mozilla.org/en/SpiderMonkey/JSAPI_Reference/JS_ClearContextThread

Summarizing some of the key points for quick reference,

* JSContext is NOT threadsafe, only JSRuntime. A JSContext can only be
  used by one thread at a time. Remember that a JSContext is just a
  stack, so it makes sense that using the same stack from two threads
  at once is not allowed.

* to call into JS_* functions, you need to have done a
  JS_BeginRequest().

  If a context has a request, then it has a lock on the runtime in
  effect, though SpiderMonkey is pretty smart and doesn't actually
  have a big global lock for most purposes. It does have a big global
  lock for running the GC (to run the GC, there can't be two contexts
  in a request).

  JS_BeginRequest() / JS_EndRequest() calls can be nested safely.

* When you JS_DefineFunction() or JS_InitClass() or use other APIs
  that specify callbacks to be invoked by SpiderMonkey, SpiderMonkey
  will automatically put a request around those callbacks.

  The implication is that when you're defining an object or functions
  in native code, usually you don't need to manually put in begin/end
  request calls inside the native functions.

* If you're going to block to do either a long CPU computation or some
  kind of IO, then you want to release the request so you aren't
  blocking other contexts or blocking GC.

  This is done with JS_SuspendRequest() and JS_ResumeRequest().

  jsrefcount saved = JS_SuspendRequest(context);
  block();
  JS_ResumeRequest(context, saved);

  TODO: EXISTING CODE DOES NOT DO THIS YET.

* The "load" and "call" contexts are problematic because in the
  current gjs architecture they would need to be shared among threads.
  However, contexts can't be shared among threads. We have to figure
  this out.

  The most likely solution is that there's a big (recursive) lock on
  these contexts that threads have to grab. After getting the lock the
  context can be moved to the current thread with
  JS_SetContextThread().

  We could change gjs_runtime_get_load_context() to be
  acquire_load_context() and add a matching release_load_context(); in
  addition to getting the context, acquire_load_context() would get
  the load context lock and JS_SetContextThread().

  TODO: THIS IS NOT CODED.

* Hopefully obvious, but the actual functional code has to be properly
  threadsafe. If you have a global variable, it will need a lock.
  Use GMutex and so forth from GLib.

  TODO: EXISTING CODE DOES NOT DO THIS YET.
