Bug 684938 - Proxy nsXPCWrappedJS::Release to the main thread. r=mrbkap.

--HG--
extra : rebase_source : faf9c46616c52bc03b776a4aae8fd3f87e11f132
This commit is contained in:
Peter Van der Beken 2011-09-14 08:44:27 -07:00
parent 610dd20bc3
commit b63af58931
2 changed files with 64 additions and 2 deletions

View File

@ -43,6 +43,7 @@
#include "xpcprivate.h"
#include "nsAtomicRefcnt.h"
#include "nsProxyRelease.h"
#include "nsThreadUtils.h"
#include "nsTextFormatter.h"
@ -196,6 +197,21 @@ nsXPCWrappedJS::Release(void)
{
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();
@ -264,6 +280,28 @@ nsXPCWrappedJS::GetJSObject(JSObject** aJSObj)
return NS_OK;
}
static bool
CheckMainThreadOnly(nsXPCWrappedJS *aWrapper)
{
if(aWrapper->IsMainThreadOnly())
return NS_IsMainThread();
nsCOMPtr<nsIClassInfo> ci;
CallQueryInterface(aWrapper, getter_AddRefs(ci));
if (ci) {
PRUint32 flags;
if (NS_SUCCEEDED(ci->GetFlags(&flags)) && !(flags & nsIClassInfo::MAIN_THREAD_ONLY))
return true;
if (!NS_IsMainThread())
return false;
}
aWrapper->SetIsMainThreadOnly();
return true;
}
// static
nsresult
nsXPCWrappedJS::GetNewOrUsed(XPCCallContext& ccx,
@ -317,7 +355,9 @@ nsXPCWrappedJS::GetNewOrUsed(XPCCallContext& ccx,
// the root will do double duty as the interface wrapper
wrapper = root = new nsXPCWrappedJS(ccx, aJSObj, clazz, nsnull,
aOuter);
if (root)
if (!root)
goto return_wrapper;
{ // scoped lock
#if DEBUG_xpc_leaks
printf("Created nsXPCWrappedJS %p, JSObject is %p\n",
@ -326,6 +366,14 @@ nsXPCWrappedJS::GetNewOrUsed(XPCCallContext& ccx,
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
@ -351,6 +399,13 @@ nsXPCWrappedJS::GetNewOrUsed(XPCCallContext& ccx,
XPCAutoLock lock(rt->GetMapLock());
map->Add(root);
}
if (!CheckMainThreadOnly(root)) {
XPCAutoLock lock(rt->GetMapLock());
map->Remove(root);
goto return_wrapper;
}
}
}
@ -395,7 +450,8 @@ nsXPCWrappedJS::nsXPCWrappedJS(XPCCallContext& ccx,
mRoot(root ? root : this),
mNext(nsnull),
mOuter(root ? nsnull : aOuter),
mMainThread(NS_IsMainThread())
mMainThread(NS_IsMainThread()),
mMainThreadOnly(root && root->mMainThreadOnly)
{
#ifdef DEBUG_stats_jband
static int count = 0;
@ -404,6 +460,8 @@ nsXPCWrappedJS::nsXPCWrappedJS(XPCCallContext& ccx,
printf("//////// %d instances of nsXPCWrappedJS created\n", count);
#endif
JS_ASSERT_IF(mMainThreadOnly, mMainThread);
InitStub(GetClass()->GetIID());
// intentionally do double addref - see Release().

View File

@ -3036,6 +3036,9 @@ public:
JSBool IsAggregatedToNative() const {return mRoot->mOuter != nsnull;}
nsISupports* GetAggregatedNativeObject() const {return mRoot->mOuter;}
void SetIsMainThreadOnly() {JS_ASSERT(mMainThread); mMainThreadOnly = true;}
bool IsMainThreadOnly() const {return mMainThreadOnly;}
void TraceJS(JSTracer* trc);
#ifdef DEBUG
static void PrintTraceName(JSTracer* trc, char *buf, size_t bufsize);
@ -3059,6 +3062,7 @@ private:
nsXPCWrappedJS* mNext;
nsISupports* mOuter; // only set in root
bool mMainThread;
bool mMainThreadOnly;
};
/***************************************************************************/