зеркало из https://github.com/mozilla/pjs.git
fix bug 84020 - don't override the context global, bug 83367 - add deferred Release of natives of wrappednatives to avoid nesting into js_AllocGCThing during JS gc, and bug 82274 - fix a little leak (patch from dbradley). sr=jst r=dbradley a=drivers
This commit is contained in:
Родитель
d29d9460d1
Коммит
8bcfe12fca
|
@ -564,6 +564,9 @@ interface nsIXPConnect : nsISupports
|
|||
getWrappedNativePrototype(in JSContextPtr aJSContext,
|
||||
in JSObjectPtr aScope,
|
||||
in nsIClassInfo aClassInfo);
|
||||
|
||||
attribute PRBool collectGarbageOnMainThreadOnly;
|
||||
attribute PRBool deferReleasesUntilAfterGarbageCollection;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -877,6 +877,8 @@ main(int argc, char **argv)
|
|||
NS_STATIC_CAST(nsIXPCSecurityManager*, new FullTrustSecMan());
|
||||
xpc->SetSecurityManagerForJSContext(jscontext, secman, 0);
|
||||
|
||||
// xpc->SetCollectGarbageOnMainThreadOnly(PR_TRUE);
|
||||
// xpc->SetDeferReleasesUntilAfterGarbageCollection(PR_TRUE);
|
||||
|
||||
#ifdef TEST_TranslateThis
|
||||
nsCOMPtr<nsIXPCFunctionThisTranslator>
|
||||
|
|
|
@ -41,7 +41,8 @@
|
|||
NS_IMPL_THREADSAFE_ISUPPORTS2(nsXPConnect,nsIXPConnect,nsISupportsWeakReference)
|
||||
|
||||
nsXPConnect* nsXPConnect::gSelf = nsnull;
|
||||
JSBool nsXPConnect::gOnceAliveNowDead = JS_FALSE;
|
||||
JSBool nsXPConnect::gOnceAliveNowDead = JS_FALSE;
|
||||
PRThread* nsXPConnect::gMainThread = nsnull;
|
||||
|
||||
const char XPC_CONTEXT_STACK_CONTRACTID[] = "@mozilla.org/js/xpc/ContextStack;1";
|
||||
const char XPC_RUNTIME_CONTRACTID[] = "@mozilla.org/js/xpc/RuntimeService;1";
|
||||
|
@ -314,6 +315,19 @@ nsXPConnect::CreateRuntime()
|
|||
return nsnull != mRuntime;
|
||||
}
|
||||
|
||||
// static
|
||||
PRThread*
|
||||
nsXPConnect::FindMainThread()
|
||||
{
|
||||
nsCOMPtr<nsIThread> t;
|
||||
nsresult rv;
|
||||
rv = nsIThread::GetMainThread(getter_AddRefs(t));
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv) && t, "bad");
|
||||
rv = t->GetPRThread(&gMainThread);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv) && gMainThread, "bad");
|
||||
return gMainThread;
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
/***************************************************************************/
|
||||
// nsIXPConnect interface methods...
|
||||
|
@ -412,7 +426,10 @@ nsXPConnect::InitClassesWithNewWrappedGlobal(JSContext * aJSContext,
|
|||
// voodoo to fixup scoping and parenting...
|
||||
|
||||
JS_SetParent(aJSContext, globalJSObj, nsnull);
|
||||
JS_SetGlobalObject(aJSContext, globalJSObj);
|
||||
|
||||
JSObject* oldGlobal = JS_GetGlobalObject(aJSContext);
|
||||
if(!oldGlobal || oldGlobal == tempGlobal)
|
||||
JS_SetGlobalObject(aJSContext, globalJSObj);
|
||||
|
||||
if(aCallJS_InitStandardClasses &&
|
||||
!JS_InitStandardClasses(aJSContext, globalJSObj))
|
||||
|
@ -667,12 +684,10 @@ NS_IMETHODIMP
|
|||
nsXPConnect::SetDefaultSecurityManager(nsIXPCSecurityManager *aManager,
|
||||
PRUint16 flags)
|
||||
{
|
||||
#if 1
|
||||
NS_IF_ADDREF(aManager);
|
||||
NS_IF_RELEASE(mDefaultSecurityManager);
|
||||
mDefaultSecurityManager = aManager;
|
||||
mDefaultSecurityManagerFlags = flags;
|
||||
#endif
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -900,6 +915,52 @@ nsXPConnect::GetWrappedNativePrototype(JSContext * aJSContext,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
/* attribute PRBool collectGarbageOnMainThreadOnly; */
|
||||
NS_IMETHODIMP
|
||||
nsXPConnect::GetCollectGarbageOnMainThreadOnly(PRBool *aCollectGarbageOnMainThreadOnly)
|
||||
{
|
||||
XPCJSRuntime* rt = GetRuntime();
|
||||
if(!rt)
|
||||
return UnexpectedFailure(NS_ERROR_FAILURE);
|
||||
|
||||
*aCollectGarbageOnMainThreadOnly = rt->GetMainThreadOnlyGC();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXPConnect::SetCollectGarbageOnMainThreadOnly(PRBool aCollectGarbageOnMainThreadOnly)
|
||||
{
|
||||
XPCJSRuntime* rt = GetRuntime();
|
||||
if(!rt)
|
||||
return UnexpectedFailure(NS_ERROR_FAILURE);
|
||||
|
||||
rt->SetMainThreadOnlyGC(aCollectGarbageOnMainThreadOnly);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* attribute PRBool deferReleasesUntilAfterGarbageCollection; */
|
||||
NS_IMETHODIMP
|
||||
nsXPConnect::GetDeferReleasesUntilAfterGarbageCollection(PRBool *aDeferReleasesUntilAfterGarbageCollection)
|
||||
{
|
||||
XPCJSRuntime* rt = GetRuntime();
|
||||
if(!rt)
|
||||
return UnexpectedFailure(NS_ERROR_FAILURE);
|
||||
|
||||
*aDeferReleasesUntilAfterGarbageCollection = rt->GetDeferReleases();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXPConnect::SetDeferReleasesUntilAfterGarbageCollection(PRBool aDeferReleasesUntilAfterGarbageCollection)
|
||||
{
|
||||
XPCJSRuntime* rt = GetRuntime();
|
||||
if(!rt)
|
||||
return UnexpectedFailure(NS_ERROR_FAILURE);
|
||||
|
||||
rt->SetDeferReleases(aDeferReleasesUntilAfterGarbageCollection);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* void debugDump (in short depth); */
|
||||
NS_IMETHODIMP
|
||||
nsXPConnect::DebugDump(PRInt16 depth)
|
||||
|
|
|
@ -204,22 +204,32 @@ DyingProtoKiller(JSDHashTable *table, JSDHashEntryHdr *hdr,
|
|||
// static
|
||||
JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status)
|
||||
{
|
||||
nsVoidArray* dyingWrappedJSArray;
|
||||
|
||||
XPCJSRuntime* self = nsXPConnect::GetRuntime();
|
||||
if(self)
|
||||
{
|
||||
nsVoidArray* dyingWrappedJSArray = &self->mWrappedJSToReleaseArray;
|
||||
|
||||
switch(status)
|
||||
{
|
||||
case JSGC_BEGIN:
|
||||
{
|
||||
// do nothing (yet)...
|
||||
if(self->GetMainThreadOnlyGC() &&
|
||||
PR_GetCurrentThread() != nsXPConnect::GetMainThread())
|
||||
{
|
||||
return JS_FALSE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case JSGC_MARK_END:
|
||||
{
|
||||
NS_ASSERTION(!self->mDoingFinalization, "bad state");
|
||||
|
||||
dyingWrappedJSArray = &self->mWrappedJSToReleaseArray;
|
||||
{
|
||||
XPCAutoLock lock(self->GetMapLock()); // lock the wrapper map
|
||||
XPCLock* lock = self->GetMainThreadOnlyGC() ?
|
||||
nsnull : self->GetMapLock();
|
||||
|
||||
XPCAutoLock al(lock); // lock the wrapper map if necessary
|
||||
JSDyingJSObjectData data = {cx, dyingWrappedJSArray};
|
||||
|
||||
// Add any wrappers whose JSObjects are to be finalized to
|
||||
|
@ -241,10 +251,13 @@ JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status)
|
|||
// 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;
|
||||
|
||||
#ifdef XPC_REPORT_NATIVE_INTERFACE_AND_SET_FLUSHING
|
||||
printf("--------------------------------------------------------------\n");
|
||||
|
@ -422,19 +435,56 @@ JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status)
|
|||
// Release all the members whose JSObjects are now known
|
||||
// to be dead.
|
||||
|
||||
// XXX We ought to enter and exit a lock and pick these
|
||||
// elements off one at a time!
|
||||
|
||||
for(PRInt32 i = dyingWrappedJSArray->Count() - 1; i >= 0; i--)
|
||||
dyingWrappedJSArray = &self->mWrappedJSToReleaseArray;
|
||||
XPCLock* lock = self->GetMainThreadOnlyGC() ?
|
||||
nsnull : self->GetMapLock();
|
||||
while(1)
|
||||
{
|
||||
nsXPCWrappedJS* wrapper =
|
||||
NS_REINTERPRET_CAST(nsXPCWrappedJS*,
|
||||
dyingWrappedJSArray->ElementAt(i));
|
||||
|
||||
nsXPCWrappedJS* wrapper;
|
||||
{
|
||||
XPCAutoLock al(lock); // lock if necessary
|
||||
PRInt32 count = dyingWrappedJSArray->Count();
|
||||
if(!count)
|
||||
{
|
||||
dyingWrappedJSArray->Compact();
|
||||
break;
|
||||
}
|
||||
wrapper = NS_REINTERPRET_CAST(nsXPCWrappedJS*,
|
||||
dyingWrappedJSArray->ElementAt(count-1));
|
||||
dyingWrappedJSArray->RemoveElementAt(count-1);
|
||||
}
|
||||
NS_RELEASE(wrapper);
|
||||
}
|
||||
dyingWrappedJSArray->Clear();
|
||||
|
||||
// Do any deferred released of native objects.
|
||||
if(self->GetDeferReleases())
|
||||
{
|
||||
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;
|
||||
{
|
||||
XPCAutoLock al(lock); // lock if necessary
|
||||
PRInt32 count = array->Count();
|
||||
if(!count)
|
||||
{
|
||||
array->Compact();
|
||||
break;
|
||||
}
|
||||
obj = NS_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:
|
||||
|
@ -572,6 +622,16 @@ XPCJSRuntime::~XPCJSRuntime()
|
|||
delete mNativeScriptableSharedMap;
|
||||
}
|
||||
|
||||
if(mDyingWrappedNativeProtoMap)
|
||||
{
|
||||
#ifdef XPC_DUMP_AT_SHUTDOWN
|
||||
uint32 count = mDyingWrappedNativeProtoMap->Count();
|
||||
if(count)
|
||||
printf("deleting XPCJSRuntime with %d live but dying XPCWrappedNativeProto\n", (int)count);
|
||||
#endif
|
||||
delete mDyingWrappedNativeProtoMap;
|
||||
}
|
||||
|
||||
// unwire the readable/JSString sharing magic
|
||||
XPCStringConvert::ShutdownDOMStringFinalizer();
|
||||
}
|
||||
|
@ -591,9 +651,12 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect,
|
|||
mNativeScriptableSharedMap(XPCNativeScriptableSharedMap::newMap(XPC_NATIVE_JSCLASS_MAP_SIZE)),
|
||||
mDyingWrappedNativeProtoMap(XPCWrappedNativeProtoMap::newMap(XPC_DYING_NATIVE_PROTO_MAP_SIZE)),
|
||||
mMapLock(XPCAutoLock::NewLock("XPCJSRuntime::mMapLock")),
|
||||
mWrappedJSToReleaseArray()
|
||||
mWrappedJSToReleaseArray(),
|
||||
mNativesToReleaseArray(),
|
||||
mMainThreadOnlyGC(JS_FALSE),
|
||||
mDeferReleases(JS_FALSE),
|
||||
mDoingFinalization(JS_FALSE)
|
||||
{
|
||||
|
||||
#ifdef XPC_CHECK_WRAPPERS_AT_SHUTDOWN
|
||||
DEBUG_WrappedNativeHashtable =
|
||||
JS_NewDHashTable(JS_DHashGetStubOps(), nsnull,
|
||||
|
@ -773,6 +836,19 @@ XPCJSRuntime::GenerateStringIDs(JSContext* cx)
|
|||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSBool
|
||||
XPCJSRuntime::DeferredRelease(nsISupports* obj)
|
||||
{
|
||||
NS_ASSERTION(obj, "bad param");
|
||||
NS_ASSERTION(GetDeferReleases(), "bad call");
|
||||
|
||||
XPCLock* lock = GetMainThreadOnlyGC() ? nsnull : GetMapLock();
|
||||
{
|
||||
XPCAutoLock al(lock); // lock if necessary
|
||||
return mNativesToReleaseArray.AppendElement(obj);
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
#ifdef DEBUG
|
||||
|
|
|
@ -125,6 +125,7 @@
|
|||
#define XPC_TRACK_WRAPPER_STATS
|
||||
#define XPC_TRACK_SCOPE_STATS
|
||||
#define XPC_TRACK_PROTO_STATS
|
||||
#define XPC_TRACK_DEFERRED_RELEASES
|
||||
#define XPC_CHECK_WRAPPERS_AT_SHUTDOWN
|
||||
#define XPC_REPORT_SHADOWED_WRAPPED_NATIVE_MEMBERS
|
||||
#define XPC_CHECK_CLASSINFO_CLAIMS
|
||||
|
@ -380,6 +381,9 @@ public:
|
|||
|
||||
static JSBool IsISupportsDescendant(nsIInterfaceInfo* info);
|
||||
|
||||
static PRThread* GetMainThread()
|
||||
{return gMainThread ? gMainThread : FindMainThread();}
|
||||
|
||||
nsIXPCSecurityManager* GetDefaultSecurityManager() const
|
||||
{return mDefaultSecurityManager;}
|
||||
|
||||
|
@ -405,10 +409,13 @@ private:
|
|||
JSBool EnsureRuntime() {return mRuntime ? JS_TRUE : CreateRuntime();}
|
||||
JSBool CreateRuntime();
|
||||
|
||||
static PRThread* FindMainThread();
|
||||
|
||||
private:
|
||||
// Singleton instance
|
||||
static nsXPConnect* gSelf;
|
||||
static JSBool gOnceAliveNowDead;
|
||||
static PRThread* gMainThread;
|
||||
|
||||
XPCJSRuntime* mRuntime;
|
||||
nsIInterfaceInfoManager* mInterfaceInfoManager;
|
||||
|
@ -467,6 +474,20 @@ public:
|
|||
XPCContext* GetXPCContext(JSContext* cx);
|
||||
XPCContext* SyncXPCContextList(JSContext* cx = nsnull);
|
||||
|
||||
JSBool GetMainThreadOnlyGC() const {return mMainThreadOnlyGC;}
|
||||
void SetMainThreadOnlyGC(JSBool b) {mMainThreadOnlyGC = b;}
|
||||
|
||||
JSBool GetDeferReleases() const {return mDeferReleases;}
|
||||
void SetDeferReleases(JSBool b)
|
||||
{/* If deferring is turned off while any are pending they'll leak! */
|
||||
NS_ASSERTION((mDeferReleases && b) ||
|
||||
!mNativesToReleaseArray.Count(), "bad");
|
||||
mDeferReleases = b;}
|
||||
|
||||
JSBool DeferredRelease(nsISupports* obj);
|
||||
|
||||
JSBool GetDoingFinalization() const {return mDoingFinalization;}
|
||||
|
||||
// Mapping of often used strings to jsid atoms that live 'forever'.
|
||||
//
|
||||
// To add a new string: add to this list and to XPCJSRuntime::mStrings
|
||||
|
@ -554,6 +575,10 @@ private:
|
|||
XPCWrappedNativeProtoMap* mDyingWrappedNativeProtoMap;
|
||||
XPCLock* mMapLock;
|
||||
nsVoidArray mWrappedJSToReleaseArray;
|
||||
nsVoidArray mNativesToReleaseArray;
|
||||
JSBool mMainThreadOnlyGC;
|
||||
JSBool mDeferReleases;
|
||||
JSBool mDoingFinalization;
|
||||
};
|
||||
|
||||
/***************************************************************************/
|
||||
|
|
|
@ -492,10 +492,25 @@ XPCWrappedNative::~XPCWrappedNative()
|
|||
map->Remove(this);
|
||||
}
|
||||
|
||||
NS_IF_RELEASE(mIdentity);
|
||||
if(mIdentity)
|
||||
{
|
||||
XPCJSRuntime* rt = GetRuntime();
|
||||
if(rt && rt->GetDeferReleases() && rt->GetDoingFinalization())
|
||||
{
|
||||
if(!rt->DeferredRelease(mIdentity))
|
||||
{
|
||||
NS_WARNING("Failed to append object for deferred release.");
|
||||
// XXX do we really want to do this???
|
||||
NS_RELEASE(mIdentity);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NS_RELEASE(mIdentity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// This is factored out so that it can be called publicly
|
||||
// static
|
||||
nsresult
|
||||
|
@ -695,14 +710,8 @@ XPCWrappedNative::Init(XPCCallContext& ccx, JSObject* parent,
|
|||
|
||||
#ifdef XPC_CHECK_WRAPPER_THREADSAFETY
|
||||
if(!gMainThread)
|
||||
{
|
||||
nsCOMPtr<nsIThread> t;
|
||||
nsresult rv;
|
||||
rv = nsIThread::GetMainThread(getter_AddRefs(t));
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv) && t, "bad");
|
||||
rv = t->GetPRThread(&gMainThread);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv) && gMainThread, "bad");
|
||||
}
|
||||
gMainThread = nsXPConnect::GetMainThread();
|
||||
|
||||
mThread = PR_GetCurrentThread();
|
||||
|
||||
if(HasProto() && GetProto()->ClassIsMainThreadOnly() && gMainThread != mThread)
|
||||
|
@ -839,7 +848,25 @@ XPCWrappedNative::FlatJSObjectFinalized(JSContext *cx, JSObject *obj)
|
|||
nsISupports* obj = to->GetNative();
|
||||
if(obj)
|
||||
{
|
||||
obj->Release();
|
||||
#ifdef XP_WIN
|
||||
// Try to detect free'd pointer
|
||||
NS_ASSERTION(*(int*)obj != 0xdddddddd, "bad pointer!");
|
||||
NS_ASSERTION(*(int*)obj != 0, "bad pointer!");
|
||||
#endif
|
||||
XPCJSRuntime* rt = GetRuntime();
|
||||
if(rt && rt->GetDeferReleases())
|
||||
{
|
||||
if(!rt->DeferredRelease(obj))
|
||||
{
|
||||
NS_WARNING("Failed to append object for deferred release.");
|
||||
// XXX do we really want to do this???
|
||||
obj->Release();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
obj->Release();
|
||||
}
|
||||
to->SetNative(nsnull);
|
||||
}
|
||||
|
||||
|
@ -849,6 +876,14 @@ XPCWrappedNative::FlatJSObjectFinalized(JSContext *cx, JSObject *obj)
|
|||
|
||||
//This makes IsValid return false from now on...
|
||||
mFlatJSObject = nsnull;
|
||||
|
||||
NS_ASSERTION(mIdentity, "bad pointer!");
|
||||
#ifdef XP_WIN
|
||||
// Try to detect free'd pointer
|
||||
NS_ASSERTION(*(int*)mIdentity != 0xdddddddd, "bad pointer!");
|
||||
NS_ASSERTION(*(int*)mIdentity != 0, "bad pointer!");
|
||||
#endif
|
||||
|
||||
Release();
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче