From 843961c1edc39f2f894cf84cc5e860f5d1184ff8 Mon Sep 17 00:00:00 2001 From: "kipp%netscape.com" Date: Thu, 30 Sep 1999 21:12:33 +0000 Subject: [PATCH] Fixed shutdown related memory leaks --- js/src/xpconnect/src/nsXPConnect.cpp | 86 ++++-- js/src/xpconnect/src/xpcmodule.cpp | 323 ++++++++++++++++------ js/src/xpconnect/src/xpcprivate.h | 7 +- js/src/xpconnect/src/xpcruntimesvc.cpp | 22 +- js/src/xpconnect/src/xpcthreadcontext.cpp | 19 +- 5 files changed, 340 insertions(+), 117 deletions(-) diff --git a/js/src/xpconnect/src/nsXPConnect.cpp b/js/src/xpconnect/src/nsXPConnect.cpp index e2232c6cff3..076a473b123 100644 --- a/js/src/xpconnect/src/nsXPConnect.cpp +++ b/js/src/xpconnect/src/nsXPConnect.cpp @@ -42,11 +42,40 @@ #define JS_CLASS_MAP_SIZE 256 #define NATIVE_CLASS_MAP_SIZE 256 +#if 0 +extern "C" { + void __log_addref(void* p, int oldrc, int newrc); + void __log_release(void* p, int oldrc, int newrc); +}; + +NS_IMETHODIMP_(nsrefcnt) nsXPConnect::AddRef(void) +{ + NS_PRECONDITION(PRInt32(mRefCnt) >= 0, "illegal refcnt"); + __log_addref(this, mRefCnt, mRefCnt + 1); + ++mRefCnt; + return mRefCnt; +} + +NS_IMETHODIMP_(nsrefcnt) nsXPConnect::Release(void) +{ + NS_PRECONDITION(0 != mRefCnt, "dup release"); + __log_release(this, mRefCnt, mRefCnt - 1); + --mRefCnt; + if (mRefCnt == 0) { + NS_DELETEXPCOM(this); + return 0; + } + return mRefCnt; +} + +NS_IMPL_QUERY_INTERFACE1(nsXPConnect, nsIXPConnect) +#else NS_IMPL_ISUPPORTS1(nsXPConnect, nsIXPConnect) +#endif const char XPC_COMPONENTS_STR[] = "Components"; -nsXPConnect* nsXPConnect::mSelf = nsnull; +nsXPConnect* nsXPConnect::gSelf = nsnull; /***************************************************************************/ @@ -141,21 +170,47 @@ GetPerThreadData() nsXPConnect* nsXPConnect::GetXPConnect() { - // XXX This pattern causes us to retain an extra ref on the singleton. - // XXX Should the singleton nsXPConnect object *ever* be deleted? - if(!mSelf) + if(!gSelf) { - mSelf = new nsXPConnect(); - if(mSelf && (!mSelf->mContextMap || - !mSelf->mArbitraryScriptable || - !mSelf->mInterfaceInfoManager || - !mSelf->mThrower || - !mSelf->mContextStack)) - NS_RELEASE(mSelf); + gSelf = new nsXPConnect(); + if (!gSelf) { + return nsnull; + } + if (!gSelf->mContextMap || + !gSelf->mArbitraryScriptable || + !gSelf->mInterfaceInfoManager || + !gSelf->mThrower || + !gSelf->mContextStack) + { + // ctor failed to create an acceptable instance + delete gSelf; + } + else + { + // Keep the singleton alive + NS_ADDREF(gSelf); + } + } + if(gSelf) + { + NS_ADDREF(gSelf); + } + return gSelf; +} + +void +nsXPConnect::FreeXPConnect() +{ + nsXPConnect* xpc = gSelf; + if (xpc) { + nsrefcnt cnt; + NS_RELEASE2(xpc, cnt); +#ifdef DEBUG_kipp + if (0 != cnt) { + printf("*** dangling reference to nsXPConnect: refcnt=%d\n", cnt); + } +#endif } - if(mSelf) - NS_ADDREF(mSelf); - return mSelf; } // static @@ -260,7 +315,6 @@ nsXPConnect::nsXPConnect() mContextStack(nsnull) { NS_INIT_REFCNT(); - NS_ADDREF_THIS(); nsXPCWrappedNativeClass::OneTimeInit(); mContextMap = JSContext2XPCContextMap::newMap(CONTEXT_MAP_SIZE); @@ -288,7 +342,7 @@ nsXPConnect::~nsXPConnect() delete mThrower; if(mContextStack) nsServiceManager::ReleaseService("nsThreadJSContextStack", mContextStack); - mSelf = nsnull; + gSelf = nsnull; } NS_IMETHODIMP diff --git a/js/src/xpconnect/src/xpcmodule.cpp b/js/src/xpconnect/src/xpcmodule.cpp index 8b26a9d215a..c553afae497 100644 --- a/js/src/xpconnect/src/xpcmodule.cpp +++ b/js/src/xpconnect/src/xpcmodule.cpp @@ -34,12 +34,12 @@ /* Module level methods. */ +#include "nsCOMPtr.h" #include "xpcprivate.h" +#include "nsIModule.h" /***************************************************************************/ -static NS_DEFINE_CID(kComponentManagerCID, NS_COMPONENTMANAGER_CID); -static NS_DEFINE_CID(kGenericFactoryCID, NS_GENERICFACTORY_CID); static NS_DEFINE_CID(kJSID_CID, NS_JS_ID_CID); static NS_DEFINE_CID(kXPCException_CID, NS_XPCEXCEPTION_CID); static NS_DEFINE_CID(kXPConnect_CID, NS_XPCONNECT_CID); @@ -142,113 +142,262 @@ Construct_nsJSRuntimeService(nsISupports *aOuter, REFNSIID aIID, void **aResult) /********************************************/ -extern "C" PR_IMPLEMENT(nsresult) -NSGetFactory(nsISupports* aServMgr, - const nsCID &aClass, - const char *aClassName, - const char *aProgID, - nsIFactory **aFactory) +// Module implementation for the xpconnect library + +class nsXPConnectModule : public nsIModule { - nsresult rv; - NS_ASSERTION(aFactory != nsnull, "bad factory pointer"); +public: + nsXPConnectModule(); + virtual ~nsXPConnectModule(); - NS_WITH_SERVICE1(nsIComponentManager, compMgr, - aServMgr, kComponentManagerCID, &rv); - if (NS_FAILED(rv)) return rv; + NS_DECL_ISUPPORTS - nsIGenericFactory* factory; - rv = compMgr->CreateInstance(kGenericFactoryCID, nsnull, - nsIGenericFactory::GetIID(), - (void**)&factory); - if (NS_FAILED(rv)) return rv; + NS_DECL_NSIMODULE - // add more factories as 'if else's below... +protected: + nsresult Initialize(); - if(aClass.Equals(kJSID_CID)) - rv = factory->SetConstructor(nsJSIDConstructor); - else if(aClass.Equals(kXPConnect_CID)) - rv = factory->SetConstructor(Construct_nsXPConnect); - else if(aClass.Equals(kXPCThreadJSContextStack_CID)) - rv = factory->SetConstructor(Construct_nsXPCThreadJSContextStack); - else if(aClass.Equals(kXPCException_CID)) - rv = factory->SetConstructor(nsXPCExceptionConstructor); - else if(aClass.Equals(kJSRuntime_CID)) - rv = factory->SetConstructor(Construct_nsJSRuntimeService); - else - { - NS_ASSERTION(0, "incorrectly registered"); - rv = NS_ERROR_NO_INTERFACE; + void Shutdown(); + + PRBool mInitialized; + nsCOMPtr mJSIDFactory; + nsCOMPtr mXPConnectFactory; + nsCOMPtr mXPCThreadJSContextStackFactory; + nsCOMPtr mXPCExceptionFactory; + nsCOMPtr mJSRuntimeServiceFactory; +}; + +static NS_DEFINE_IID(kIModuleIID, NS_IMODULE_IID); + +nsXPConnectModule::nsXPConnectModule() + : mInitialized(PR_FALSE) +{ + NS_INIT_ISUPPORTS(); +} + +nsXPConnectModule::~nsXPConnectModule() +{ + Shutdown(); +} + +NS_IMPL_ISUPPORTS(nsXPConnectModule, kIModuleIID) + +// Perform our one-time intialization for this module +nsresult +nsXPConnectModule::Initialize() +{ + if (mInitialized) { + return NS_OK; } - - if (NS_FAILED(rv)) { - NS_RELEASE(factory); - return rv; - } - *aFactory = factory; + mInitialized = PR_TRUE; return NS_OK; } -/***************************************************************************/ - -extern "C" XPC_PUBLIC_API(PRBool) -NSCanUnload(nsISupports* aServMgr) +// Shutdown this module, releasing all of the module resources +void +nsXPConnectModule::Shutdown() { - return PR_FALSE; + // Release the factory objects + mJSIDFactory = nsnull; + mXPConnectFactory = nsnull; + mXPCThreadJSContextStackFactory = nsnull; + mXPCExceptionFactory = nsnull; + mJSRuntimeServiceFactory = nsnull; + + // Release our singletons + nsXPCThreadJSContextStackImpl::FreeSingleton(); + nsJSRuntimeServiceImpl::FreeSingleton(); + nsXPConnect::FreeXPConnect(); } -extern "C" XPC_PUBLIC_API(nsresult) -NSRegisterSelf(nsISupports* aServMgr, const char *aPath) +// Create a factory object for creating instances of aClass. +NS_IMETHODIMP +nsXPConnectModule::GetClassObject(nsIComponentManager *aCompMgr, + const nsCID& aClass, + const nsIID& aIID, + void** r_classObj) { nsresult rv; + + // Defensive programming: Initialize *r_classObj in case of error below + if (!r_classObj) { + return NS_ERROR_INVALID_POINTER; + } + *r_classObj = NULL; + + // Do one-time-only initialization if necessary + if (!mInitialized) { + rv = Initialize(); + if (NS_FAILED(rv)) { + // Initialization failed! yikes! + return rv; + } + } + + // Choose the appropriate factory, based on the desired instance + // class type (aClass). + nsCOMPtr fact; + if (aClass.Equals(kJSID_CID)) { + if (!mJSIDFactory.get()) { + rv = NS_NewGenericFactory(getter_AddRefs(mJSIDFactory), + nsJSIDConstructor); + } + fact = mJSIDFactory; + } else if(aClass.Equals(kXPConnect_CID)) { + if (!mXPConnectFactory.get()) { + rv = NS_NewGenericFactory(getter_AddRefs(mXPConnectFactory), + Construct_nsXPConnect); + } + fact = mXPConnectFactory; + } else if(aClass.Equals(kXPCThreadJSContextStack_CID)) { + if (!mXPCThreadJSContextStackFactory.get()) { + rv = NS_NewGenericFactory(getter_AddRefs(mXPCThreadJSContextStackFactory), + Construct_nsXPCThreadJSContextStack); + } + fact = mXPCThreadJSContextStackFactory; + } else if(aClass.Equals(kXPCException_CID)) { + if (!mXPCExceptionFactory.get()) { + rv = NS_NewGenericFactory(getter_AddRefs(mXPCExceptionFactory), + nsXPCExceptionConstructor); + } + fact = mXPCExceptionFactory; + } else if(aClass.Equals(kJSRuntime_CID)) { + if (!mJSRuntimeServiceFactory.get()) { + rv = NS_NewGenericFactory(getter_AddRefs(mJSRuntimeServiceFactory), + Construct_nsJSRuntimeService); + } + fact = mJSRuntimeServiceFactory; + } + else { + rv = NS_ERROR_FACTORY_NOT_REGISTERED; #ifdef DEBUG - printf("*** Register XPConnect\n"); + char* cs = aClass.ToString(); + printf("+++ nsXPConnectModule: unable to create factory for %s\n", cs); + nsCRT::free(cs); #endif - NS_WITH_SERVICE1(nsIComponentManager, compMgr, - aServMgr, kComponentManagerCID, &rv); - if (NS_FAILED(rv)) return rv; + } - rv = compMgr->RegisterComponent(kJSID_CID, - "nsIJSID","nsID", - aPath, PR_TRUE, PR_TRUE); - if (NS_FAILED(rv)) return rv; + if (fact) { + rv = fact->QueryInterface(aIID, r_classObj); + } - rv = compMgr->RegisterComponent(kXPConnect_CID, - "nsIXPConnect","nsIXPConnect", - aPath, PR_TRUE, PR_TRUE); - if (NS_FAILED(rv)) return rv; - - rv = compMgr->RegisterComponent(kXPCThreadJSContextStack_CID, - "nsThreadJSContextStack","nsThreadJSContextStack", - aPath, PR_TRUE, PR_TRUE); - if (NS_FAILED(rv)) return rv; - - rv = compMgr->RegisterComponent(kXPCException_CID, - "nsXPCException","nsXPCException", - aPath, PR_TRUE, PR_TRUE); - if (NS_FAILED(rv)) return rv; - - rv = compMgr->RegisterComponent(kJSRuntime_CID, - "JS Runtime Service", "nsJSRuntimeService", - aPath, PR_TRUE, PR_TRUE); return rv; } -extern "C" XPC_PUBLIC_API(nsresult) -NSUnregisterSelf(nsISupports* aServMgr, const char *aPath) -{ - nsresult rv; -#ifdef DEBUG - printf("*** Unregister XPConnect\n"); -#endif - NS_WITH_SERVICE1(nsIComponentManager, compMgr, - aServMgr, kComponentManagerCID, &rv); - if (NS_FAILED(rv)) return rv; +//---------------------------------------- - rv = compMgr->UnregisterComponent(kJSID_CID, aPath); - rv = compMgr->UnregisterComponent(kXPConnect_CID, aPath); - rv = compMgr->UnregisterComponent(kXPCThreadJSContextStack_CID, aPath); - rv = compMgr->UnregisterComponent(kXPCException_CID, aPath); - rv = compMgr->UnregisterComponent(kJSRuntime_CID, aPath); +struct Components { + const char* mDescription; + const nsID* mCID; + const char* mProgID; +}; + +// The list of components we register +// XXX These progids look pretty bogus! +static Components gComponents[] = { + { "nsIJSID", &kJSID_CID, + "nsID", }, + { "nsIXPConnect", &kXPConnect_CID, + "nsIXPConnect", }, + { "nsThreadJSContextStack", &kXPCThreadJSContextStack_CID, + "nsThreadJSContextStack", }, + { "nsXPCException", &kXPCException_CID, + "nsXPCException", }, + { "JS Runtime Service", &kJSRuntime_CID, + "nsJSRuntimeService", }, +}; +#define NUM_COMPONENTS (sizeof(gComponents) / sizeof(gComponents[0])) + +NS_IMETHODIMP +nsXPConnectModule::RegisterSelf(nsIComponentManager *aCompMgr, + nsIFileSpec* aPath, + const char* registryLocation, + const char* componentType) +{ + nsresult rv = NS_OK; + +#ifdef DEBUG + printf("*** Registering xpconnect components\n"); +#endif + + Components* cp = gComponents; + Components* end = cp + NUM_COMPONENTS; + while (cp < end) { + rv = aCompMgr->RegisterComponentSpec(*cp->mCID, cp->mDescription, + cp->mProgID, aPath, PR_TRUE, + PR_TRUE); + if (NS_FAILED(rv)) { +#ifdef DEBUG + printf("nsXPConnectModule: unable to register %s component => %x\n", + cp->mDescription, rv); +#endif + break; + } + cp++; + } return rv; } + +NS_IMETHODIMP +nsXPConnectModule::UnregisterSelf(nsIComponentManager* aCompMgr, + nsIFileSpec* aPath, + const char* registryLocation) +{ +#ifdef DEBUG + printf("*** Unregistering xpconnect components\n"); +#endif + Components* cp = gComponents; + Components* end = cp + NUM_COMPONENTS; + while (cp < end) { + nsresult rv = aCompMgr->UnregisterComponentSpec(*cp->mCID, aPath); + if (NS_FAILED(rv)) { +#ifdef DEBUG + printf("nsXPConnectModule: unable to unregister %s component => %x\n", + cp->mDescription, rv); +#endif + } + cp++; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXPConnectModule::CanUnload(nsIComponentManager *aCompMgr, PRBool *okToUnload) +{ + if (!okToUnload) { + return NS_ERROR_INVALID_POINTER; + } + *okToUnload = PR_FALSE; + return NS_ERROR_FAILURE; +} + +//---------------------------------------------------------------------- + +static nsXPConnectModule *gModule = NULL; + +extern "C" NS_EXPORT nsresult NSGetModule(nsIComponentManager *servMgr, + nsIFileSpec* location, + nsIModule** return_cobj) +{ + nsresult rv = NS_OK; + + NS_ASSERTION(return_cobj, "Null argument"); + NS_ASSERTION(gModule == NULL, "nsXPConnectModule: Module already created."); + + // Create an initialize the layout module instance + nsXPConnectModule *m = new nsXPConnectModule(); + if (!m) { + return NS_ERROR_OUT_OF_MEMORY; + } + + // Increase refcnt and store away nsIModule interface to m in return_cobj + rv = m->QueryInterface(NS_GET_IID(nsIModule), (void**)return_cobj); + if (NS_FAILED(rv)) { + delete m; + m = nsnull; + } + gModule = m; // WARNING: Weak Reference + return rv; +} diff --git a/js/src/xpconnect/src/xpcprivate.h b/js/src/xpconnect/src/xpcprivate.h index 49bcf41a667..c888e71dac0 100644 --- a/js/src/xpconnect/src/xpcprivate.h +++ b/js/src/xpconnect/src/xpcprivate.h @@ -160,6 +160,7 @@ class nsXPConnect : public nsIXPConnect // non-interface implementation public: static nsXPConnect* GetXPConnect(); + static void FreeXPConnect(); static nsIInterfaceInfoManager* GetInterfaceInfoManager(nsXPConnect* xpc = nsnull); static XPCContext* GetContext(JSContext* cx, nsXPConnect* xpc = nsnull); static XPCJSThrower* GetJSThrower(nsXPConnect* xpc = nsnull); @@ -176,7 +177,9 @@ private: JSBool doInit = JS_TRUE); private: - static nsXPConnect* mSelf; + // Singleton instance + static nsXPConnect* gSelf; + JSContext2XPCContextMap* mContextMap; nsIXPCScriptable* mArbitraryScriptable; nsIInterfaceInfoManager* mInterfaceInfoManager; @@ -963,6 +966,7 @@ public: NS_DECL_NSIJSCONTEXTSTACK static nsXPCThreadJSContextStackImpl* GetSingleton(); + static void FreeSingleton(); nsXPCThreadJSContextStackImpl(); virtual ~nsXPCThreadJSContextStackImpl(); @@ -1005,6 +1009,7 @@ class nsJSRuntimeServiceImpl : public nsIJSRuntimeService NS_DECL_NSIJSRUNTIMESERVICE static nsJSRuntimeServiceImpl *GetSingleton(); + static void FreeSingleton(); nsJSRuntimeServiceImpl(); virtual ~nsJSRuntimeServiceImpl(); diff --git a/js/src/xpconnect/src/xpcruntimesvc.cpp b/js/src/xpconnect/src/xpcruntimesvc.cpp index 4f1d018364d..95e3685f894 100644 --- a/js/src/xpconnect/src/xpcruntimesvc.cpp +++ b/js/src/xpconnect/src/xpcruntimesvc.cpp @@ -30,7 +30,6 @@ nsJSRuntimeServiceImpl::nsJSRuntimeServiceImpl() : mRuntime(0) { NS_INIT_ISUPPORTS(); - NS_ADDREF_THIS(); } nsJSRuntimeServiceImpl::~nsJSRuntimeServiceImpl() { @@ -43,16 +42,25 @@ nsJSRuntimeServiceImpl::~nsJSRuntimeServiceImpl() { NS_IMPL_ISUPPORTS1(nsJSRuntimeServiceImpl, nsIJSRuntimeService); +static nsJSRuntimeServiceImpl* gJSRuntimeService = nsnull; + nsJSRuntimeServiceImpl* nsJSRuntimeServiceImpl::GetSingleton() { - static nsJSRuntimeServiceImpl *singleton = nsnull; - if (!singleton) { - if ((singleton = new nsJSRuntimeServiceImpl()) != nsnull) - NS_ADDREF(singleton); + if (!gJSRuntimeService) { + gJSRuntimeService = new nsJSRuntimeServiceImpl(); + if (gJSRuntimeService) { + NS_ADDREF(gJSRuntimeService); + } } - NS_IF_ADDREF(singleton); - return singleton; + NS_IF_ADDREF(gJSRuntimeService); + return gJSRuntimeService; +} + +void +nsJSRuntimeServiceImpl::FreeSingleton() +{ + NS_IF_RELEASE(gJSRuntimeService); } const uint32 gGCSize = 4L * 1024L * 1024L; /* pref? */ diff --git a/js/src/xpconnect/src/xpcthreadcontext.cpp b/js/src/xpconnect/src/xpcthreadcontext.cpp index 0fe41e735f3..b488a9ddc8a 100644 --- a/js/src/xpconnect/src/xpcthreadcontext.cpp +++ b/js/src/xpconnect/src/xpcthreadcontext.cpp @@ -100,18 +100,25 @@ nsXPCThreadJSContextStackImpl::~nsXPCThreadJSContextStackImpl() {} NS_IMPL_ISUPPORTS1(nsXPCThreadJSContextStackImpl, nsIJSContextStack) +static nsXPCThreadJSContextStackImpl* gXPCThreadJSContextStack = nsnull; + //static nsXPCThreadJSContextStackImpl* nsXPCThreadJSContextStackImpl::GetSingleton() { - static nsXPCThreadJSContextStackImpl* singleton = nsnull; - if(!singleton) + if(!gXPCThreadJSContextStack) { - if(nsnull != (singleton = new nsXPCThreadJSContextStackImpl())) - NS_ADDREF(singleton); + if(nsnull != (gXPCThreadJSContextStack = new nsXPCThreadJSContextStackImpl())) + NS_ADDREF(gXPCThreadJSContextStack); } - NS_IF_ADDREF(singleton); - return singleton; + NS_IF_ADDREF(gXPCThreadJSContextStack); + return gXPCThreadJSContextStack; +} + +void +nsXPCThreadJSContextStackImpl::FreeSingleton() +{ + NS_IF_RELEASE(gXPCThreadJSContextStack); } /* readonly attribute PRInt32 Count; */