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:
jband%netscape.com 2001-06-05 00:59:53 +00:00
Родитель d29d9460d1
Коммит 8bcfe12fca
6 изменённых файлов: 232 добавлений и 30 удалений

Просмотреть файл

@ -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();
}