void js_DestroyContext(JSContext *cx) { JSRuntime *rt; JSBool rtempty; rt = cx->runtime; PR_ASSERT(JS_IS_RUNTIME_LOCKED(rt)); /* Remove cx from context list first. */ PR_REMOVE_LINK(&cx->links); rtempty = (rt->contextList.next == (PRCList *)&rt->contextList); if (js_InterpreterHooks && js_InterpreterHooks->destroyContext) { /* This is a stub, but in case it removes roots, call it now. */ js_InterpreterHooks->destroyContext(cx); } if (rtempty) { /* No more contexts: clear debugging state to remove GC roots. */ JS_ClearAllTraps(cx); JS_ClearAllWatchPoints(cx); } /* Remove more GC roots in regExpStatics, then collect garbage. */ #if JS_HAS_REGEXPS js_FreeRegExpStatics(cx, &cx->regExpStatics); #endif js_ForceGC(cx); if (rtempty) { /* Free atom state now that we've run the GC. */ js_FreeAtomState(cx, &rt->atomState); } /* Free the stuff hanging off of cx. */ PR_FinishArenaPool(&cx->stackPool); PR_FinishArenaPool(&cx->codePool); PR_FinishArenaPool(&cx->tempPool); if (cx->lastMessage) free(cx->lastMessage); free(cx); }
void js_FinishAtomState(JSAtomState *state) { UninternArgs args; if (!state->runtime) return; args.rt = state->runtime; args.leaks = 0; JS_HashTableEnumerateEntries(state->table, js_atom_uninterner, &args); #ifdef DEBUG if (args.leaks != 0) { fprintf(stderr, "JS engine warning: %lu atoms remain after destroying the JSRuntime.\n" " These atoms may point to freed memory. Things reachable\n" " through them have not been finalized.\n", (unsigned long) args.leaks); } #endif js_FreeAtomState(NULL, state); }
JSBool js_InitAtomState(JSContext *cx, JSAtomState *state) { state->runtime = cx->runtime; state->table = JS_NewHashTable(JS_ATOM_HASH_SIZE, js_hash_atom_key, js_compare_atom_keys, js_compare_stub, &atom_alloc_ops, state); if (!state->table) { JS_ReportOutOfMemory(cx); return JS_FALSE; } #ifdef JS_THREADSAFE js_InitLock(&state->lock); state->tablegen = 0; #endif if (!js_InitPinnedAtoms(cx, state)) { js_FreeAtomState(cx, state); return JS_FALSE; } return JS_TRUE; }
void js_DestroyContext(JSContext *cx, JSGCMode gcmode) { JSRuntime *rt; JSBool last; JSArgumentFormatMap *map; rt = cx->runtime; /* Remove cx from context list first. */ JS_LOCK_RUNTIME(rt); JS_ASSERT(rt->state == JSRTS_UP || rt->state == JSRTS_LAUNCHING); JS_REMOVE_LINK(&cx->links); last = (rt->contextList.next == &rt->contextList); if (last) rt->state = JSRTS_LANDING; JS_UNLOCK_RUNTIME(rt); if (last) { /* Unpin all pinned atoms before final GC. */ js_UnpinPinnedAtoms(&rt->atomState); /* Unlock and clear GC things held by runtime pointers. */ js_FinishRuntimeNumberState(cx); js_FinishRuntimeStringState(cx); /* Clear debugging state to remove GC roots. */ JS_ClearAllTraps(cx); JS_ClearAllWatchPoints(cx); } #if JS_HAS_REGEXPS /* * Remove more GC roots in regExpStatics, then collect garbage. * XXX anti-modularity alert: we rely on the call to js_RemoveRoot within * XXX this function call to wait for any racing GC to complete, in the * XXX case where JS_DestroyContext is called outside of a request on cx */ js_FreeRegExpStatics(cx, &cx->regExpStatics); #endif #ifdef JS_THREADSAFE /* * Destroying a context implicitly calls JS_EndRequest(). Also, we must * end our request here in case we are "last" -- in that event, another * js_DestroyContext that was not last might be waiting in the GC for our * request to end. We'll let it run below, just before we do the truly * final GC and then free atom state. * * At this point, cx must be inaccessible to other threads. It's off the * rt->contextList, and it should not be reachable via any object private * data structure. */ while (cx->requestDepth != 0) JS_EndRequest(cx); #endif if (last) { /* Always force, so we wait for any racing GC to finish. */ js_ForceGC(cx); /* Iterate until no finalizer removes a GC root or lock. */ while (rt->gcPoke) js_GC(cx, GC_LAST_CONTEXT); /* Try to free atom state, now that no unrooted scripts survive. */ if (rt->atomState.liveAtoms == 0) js_FreeAtomState(cx, &rt->atomState); /* Take the runtime down, now that it has no contexts or atoms. */ JS_LOCK_RUNTIME(rt); rt->state = JSRTS_DOWN; JS_NOTIFY_ALL_CONDVAR(rt->stateChange); JS_UNLOCK_RUNTIME(rt); } else { if (gcmode == JS_FORCE_GC) js_ForceGC(cx); else if (gcmode == JS_MAYBE_GC) JS_MaybeGC(cx); } /* Free the stuff hanging off of cx. */ JS_FinishArenaPool(&cx->stackPool); JS_FinishArenaPool(&cx->codePool); JS_FinishArenaPool(&cx->notePool); JS_FinishArenaPool(&cx->tempPool); if (cx->lastMessage) free(cx->lastMessage); /* Remove any argument formatters. */ map = cx->argumentFormatMap; while (map) { JSArgumentFormatMap *temp = map; map = map->next; JS_free(cx, temp); } /* Destroy the resolve recursion damper. */ if (cx->resolvingTable) { JS_DHashTableDestroy(cx->resolvingTable); cx->resolvingTable = NULL; } /* Finally, free cx itself. */ free(cx); }
void js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) { JSRuntime *rt; JSContextCallback cxCallback; JSBool last; JSArgumentFormatMap *map; JSLocalRootStack *lrs; JSLocalRootChunk *lrc; rt = cx->runtime; if (mode != JSDCM_NEW_FAILED) { cxCallback = rt->cxCallback; if (cxCallback) { /* * JSCONTEXT_DESTROY callback is not allowed to fail and must * return true. */ #ifdef DEBUG JSBool callbackStatus = #endif cxCallback(cx, JSCONTEXT_DESTROY); JS_ASSERT(callbackStatus); } } /* Remove cx from context list first. */ JS_LOCK_GC(rt); JS_ASSERT(rt->state == JSRTS_UP || rt->state == JSRTS_LAUNCHING); JS_REMOVE_LINK(&cx->links); last = (rt->contextList.next == &rt->contextList); if (last) rt->state = JSRTS_LANDING; JS_UNLOCK_GC(rt); if (last) { #ifdef JS_THREADSAFE /* * If cx is not in a request already, begin one now so that we wait * for any racing GC started on a not-last context to finish, before * we plow ahead and unpin atoms. Note that even though we begin a * request here if necessary, we end all requests on cx below before * forcing a final GC. This lets any not-last context destruction * racing in another thread try to force or maybe run the GC, but by * that point, rt->state will not be JSRTS_UP, and that GC attempt * will return early. */ if (cx->requestDepth == 0) JS_BeginRequest(cx); #endif /* Unpin all pinned atoms before final GC. */ js_UnpinPinnedAtoms(&rt->atomState); /* Unlock and clear GC things held by runtime pointers. */ js_FinishRuntimeNumberState(cx); js_FinishRuntimeStringState(cx); /* Clear debugging state to remove GC roots. */ JS_ClearAllTraps(cx); JS_ClearAllWatchPoints(cx); } /* * Remove more GC roots in regExpStatics, then collect garbage. * XXX anti-modularity alert: we rely on the call to js_RemoveRoot within * XXX this function call to wait for any racing GC to complete, in the * XXX case where JS_DestroyContext is called outside of a request on cx */ js_FreeRegExpStatics(cx, &cx->regExpStatics); #ifdef JS_THREADSAFE /* * Destroying a context implicitly calls JS_EndRequest(). Also, we must * end our request here in case we are "last" -- in that event, another * js_DestroyContext that was not last might be waiting in the GC for our * request to end. We'll let it run below, just before we do the truly * final GC and then free atom state. * * At this point, cx must be inaccessible to other threads. It's off the * rt->contextList, and it should not be reachable via any object private * data structure. */ while (cx->requestDepth != 0) JS_EndRequest(cx); #endif if (last) { /* Always force, so we wait for any racing GC to finish. */ js_ForceGC(cx, GC_LAST_CONTEXT); /* Iterate until no JSGC_END-status callback creates more garbage. */ while (rt->gcPoke) js_GC(cx, GC_LAST_CONTEXT); /* Try to free atom state, now that no unrooted scripts survive. */ if (rt->atomState.liveAtoms == 0) js_FreeAtomState(cx, &rt->atomState); /* Also free the script filename table if it exists and is empty. */ if (rt->scriptFilenameTable && rt->scriptFilenameTable->nentries == 0) js_FinishRuntimeScriptState(rt); /* * Free the deflated string cache, but only after the last GC has * collected all unleaked strings. */ js_FinishDeflatedStringCache(rt); /* Take the runtime down, now that it has no contexts or atoms. */ JS_LOCK_GC(rt); rt->state = JSRTS_DOWN; JS_NOTIFY_ALL_CONDVAR(rt->stateChange); JS_UNLOCK_GC(rt); } else { if (mode == JSDCM_FORCE_GC) js_ForceGC(cx, 0); else if (mode == JSDCM_MAYBE_GC) JS_MaybeGC(cx); } /* Free the stuff hanging off of cx. */ JS_FinishArenaPool(&cx->stackPool); JS_FinishArenaPool(&cx->tempPool); if (cx->lastMessage) free(cx->lastMessage); /* Remove any argument formatters. */ map = cx->argumentFormatMap; while (map) { JSArgumentFormatMap *temp = map; map = map->next; JS_free(cx, temp); } /* Destroy the resolve recursion damper. */ if (cx->resolvingTable) { JS_DHashTableDestroy(cx->resolvingTable); cx->resolvingTable = NULL; } lrs = cx->localRootStack; if (lrs) { while ((lrc = lrs->topChunk) != &lrs->firstChunk) { lrs->topChunk = lrc->down; JS_free(cx, lrc); } JS_free(cx, lrs); } #ifdef JS_THREADSAFE js_ClearContextThread(cx); #endif /* Finally, free cx itself. */ free(cx); }