Subj : Re: Puzzling assertion in js_AllocGCThing() To : Bob Kline From : Brendan Eich Date : Sun Mar 14 2004 12:18 pm Bob Kline wrote: > > So, the calls JS_BeginRequest and JS_EndRequest tell the SpiderMonkey engine > that it's not safe to perform any operations (for example, garbage > collection) which must be single-threaded. Since I don't see any indication > in the JS_ API as to which calls could trigger such operations, I have to > assume that the Begin/End bracket must surround *any* calls to the > SpiderMonkey package Right, I didn't qualify "API calls" in "must bracket hunks of API calls embedded in native code with requests." > except, presumably, calls to create or destroy the > contexts and the runtime, since the runtime is needed to create the > contexts, and the context is needed for the calls to the Begin/End request > marker functions. Quite right! There are a few other API calls that take no cx parameter, and may therefore be called outside of a request. See for example APIs such as JS_AddNamedRootRT and JS_RemoveNamedRootRT. > The pair JS_SuspendRequest and JS_ResumeRequest would be > used inside a Begin/End block for blocking or long-running native code which > is guaranteed *not* to have any JS_xxx calls in it. Right -- "native" implies "not JS engine", although the terminology is used freely. A native method in Java or JS is one implemented in C or C++ or another "host" or "embedding" language. > So, in the Sablotron > engine, processing might need to look something like this to be thread-safe: > > codeWhichUsesSpiderMonkey() > { > JSContext* context = getOrCreateContext(); > JS_BeginRequest(context); > JS_This(); > JS_That(); > JS_SuspendRequest(context); > talkToDatabase(); // no JS_ calls allowed in here > JS_ResumeRequest(context); > JS_TheOther(); > JS_EndRequest(context); > } > > Assuming I got it right so far (big assumption, I know), You're doing fine. > there's still a couple of points on which I'm still fuzzy: > > 1. If the JavaScript package won't do garbage collection while any thread is > in an unsuspended request (as determined by these four JS_xxxRequest() > functions), then presumably calls to JS_GC() and JS_MaybeGC() would also > have to be placed outside the fenced-off request block. Is this right? No, a GC call from an active request (or from a set of active requests nesting on the same thread -- whether or not on the same JSContext! -- but you should try to pool contexts to threads and use one per thread) is smart enough to suspend the requests active on the calling thread, automatically. So your embedding host code doesn't need to call JS_SuspendRequest/JS_ResumeRequest around calls you make to JS_GC or JS_MaybeGC. > 2. From my reading of the JS_ API docs, I get the impression that if the > client code (Sablotron, in this case) never invokes either of the GC calls, > garbage collection will be done as needed anyway internally. Is this > correct? Not quite. If JS_NewRuntime (aka JS_Init, historical entry point name, kept in the interest of binary compatibility) is given a small-enough maxbytes parameter, then when the GC has allocated at least maxbytes bytes of JSGCThings from its heap (this does not include malloc'd memory associated with string and object gc-things, note well), the next attempt to allocate a gc-thing will try a "last ditch" GC to collect garbage, and retry the allocation attempt once. If that last-ditch GC did not free anything, thereby resettign the internal per-runtime maxbytes accounting, then the attempt fails. Your embedding host code should call JS_GC and JS_MaybeGC from other places to avoid falling into this last ditch, to smooth out memory use and GC overhead: - From the embedding-specific branch callback you define and configure using JS_SetBranchCallback. This should keep an unsigned counter and try JS_MaybeGC every large-power-of-two attempts, or based on well-amortized time deltas. See for example http://lxr.mozilla.org/mozilla/ident?i=DOMBranchCallback. The branch callback is invoked for every backward branch bytecode, and for returns from functions. Your implementation of it should be optimized to take as few cycles as possible (hence the unsigned counter and large power of two modulus, which can be strength-reduced to a mask operation, by hand or by a decent compiler). - When your embedding is doing some mandatory-cleanup operation such as closing a window at a user's request, and such an operation is bound to create garbage to collect. Your embedding host code would probably call JS_GC in such cases, although it might use a timer to coalesce bursts of JS_GC calls into one slightly delayed JS_GC call. See http://lxr.mozilla.org/mozilla/ident?i=FireGCTimer for an example. /be .