void nsXPCWrappedJS::Unlink() { if (IsValid()) { XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); if (rt) { if (mRoot == this) { // remove this root wrapper from the map JSObject2WrappedJSMap* map = rt->GetWrappedJSMap(); if (map) { XPCAutoLock lock(rt->GetMapLock()); map->Remove(this); } } if (mRefCnt > 1) RemoveFromRootSet(rt->GetMapLock()); } mJSObj = nsnull; } if (mRoot == this) { ClearWeakReferences(); } else if (mRoot) { // unlink this wrapper nsXPCWrappedJS* cur = mRoot; while (1) { if (cur->mNext == this) { cur->mNext = mNext; break; } cur = cur->mNext; NS_ASSERTION(cur, "failed to find wrapper in its own chain"); } // let the root go NS_RELEASE(mRoot); } NS_IF_RELEASE(mClass); if (mOuter) { XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); if (rt->GetThreadRunningGC()) { rt->DeferredRelease(mOuter); mOuter = nsnull; } else { NS_RELEASE(mOuter); } } }
nsrefcnt nsXPCWrappedJS::Release(void) { if (!MOZ_LIKELY(NS_IsMainThread() || NS_IsCycleCollectorThread())) MOZ_CRASH(); NS_PRECONDITION(0 != mRefCnt, "dup release"); if (mMainThreadOnly && !NS_IsMainThread()) { // We'd like to abort here, but this can happen if someone uses a proxy // for the nsXPCWrappedJS. nsCOMPtr<nsIThread> mainThread = do_GetMainThread(); // If we can't get the main thread anymore we just leak, but this really // shouldn't happen. NS_ASSERTION(mainThread, "Can't get main thread, leaking nsXPCWrappedJS!"); if (mainThread) { NS_ProxyRelease(mainThread, static_cast<nsIXPConnectWrappedJS*>(this)); } return mRefCnt; } // need to take the map lock here to prevent GetNewOrUsed from trying // to reuse a wrapper on one thread while it's being destroyed on another XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); XPCAutoLock lock(rt->GetMapLock()); do_decrement: nsrefcnt cnt = --mRefCnt; NS_LOG_RELEASE(this, cnt, "nsXPCWrappedJS"); if (0 == cnt) { delete this; // also unlinks us from chain return 0; } if (1 == cnt) { if (IsValid()) RemoveFromRootSet(rt->GetMapLock()); // If we are not the root wrapper or if we are not being used from a // weak reference, then this extra ref is not needed and we can let // ourself be deleted. // Note: HasWeakReferences() could only return true for the root. if (!HasWeakReferences()) goto do_decrement; } return cnt; }
// static XPCNativeSet* XPCNativeSet::GetNewOrUsed(XPCCallContext& ccx, const nsIID* iid) { AutoMarkingNativeSetPtr set(ccx); AutoMarkingNativeInterfacePtr iface(ccx); iface = XPCNativeInterface::GetNewOrUsed(ccx, iid); if(!iface) return nsnull; XPCNativeSetKey key(nsnull, iface, 0); XPCJSRuntime* rt = ccx.GetRuntime(); NativeSetMap* map = rt->GetNativeSetMap(); if(!map) return nsnull; { // scoped lock XPCAutoLock lock(rt->GetMapLock()); set = map->Find(&key); } if(set) return set; // hacky way to get a XPCNativeInterface** using the AutoPtr XPCNativeInterface* temp[] = {iface}; set = NewInstance(ccx, temp, 1); if(!set) return nsnull; { // scoped lock XPCAutoLock lock(rt->GetMapLock()); XPCNativeSet* set2 = map->Add(&key, set); if(!set2) { NS_ERROR("failed to add our set!"); DestroyInstance(set); set = nsnull; } else if(set2 != set) { DestroyInstance(set); set = set2; } } return set; }
// static XPCNativeSet* XPCNativeSet::GetNewOrUsed(XPCCallContext& ccx, XPCNativeSet* otherSet, XPCNativeInterface* newInterface, PRUint16 position) { AutoMarkingNativeSetPtr set(ccx); XPCJSRuntime* rt = ccx.GetRuntime(); NativeSetMap* map = rt->GetNativeSetMap(); if(!map) return nsnull; XPCNativeSetKey key(otherSet, newInterface, position); { // scoped lock XPCAutoLock lock(rt->GetMapLock()); set = map->Find(&key); } if(set) return set; if(otherSet) set = NewInstanceMutate(otherSet, newInterface, position); else set = NewInstance(ccx, &newInterface, 1); if(!set) return nsnull; { // scoped lock XPCAutoLock lock(rt->GetMapLock()); XPCNativeSet* set2 = map->Add(&key, set); if(!set2) { NS_ERROR("failed to add our set!"); DestroyInstance(set); set = nsnull; } else if(set2 != set) { DestroyInstance(set); set = set2; } } return set; }
// static XPCJSRuntime* XPCJSRuntime::newXPCJSRuntime(nsXPConnect* aXPConnect, nsIJSRuntimeService* aJSRuntimeService) { NS_PRECONDITION(aXPConnect,"bad param"); NS_PRECONDITION(aJSRuntimeService,"bad param"); XPCJSRuntime* self; self = new XPCJSRuntime(aXPConnect, aJSRuntimeService); if(self && self->GetJSRuntime() && self->GetContextMap() && self->GetWrappedJSMap() && self->GetWrappedJSClassMap() && self->GetIID2NativeInterfaceMap() && self->GetClassInfo2NativeSetMap() && self->GetNativeSetMap() && self->GetThisTranslatorMap() && self->GetNativeScriptableSharedMap() && self->GetDyingWrappedNativeProtoMap() && self->GetExplicitNativeWrapperMap() && self->GetMapLock()) { return self; } delete self; return nsnull; }
// static XPCNativeInterface* XPCNativeInterface::GetNewOrUsed(XPCCallContext& ccx, nsIInterfaceInfo* info) { AutoMarkingNativeInterfacePtr iface(ccx); const nsIID* iid; if(NS_FAILED(info->GetIIDShared(&iid)) || !iid) return nsnull; XPCJSRuntime* rt = ccx.GetRuntime(); IID2NativeInterfaceMap* map = rt->GetIID2NativeInterfaceMap(); if(!map) return nsnull; { // scoped lock XPCAutoLock lock(rt->GetMapLock()); iface = map->Find(*iid); } if(iface) return iface; iface = NewInstance(ccx, info); if(!iface) return nsnull; { // scoped lock XPCAutoLock lock(rt->GetMapLock()); XPCNativeInterface* iface2 = map->Add(iface); if(!iface2) { NS_ERROR("failed to add our interface!"); DestroyInstance(ccx, rt, iface); iface = nsnull; } else if(iface2 != iface) { DestroyInstance(ccx, rt, iface); iface = iface2; } } return iface; }
// static XPCNativeInterface* XPCNativeInterface::GetNewOrUsed(XPCCallContext& ccx, const nsIID* iid) { AutoMarkingNativeInterfacePtr iface(ccx); XPCJSRuntime* rt = ccx.GetRuntime(); IID2NativeInterfaceMap* map = rt->GetIID2NativeInterfaceMap(); if(!map) return nsnull; { // scoped lock XPCAutoLock lock(rt->GetMapLock()); iface = map->Find(*iid); } if(iface) return iface; nsCOMPtr<nsIInterfaceInfo> info; ccx.GetXPConnect()->GetInfoForIID(iid, getter_AddRefs(info)); if(!info) return nsnull; iface = NewInstance(ccx, info); if(!iface) return nsnull; { // scoped lock XPCAutoLock lock(rt->GetMapLock()); XPCNativeInterface* iface2 = map->Add(iface); if(!iface2) { NS_ERROR("failed to add our interface!"); DestroyInstance(ccx, rt, iface); iface = nsnull; } else if(iface2 != iface) { DestroyInstance(ccx, rt, iface); iface = iface2; } } return iface; }
// static XPCNativeInterface* XPCNativeInterface::GetNewOrUsed(const nsIID* iid) { AutoJSContext cx; AutoMarkingNativeInterfacePtr iface(cx); XPCJSRuntime* rt = XPCJSRuntime::Get(); IID2NativeInterfaceMap* map = rt->GetIID2NativeInterfaceMap(); if (!map) return nullptr; { // scoped lock XPCAutoLock lock(rt->GetMapLock()); iface = map->Find(*iid); } if (iface) return iface; nsCOMPtr<nsIInterfaceInfo> info; XPTInterfaceInfoManager::GetSingleton()->GetInfoForIID(iid, getter_AddRefs(info)); if (!info) return nullptr; iface = NewInstance(info); if (!iface) return nullptr; { // scoped lock XPCAutoLock lock(rt->GetMapLock()); XPCNativeInterface* iface2 = map->Add(iface); if (!iface2) { NS_ERROR("failed to add our interface!"); DestroyInstance(iface); iface = nullptr; } else if (iface2 != iface) { DestroyInstance(iface); iface = iface2; } } return iface; }
// static void XPCNativeSet::ClearCacheEntryForClassInfo(nsIClassInfo* classInfo) { XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); ClassInfo2NativeSetMap* map = rt->GetClassInfo2NativeSetMap(); if (map) { // scoped lock XPCAutoLock lock(rt->GetMapLock()); map->Remove(classInfo); } }
// static void XPCWrappedNativeScope::FinishedFinalizationPhaseOfGC() { XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); // FIXME The lock may not be necessary since we are inside // JSGC_FINALIZE_END callback and at this point GC still serializes access // to JS runtime. See bug 380139. XPCAutoLock lock(rt->GetMapLock()); KillDyingScopes(); }
size_t XPCWrappedNativeScope::SizeOfAllScopesIncludingThis(nsMallocSizeOfFun mallocSizeOf) { XPCJSRuntime *rt = nsXPConnect::GetRuntimeInstance(); XPCAutoLock lock(rt->GetMapLock()); size_t n = 0; for (XPCWrappedNativeScope *cur = gScopes; cur; cur = cur->mNext) { n += cur->SizeOfIncludingThis(mallocSizeOf); } return n; }
JSBool WrapperMoved(JSContext *cx, XPCWrappedNative *innerObj, XPCWrappedNativeScope *newScope) { typedef WrappedNative2WrapperMap::Link Link; XPCJSRuntime *rt = nsXPConnect::GetRuntimeInstance(); WrappedNative2WrapperMap *map = innerObj->GetScope()->GetWrapperMap(); Link *link; { // Scoped lock XPCAutoLock al(rt->GetMapLock()); link = map->FindLink(innerObj->GetFlatJSObject()); } if (!link) { // No link here means that there were no XOWs for this object. return JS_TRUE; } JSObject *xow = link->obj; { // Scoped lock. XPCAutoLock al(rt->GetMapLock()); if (!newScope->GetWrapperMap()->AddLink(innerObj->GetFlatJSObject(), link)) return JS_FALSE; map->Remove(innerObj->GetFlatJSObject()); } if (!xow) { // Nothing else to do. return JS_TRUE; } return JS_SetReservedSlot(cx, xow, XPC_XOW_ScopeSlot, PRIVATE_TO_JSVAL(newScope)) && JS_SetParent(cx, xow, newScope->GetGlobalJSObject()); }
nsXPCWrappedJS::~nsXPCWrappedJS() { NS_PRECONDITION(0 == mRefCnt, "refcounting error"); if (mRoot == this) { // Remove this root wrapper from the map XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); JSObject2WrappedJSMap* map = rt->GetWrappedJSMap(); if (map) { XPCAutoLock lock(rt->GetMapLock()); map->Remove(this); } } Unlink(); }
// static XPCNativeSet* XPCNativeSet::GetNewOrUsed(XPCCallContext& ccx, nsIClassInfo* classInfo) { AutoMarkingNativeSetPtr set(ccx); XPCJSRuntime* rt = ccx.GetRuntime(); ClassInfo2NativeSetMap* map = rt->GetClassInfo2NativeSetMap(); if (!map) return nsnull; { // scoped lock XPCAutoLock lock(rt->GetMapLock()); set = map->Find(classInfo); } if (set) return set; nsIID** iidArray = nsnull; AutoMarkingNativeInterfacePtrArrayPtr interfaceArray(ccx); PRUint32 iidCount = 0; if (NS_FAILED(classInfo->GetInterfaces(&iidCount, &iidArray))) { // Note: I'm making it OK for this call to fail so that one can add // nsIClassInfo to classes implemented in script without requiring this // method to be implemented. // Make sure these are set correctly... iidArray = nsnull; iidCount = 0; } NS_ASSERTION((iidCount && iidArray) || !(iidCount || iidArray), "GetInterfaces returned bad array"); // !!! from here on we only exit through the 'out' label !!! if (iidCount) { AutoMarkingNativeInterfacePtrArrayPtr arr(ccx, new XPCNativeInterface*[iidCount], iidCount, true); if (!arr) goto out; interfaceArray = arr; XPCNativeInterface** currentInterface = interfaceArray; nsIID** currentIID = iidArray; PRUint16 interfaceCount = 0; for (PRUint32 i = 0; i < iidCount; i++) { nsIID* iid = *(currentIID++); if (!iid) { NS_ERROR("Null found in classinfo interface list"); continue; } XPCNativeInterface* iface = XPCNativeInterface::GetNewOrUsed(ccx, iid); if (!iface) { // XXX warn here continue; } *(currentInterface++) = iface; interfaceCount++; } if (interfaceCount) { set = NewInstance(ccx, interfaceArray, interfaceCount); if (set) { NativeSetMap* map2 = rt->GetNativeSetMap(); if (!map2) goto out; XPCNativeSetKey key(set, nsnull, 0); { // scoped lock XPCAutoLock lock(rt->GetMapLock()); XPCNativeSet* set2 = map2->Add(&key, set); if (!set2) { NS_ERROR("failed to add our set!"); DestroyInstance(set); set = nsnull; goto out; } if (set2 != set) { DestroyInstance(set); set = set2; } } } } else set = GetNewOrUsed(ccx, &NS_GET_IID(nsISupports)); } else set = GetNewOrUsed(ccx, &NS_GET_IID(nsISupports)); if (set) { // scoped lock XPCAutoLock lock(rt->GetMapLock()); #ifdef DEBUG XPCNativeSet* set2 = #endif map->Add(classInfo, set); NS_ASSERTION(set2, "failed to add our set!"); NS_ASSERTION(set2 == set, "hashtables inconsistent!"); } out: if (iidArray) NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(iidCount, iidArray); if (interfaceArray) delete [] interfaceArray.get(); return set; }
// static nsresult nsXPCWrappedJS::GetNewOrUsed(JS::HandleObject jsObj, REFNSIID aIID, nsISupports* aOuter, nsXPCWrappedJS** wrapperResult) { // Do a release-mode assert against accessing nsXPCWrappedJS off-main-thread. if (!MOZ_LIKELY(NS_IsMainThread() || NS_IsCycleCollectorThread())) MOZ_CRASH(); AutoJSContext cx; JSObject2WrappedJSMap* map; nsXPCWrappedJS* root = nullptr; nsXPCWrappedJS* wrapper = nullptr; nsXPCWrappedJSClass* clazz = nullptr; XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); bool release_root = false; map = rt->GetWrappedJSMap(); if (!map) { NS_ASSERTION(map,"bad map"); return NS_ERROR_FAILURE; } nsXPCWrappedJSClass::GetNewOrUsed(cx, aIID, &clazz); if (!clazz) return NS_ERROR_FAILURE; // from here on we need to return through 'return_wrapper' // always find the root JSObject JS::RootedObject rootJSObj(cx, clazz->GetRootJSObject(cx, jsObj)); if (!rootJSObj) goto return_wrapper; // look for the root wrapper, and if found, hold the map lock until // we've added our ref to prevent another thread from destroying it // under us { // scoped lock XPCAutoLock lock(rt->GetMapLock()); root = map->Find(rootJSObj); if (root) { if ((nullptr != (wrapper = root->Find(aIID))) || (nullptr != (wrapper = root->FindInherited(aIID)))) { NS_ADDREF(wrapper); goto return_wrapper; } } } if (!root) { // build the root wrapper if (rootJSObj == jsObj) { // the root will do double duty as the interface wrapper wrapper = root = new nsXPCWrappedJS(cx, jsObj, clazz, nullptr, aOuter); if (!root) goto return_wrapper; { // scoped lock #if DEBUG_xpc_leaks printf("Created nsXPCWrappedJS %p, JSObject is %p\n", (void*)wrapper, (void*)jsObj); #endif XPCAutoLock lock(rt->GetMapLock()); map->Add(cx, root); } if (!CheckMainThreadOnly(root)) { XPCAutoLock lock(rt->GetMapLock()); map->Remove(root); wrapper = NULL; } goto return_wrapper; } else { // just a root wrapper nsXPCWrappedJSClass* rootClazz = nullptr; nsXPCWrappedJSClass::GetNewOrUsed(cx, NS_GET_IID(nsISupports), &rootClazz); if (!rootClazz) goto return_wrapper; root = new nsXPCWrappedJS(cx, rootJSObj, rootClazz, nullptr, aOuter); NS_RELEASE(rootClazz); if (!root) goto return_wrapper; release_root = true; { // scoped lock #if DEBUG_xpc_leaks printf("Created nsXPCWrappedJS %p, JSObject is %p\n", (void*)root, (void*)rootJSObj); #endif XPCAutoLock lock(rt->GetMapLock()); map->Add(cx, root); } if (!CheckMainThreadOnly(root)) { XPCAutoLock lock(rt->GetMapLock()); map->Remove(root); goto return_wrapper; } } } // at this point we have a root and may need to build the specific wrapper NS_ASSERTION(root,"bad root"); NS_ASSERTION(clazz,"bad clazz"); if (!wrapper) { wrapper = new nsXPCWrappedJS(cx, jsObj, clazz, root, aOuter); if (!wrapper) goto return_wrapper; #if DEBUG_xpc_leaks printf("Created nsXPCWrappedJS %p, JSObject is %p\n", (void*)wrapper, (void*)jsObj); #endif } wrapper->mNext = root->mNext; root->mNext = wrapper; return_wrapper: if (clazz) NS_RELEASE(clazz); if (release_root) NS_RELEASE(root); if (!wrapper) return NS_ERROR_FAILURE; *wrapperResult = wrapper; return NS_OK; }
// static JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status) { nsVoidArray* dyingWrappedJSArray; XPCJSRuntime* self = nsXPConnect::GetRuntime(); if(self) { switch(status) { case JSGC_BEGIN: { if(!NS_IsMainThread()) { return JS_FALSE; } break; } case JSGC_MARK_END: { NS_ASSERTION(!self->mDoingFinalization, "bad state"); // mThreadRunningGC indicates that GC is running { // scoped lock XPCAutoLock lock(self->GetMapLock()); NS_ASSERTION(!self->mThreadRunningGC, "bad state"); self->mThreadRunningGC = PR_GetCurrentThread(); } dyingWrappedJSArray = &self->mWrappedJSToReleaseArray; { JSDyingJSObjectData data = {cx, dyingWrappedJSArray}; // Add any wrappers whose JSObjects are to be finalized to // this array. Note that this is a nsVoidArray because // we do not want to be changing the refcount of these wrappers. // We add them to the array now and Release the array members // later to avoid the posibility of doing any JS GCThing // allocations during the gc cycle. self->mWrappedJSMap-> Enumerate(WrappedJSDyingJSObjectFinder, &data); } // Do cleanup in NativeInterfaces. This part just finds // member cloned function objects that are about to be // collected. It does not deal with collection of interfaces or // sets at this point. CX_AND_XPCRT_Data data = {cx, self}; self->mIID2NativeInterfaceMap-> Enumerate(NativeInterfaceGC, &data); // Find dying scopes... XPCWrappedNativeScope::FinishedMarkPhaseOfGC(cx, self); self->mDoingFinalization = JS_TRUE; break; } case JSGC_FINALIZE_END: { NS_ASSERTION(self->mDoingFinalization, "bad state"); self->mDoingFinalization = JS_FALSE; // Release all the members whose JSObjects are now known // to be dead. dyingWrappedJSArray = &self->mWrappedJSToReleaseArray; while(1) { nsXPCWrappedJS* wrapper; PRInt32 count = dyingWrappedJSArray->Count(); if(!count) { dyingWrappedJSArray->Compact(); break; } wrapper = static_cast<nsXPCWrappedJS*> (dyingWrappedJSArray->ElementAt(count-1)); dyingWrappedJSArray->RemoveElementAt(count-1); NS_RELEASE(wrapper); } #ifdef XPC_REPORT_NATIVE_INTERFACE_AND_SET_FLUSHING printf("--------------------------------------------------------------\n"); int setsBefore = (int) self->mNativeSetMap->Count(); int ifacesBefore = (int) self->mIID2NativeInterfaceMap->Count(); #endif // We use this occasion to mark and sweep NativeInterfaces, // NativeSets, and the WrappedNativeJSClasses... // Do the marking... XPCWrappedNativeScope::MarkAllWrappedNativesAndProtos(); self->mDetachedWrappedNativeProtoMap-> Enumerate(DetachedWrappedNativeProtoMarker, nsnull); // Mark the sets used in the call contexts. There is a small // chance that a wrapper's set will change *while* a call is // happening which uses that wrapper's old interfface set. So, // we need to do this marking to avoid collecting those sets // that might no longer be otherwise reachable from the wrappers // or the wrapperprotos. // Skip this part if XPConnect is shutting down. We get into // bad locking problems with the thread iteration otherwise. if(!self->GetXPConnect()->IsShuttingDown()) { PRLock* threadLock = XPCPerThreadData::GetLock(); if(threadLock) { // scoped lock nsAutoLock lock(threadLock); XPCPerThreadData* iterp = nsnull; XPCPerThreadData* thread; while(nsnull != (thread = XPCPerThreadData::IterateThreads(&iterp))) { // Mark those AutoMarkingPtr lists! thread->MarkAutoRootsAfterJSFinalize(); XPCCallContext* ccxp = thread->GetCallContext(); while(ccxp) { // Deal with the strictness of callcontext that // complains if you ask for a set when // it is in a state where the set could not // possibly be valid. if(ccxp->CanGetSet()) { XPCNativeSet* set = ccxp->GetSet(); if(set) set->Mark(); } if(ccxp->CanGetInterface()) { XPCNativeInterface* iface = ccxp->GetInterface(); if(iface) iface->Mark(); } ccxp = ccxp->GetPrevCallContext(); } } } } // Do the sweeping... // We don't want to sweep the JSClasses at shutdown time. // At this point there may be JSObjects using them that have // been removed from the other maps. if(!self->GetXPConnect()->IsShuttingDown()) { self->mNativeScriptableSharedMap-> Enumerate(JSClassSweeper, nsnull); } self->mClassInfo2NativeSetMap-> Enumerate(NativeUnMarkedSetRemover, nsnull); self->mNativeSetMap-> Enumerate(NativeSetSweeper, nsnull); CX_AND_XPCRT_Data data = {cx, self}; self->mIID2NativeInterfaceMap-> Enumerate(NativeInterfaceSweeper, &data); #ifdef DEBUG XPCWrappedNativeScope::ASSERT_NoInterfaceSetsAreMarked(); #endif #ifdef XPC_REPORT_NATIVE_INTERFACE_AND_SET_FLUSHING int setsAfter = (int) self->mNativeSetMap->Count(); int ifacesAfter = (int) self->mIID2NativeInterfaceMap->Count(); printf("\n"); printf("XPCNativeSets: before: %d collected: %d remaining: %d\n", setsBefore, setsBefore - setsAfter, setsAfter); printf("XPCNativeInterfaces: before: %d collected: %d remaining: %d\n", ifacesBefore, ifacesBefore - ifacesAfter, ifacesAfter); printf("--------------------------------------------------------------\n"); #endif // Sweep scopes needing cleanup XPCWrappedNativeScope::FinishedFinalizationPhaseOfGC(cx); // Now we are going to recycle any unused WrappedNativeTearoffs. // We do this by iterating all the live callcontexts (on all // threads!) and marking the tearoffs in use. And then we // iterate over all the WrappedNative wrappers and sweep their // tearoffs. // // This allows us to perhaps minimize the growth of the // tearoffs. And also makes us not hold references to interfaces // on our wrapped natives that we are not actually using. // // XXX We may decide to not do this on *every* gc cycle. // Skip this part if XPConnect is shutting down. We get into // bad locking problems with the thread iteration otherwise. if(!self->GetXPConnect()->IsShuttingDown()) { PRLock* threadLock = XPCPerThreadData::GetLock(); if(threadLock) { // Do the marking... { // scoped lock nsAutoLock lock(threadLock); XPCPerThreadData* iterp = nsnull; XPCPerThreadData* thread; while(nsnull != (thread = XPCPerThreadData::IterateThreads(&iterp))) { XPCCallContext* ccxp = thread->GetCallContext(); while(ccxp) { // Deal with the strictness of callcontext that // complains if you ask for a tearoff when // it is in a state where the tearoff could not // possibly be valid. if(ccxp->CanGetTearOff()) { XPCWrappedNativeTearOff* to = ccxp->GetTearOff(); if(to) to->Mark(); } ccxp = ccxp->GetPrevCallContext(); } } } // Do the sweeping... XPCWrappedNativeScope::SweepAllWrappedNativeTearOffs(); } } // Now we need to kill the 'Dying' XPCWrappedNativeProtos. // We transfered these native objects to this table when their // JSObject's were finalized. We did not destroy them immediately // at that point because the ordering of JS finalization is not // deterministic and we did not yet know if any wrappers that // might still be referencing the protos where still yet to be // finalized and destroyed. We *do* know that the protos' // JSObjects would not have been finalized if there were any // wrappers that referenced the proto but where not themselves // slated for finalization in this gc cycle. So... at this point // we know that any and all wrappers that might have been // referencing the protos in the dying list are themselves dead. // So, we can safely delete all the protos in the list. self->mDyingWrappedNativeProtoMap-> Enumerate(DyingProtoKiller, nsnull); // mThreadRunningGC indicates that GC is running. // Clear it and notify waiters. { // scoped lock XPCAutoLock lock(self->GetMapLock()); NS_ASSERTION(self->mThreadRunningGC == PR_GetCurrentThread(), "bad state"); self->mThreadRunningGC = nsnull; xpc_NotifyAll(self->GetMapLock()); } break; } case JSGC_END: { // NOTE that this event happens outside of the gc lock in // the js engine. So this could be simultaneous with the // events above. // Do any deferred released of native objects. nsVoidArray* array = &self->mNativesToReleaseArray; #ifdef XPC_TRACK_DEFERRED_RELEASES printf("XPC - Begin deferred Release of %d nsISupports pointers\n", array->Count()); #endif while(1) { nsISupports* obj; { PRInt32 count = array->Count(); if(!count) { array->Compact(); break; } obj = reinterpret_cast<nsISupports*> (array->ElementAt(count-1)); array->RemoveElementAt(count-1); } NS_RELEASE(obj); } #ifdef XPC_TRACK_DEFERRED_RELEASES printf("XPC - End deferred Releases\n"); #endif break; } default: break; } } // always chain to old GCCallback if non-null. return gOldJSGCCallback ? gOldJSGCCallback(cx, status) : JS_TRUE; }
// static nsresult nsXPCWrappedJS::GetNewOrUsed(XPCCallContext& ccx, JSObject* aJSObj, REFNSIID aIID, nsISupports* aOuter, nsXPCWrappedJS** wrapperResult) { JSObject2WrappedJSMap* map; JSObject* rootJSObj; nsXPCWrappedJS* root = nsnull; nsXPCWrappedJS* wrapper = nsnull; nsXPCWrappedJSClass* clazz = nsnull; XPCJSRuntime* rt = ccx.GetRuntime(); JSBool release_root = JS_FALSE; map = rt->GetWrappedJSMap(); if (!map) { NS_ASSERTION(map,"bad map"); return NS_ERROR_FAILURE; } nsXPCWrappedJSClass::GetNewOrUsed(ccx, aIID, &clazz); if (!clazz) return NS_ERROR_FAILURE; // from here on we need to return through 'return_wrapper' // always find the root JSObject rootJSObj = clazz->GetRootJSObject(ccx, aJSObj); if (!rootJSObj) goto return_wrapper; // look for the root wrapper, and if found, hold the map lock until // we've added our ref to prevent another thread from destroying it // under us { // scoped lock XPCAutoLock lock(rt->GetMapLock()); root = map->Find(rootJSObj); if (root) { if ((nsnull != (wrapper = root->Find(aIID))) || (nsnull != (wrapper = root->FindInherited(aIID)))) { NS_ADDREF(wrapper); goto return_wrapper; } } } if (!root) { // build the root wrapper if (rootJSObj == aJSObj) { // the root will do double duty as the interface wrapper wrapper = root = new nsXPCWrappedJS(ccx, aJSObj, clazz, nsnull, aOuter); if (!root) goto return_wrapper; { // scoped lock #if DEBUG_xpc_leaks printf("Created nsXPCWrappedJS %p, JSObject is %p\n", (void*)wrapper, (void*)aJSObj); #endif XPCAutoLock lock(rt->GetMapLock()); map->Add(root); } if (!CheckMainThreadOnly(root)) { XPCAutoLock lock(rt->GetMapLock()); map->Remove(root); wrapper = NULL; } goto return_wrapper; } else { // just a root wrapper nsXPCWrappedJSClass* rootClazz = nsnull; nsXPCWrappedJSClass::GetNewOrUsed(ccx, NS_GET_IID(nsISupports), &rootClazz); if (!rootClazz) goto return_wrapper; root = new nsXPCWrappedJS(ccx, rootJSObj, rootClazz, nsnull, aOuter); NS_RELEASE(rootClazz); if (!root) goto return_wrapper; release_root = JS_TRUE; { // scoped lock #if DEBUG_xpc_leaks printf("Created nsXPCWrappedJS %p, JSObject is %p\n", (void*)root, (void*)rootJSObj); #endif XPCAutoLock lock(rt->GetMapLock()); map->Add(root); } if (!CheckMainThreadOnly(root)) { XPCAutoLock lock(rt->GetMapLock()); map->Remove(root); goto return_wrapper; } } } // at this point we have a root and may need to build the specific wrapper NS_ASSERTION(root,"bad root"); NS_ASSERTION(clazz,"bad clazz"); if (!wrapper) { wrapper = new nsXPCWrappedJS(ccx, aJSObj, clazz, root, aOuter); if (!wrapper) goto return_wrapper; #if DEBUG_xpc_leaks printf("Created nsXPCWrappedJS %p, JSObject is %p\n", (void*)wrapper, (void*)aJSObj); #endif } wrapper->mNext = root->mNext; root->mNext = wrapper; return_wrapper: if (clazz) NS_RELEASE(clazz); if (release_root) NS_RELEASE(root); if (!wrapper) return NS_ERROR_FAILURE; *wrapperResult = wrapper; return NS_OK; }