Bug 326491 - cleanup observers on shutdown even if the observer service is leaked, r=darin

This commit is contained in:
benjamin%smedbergs.us 2006-03-04 14:04:05 +00:00
Родитель eac373d533
Коммит 05825942c6
10 изменённых файлов: 251 добавлений и 245 удалений

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

@ -93,7 +93,6 @@
#include "nsIPrefBranch.h"
#include "nsIPrefBranch2.h"
#include "nsIWritablePropertyBag2.h"
#include "nsObserverService.h"
// we want to explore making the document own the load group
// so we can associate the document URI with the load group.

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

@ -84,6 +84,13 @@
*/
#define NS_ARRAY_CONTRACTID "@mozilla.org/array;1"
/**
* Observer Service ContractID
* The observer service implements the global nsIObserverService object.
* It should be used from the main thread only.
*/
#define NS_OBSERVERSERVICE_CONTRACTID "@mozilla.org/observer-service;1"
/**
* The following are the CIDs and Contract IDs of the nsISupports wrappers for
* primative types.

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

@ -681,8 +681,9 @@ NS_ShutdownXPCOM(nsIServiceManager* servMgr)
nsCOMPtr <nsIEventQueue> currentQ;
NS_GetCurrentEventQ(getter_AddRefs(currentQ), eqs);
nsCOMPtr<nsIObserverService> observerService =
do_GetService("@mozilla.org/observer-service;1");
nsRefPtr<nsObserverService> observerService;
CallGetService("@mozilla.org/observer-service;1",
(nsObserverService**) getter_AddRefs(observerService));
if (observerService)
{
@ -721,10 +722,13 @@ NS_ShutdownXPCOM(nsIServiceManager* servMgr)
// We save the "xpcom-shutdown-loaders" observers to notify after
// the observerservice is gone.
if (observerService)
if (observerService) {
observerService->
EnumerateObservers(NS_XPCOM_SHUTDOWN_LOADERS_OBSERVER_ID,
getter_AddRefs(moduleLoaders));
observerService->Shutdown();
}
}
// XPCOM is officially in shutdown mode NOW

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

@ -99,7 +99,6 @@ EXPORTS = \
nsIByteBuffer.h \
nsIUnicharBuffer.h \
nsInt64.h \
nsObserverService.h \
nsRecyclingAllocator.h \
nsStaticNameTable.h \
nsStaticAtom.h \

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

@ -37,53 +37,29 @@
#include "nsObserverList.h"
#include "pratom.h"
#include "nsAutoLock.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsIObserver.h"
#include "nsCOMArray.h"
#include "nsISimpleEnumerator.h"
#include "nsIWeakReference.h"
nsObserverList::nsObserverList(nsresult &rv)
{
MOZ_COUNT_CTOR(nsObserverList);
mLock = PR_NewLock();
if (!mLock)
rv = NS_ERROR_OUT_OF_MEMORY;
}
nsObserverList::~nsObserverList(void)
{
MOZ_COUNT_DTOR(nsObserverList);
if (mLock)
PR_DestroyLock(mLock);
}
nsresult
nsObserverList::AddObserver(nsIObserver* anObserver, PRBool ownsWeak)
{
NS_ENSURE_ARG(anObserver);
NS_ASSERTION(anObserver, "Null input");
nsAutoLock lock(mLock);
if (!ownsWeak) {
ObserverRef* o = mObservers.AppendElement(anObserver);
if (!o)
return NS_ERROR_OUT_OF_MEMORY;
nsCOMPtr<nsISupports> observerRef;
if (ownsWeak) {
nsCOMPtr<nsISupportsWeakReference>
weakRefFactory(do_QueryInterface(anObserver));
NS_ASSERTION(weakRefFactory,
"Doesn't implement nsISupportsWeakReference");
if (weakRefFactory)
weakRefFactory->
GetWeakReference((nsIWeakReference**)(nsISupports**)
getter_AddRefs(observerRef));
} else {
observerRef = anObserver;
return NS_OK;
}
if (!observerRef)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIWeakReference> weak = do_GetWeakReference(anObserver);
if (!weak)
return NS_NOINTERFACE;
if (!mObservers.AppendObject(observerRef))
ObserverRef *o = mObservers.AppendElement(weak);
if (!o)
return NS_ERROR_OUT_OF_MEMORY;
return NS_OK;
@ -92,51 +68,25 @@ nsObserverList::AddObserver(nsIObserver* anObserver, PRBool ownsWeak)
nsresult
nsObserverList::RemoveObserver(nsIObserver* anObserver)
{
NS_ENSURE_ARG(anObserver);
NS_ASSERTION(anObserver, "Null input");
nsAutoLock lock(mLock);
if (mObservers.RemoveObject(anObserver))
if (mObservers.RemoveElement(NS_STATIC_CAST(nsISupports*, anObserver)))
return NS_OK;
nsCOMPtr<nsISupportsWeakReference>
weakRefFactory(do_QueryInterface(anObserver));
if (!weakRefFactory)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIWeakReference> observerRef;
weakRefFactory->GetWeakReference(getter_AddRefs(observerRef));
nsCOMPtr<nsIWeakReference> observerRef = do_GetWeakReference(anObserver);
if (!observerRef)
return NS_ERROR_FAILURE;
if (!mObservers.RemoveObject(observerRef))
if (!mObservers.RemoveElement(observerRef))
return NS_ERROR_FAILURE;
return NS_OK;
}
class nsObserverEnumerator : public nsISimpleEnumerator
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISIMPLEENUMERATOR
nsObserverEnumerator(nsCOMArray<nsISupports> &aObservers);
private:
~nsObserverEnumerator() { }
PRUint32 mIndex; // Counts down, ends at 0
nsCOMArray<nsISupports> mObservers;
};
nsresult
nsObserverList::GetObserverList(nsISimpleEnumerator** anEnumerator)
{
nsAutoLock lock(mLock);
nsRefPtr<nsObserverEnumerator> e(new nsObserverEnumerator(mObservers));
nsRefPtr<nsObserverEnumerator> e(new nsObserverEnumerator(this));
if (!e)
return NS_ERROR_OUT_OF_MEMORY;
@ -144,41 +94,82 @@ nsObserverList::GetObserverList(nsISimpleEnumerator** anEnumerator)
return NS_OK;
}
nsObserverEnumerator::nsObserverEnumerator(nsCOMArray<nsISupports> &aObservers)
void
nsObserverList::EnumerateObservers(EnumObserversFunc aFunc, void *aClosure)
{
for (PRInt32 i = 0; i < aObservers.Count(); ++i) {
nsCOMPtr<nsIWeakReference> weak(do_QueryInterface(aObservers[i]));
if (weak) {
nsCOMPtr<nsISupports> strong(do_QueryReferent(weak));
if (strong)
mObservers.AppendObject(strong);
for (PRInt32 i = mObservers.Length() - 1; i >= 0; --i) {
if (mObservers[i].isWeakRef) {
nsCOMPtr<nsIObserver> o(do_QueryReferent(mObservers[i].asWeak()));
if (o) {
aFunc(o, aClosure);
}
else {
// the object has gone away, remove the weakref
mObservers.RemoveElementAt(i);
}
}
else {
mObservers.AppendObject(aObservers[i]);
aFunc(mObservers[i].asObserver(), aClosure);
}
}
}
mIndex = mObservers.Count();
struct NotifyData
{
nsISupports* aSubject;
const char *aTopic;
const PRUnichar *someData;
};
void
nsObserverList::NotifyObservers(nsISupports *aSubject,
const char *aTopic,
const PRUnichar *someData)
{
NotifyData nd = { aSubject, aTopic, someData };
EnumerateObservers(Notify, &nd);
}
void
nsObserverList::Notify(nsIObserver* aObserver, void *aClosure)
{
NotifyData *nd = NS_REINTERPRET_CAST(NotifyData*, aClosure);
aObserver->Observe(nd->aSubject, nd->aTopic, nd->someData);
}
NS_IMPL_ISUPPORTS1(nsObserverEnumerator, nsISimpleEnumerator)
void
nsObserverEnumerator::Fill(nsIObserver* aObserver, void *aClosure)
{
nsObserverEnumerator* e =
NS_REINTERPRET_CAST(nsObserverEnumerator*, aClosure);
e->mObservers.AppendObject(aObserver);
}
nsObserverEnumerator::nsObserverEnumerator(nsObserverList* aObserverList)
: mIndex(0)
{
mObservers.SetCapacity(aObserverList->mObservers.Length());
aObserverList->EnumerateObservers(Fill, this);
}
NS_IMETHODIMP
nsObserverEnumerator::HasMoreElements(PRBool *aResult)
{
*aResult = (mIndex > 0);
*aResult = (mIndex < mObservers.Count());
return NS_OK;
}
NS_IMETHODIMP
nsObserverEnumerator::GetNext(nsISupports* *aResult)
{
if (!mIndex) {
if (mIndex == mObservers.Count()) {
NS_ERROR("Enumerating after HasMoreElements returned false.");
return NS_ERROR_UNEXPECTED;
}
--mIndex;
NS_ADDREF(*aResult = mObservers[mIndex]);
++mIndex;
return NS_OK;
}

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

@ -39,25 +39,88 @@
#define nsObserverList_h___
#include "nsISupports.h"
#include "nsTArray.h"
#include "nsCOMPtr.h"
#include "nsCOMArray.h"
#include "nsIObserver.h"
#include "nsIWeakReference.h"
#include "nsHashKeys.h"
#include "nsISimpleEnumerator.h"
class nsISimpleEnumerator;
class nsIObserver;
struct ObserverRef
{
ObserverRef(const ObserverRef& o) :
isWeakRef(o.isWeakRef), ref(o.ref) { }
ObserverRef(nsIObserver* aObserver) : isWeakRef(PR_FALSE), ref(aObserver) { }
ObserverRef(nsIWeakReference* aWeak) : isWeakRef(PR_TRUE), ref(aWeak) { }
class nsObserverList
PRBool isWeakRef;
nsCOMPtr<nsISupports> ref;
nsIObserver* asObserver() {
NS_ASSERTION(!isWeakRef, "Isn't a strong ref.");
return NS_STATIC_CAST(nsIObserver*, (nsISupports*) ref);
}
nsIWeakReference* asWeak() {
NS_ASSERTION(isWeakRef, "Isn't a weak ref.");
return NS_STATIC_CAST(nsIWeakReference*, (nsISupports*) ref);
}
PRBool operator==(nsISupports* b) const { return ref == b; }
};
class nsObserverList : public nsCharPtrHashKey
{
public:
nsObserverList(nsresult &rv);
~nsObserverList();
nsObserverList(const char *key) : nsCharPtrHashKey(key)
{ MOZ_COUNT_CTOR(nsObserverList); }
~nsObserverList() { MOZ_COUNT_DTOR(nsObserverList); }
nsresult AddObserver(nsIObserver* anObserver, PRBool ownsWeak);
nsresult RemoveObserver(nsIObserver* anObserver);
void NotifyObservers(nsISupports *aSubject,
const char *aTopic,
const PRUnichar *someData);
nsresult GetObserverList(nsISimpleEnumerator** anEnumerator);
protected:
PRLock* mLock;
nsCOMArray<nsISupports> mObservers;
private:
friend class nsObserverEnumerator;
typedef void (*EnumObserversFunc)(nsIObserver* aObserver, void *aClosure);
// EnumerateObservers passes all the registered observers to aFunc
// in LIFO order. If there are any stale weak references they are
// removed during enumeration. aFunc must not modify the observerlist
// during enumeration.
void EnumerateObservers(EnumObserversFunc aFunc, void *aClosure);
// Static helper function for NotifyObservers
static void Notify(nsIObserver* aObserver, void *aClosure);
nsTArray<ObserverRef> mObservers;
};
class nsObserverEnumerator : public nsISimpleEnumerator
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISIMPLEENUMERATOR
nsObserverEnumerator(nsObserverList* aObserverList);
private:
~nsObserverEnumerator() { }
// Static helper function for the constructor
static void Fill(nsIObserver* aObserver, void *aClosure);
PRInt32 mIndex; // Counts up from 0
nsCOMArray<nsIObserver> mObservers;
};
#endif /* nsObserverList_h___ */

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

@ -37,7 +37,7 @@
* ***** END LICENSE BLOCK ***** */
#include "prlog.h"
#include "prlock.h"
#include "nsAutoPtr.h"
#include "nsIFactory.h"
#include "nsIServiceManager.h"
#include "nsIComponentManager.h"
@ -48,6 +48,8 @@
#include "nsObserverList.h"
#include "nsHashtable.h"
#include "nsIWeakReference.h"
#include "nsIThread.h"
#include "nsIEnumerator.h"
#define NOTIFY_GLOBAL_OBSERVERS
@ -68,17 +70,26 @@ PRLogModuleInfo* observerServiceLog = nsnull;
// nsObserverService Implementation
NS_IMPL_THREADSAFE_ISUPPORTS1(nsObserverService, nsIObserverService)
NS_IMPL_THREADSAFE_ISUPPORTS2(nsObserverService, nsIObserverService, nsObserverService)
nsObserverService::nsObserverService()
: mObserverTopicTable(nsnull)
nsObserverService::nsObserverService() :
mShuttingDown(PR_FALSE)
{
mObserverTopicTable.Init();
}
nsObserverService::~nsObserverService(void)
{
if(mObserverTopicTable)
delete mObserverTopicTable;
Shutdown();
}
void
nsObserverService::Shutdown()
{
mShuttingDown = PR_TRUE;
if (mObserverTopicTable.IsInitialized())
mObserverTopicTable.Clear();
}
NS_METHOD
@ -89,158 +100,85 @@ nsObserverService::Create(nsISupports* outer, const nsIID& aIID, void* *aInstanc
observerServiceLog = PR_NewLogModule("ObserverService");
#endif
nsresult rv;
nsObserverService* os = new nsObserverService();
if (os == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(os);
rv = os->QueryInterface(aIID, aInstancePtr);
NS_RELEASE(os);
return rv;
}
nsRefPtr<nsObserverService> os = new nsObserverService();
static PRBool PR_CALLBACK
ReleaseObserverList(nsHashKey *aKey, void *aData, void* closure)
{
nsObserverList* observerList = NS_STATIC_CAST(nsObserverList*, aData);
delete(observerList);
return PR_TRUE;
}
nsresult nsObserverService::GetObserverList(const char* aTopic, nsObserverList** anObserverList)
{
if (anObserverList == nsnull)
return NS_ERROR_NULL_POINTER;
if(mObserverTopicTable == nsnull)
{
mObserverTopicTable = new nsObjectHashtable(nsnull,
nsnull, // should never be cloned
ReleaseObserverList,
nsnull,
256,
PR_TRUE);
if (mObserverTopicTable == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
}
nsCStringKey key(aTopic);
nsObserverList *topicObservers;
topicObservers = (nsObserverList *) mObserverTopicTable->Get(&key);
if (topicObservers)
{
*anObserverList = topicObservers;
return NS_OK;
}
nsresult rv = NS_OK;
topicObservers = new nsObserverList(rv);
if (!topicObservers)
// The cast is required for MSVC6, otherwise it complains about calling
// a private function.
if (!os || !((nsObserverService*) os)->mObserverTopicTable.IsInitialized())
return NS_ERROR_OUT_OF_MEMORY;
if (NS_FAILED(rv)) {
delete topicObservers;
return rv;
return os->QueryInterface(aIID, aInstancePtr);
}
#define NS_ENSURE_VALIDCALL \
if (!nsIThread::IsMainThread()) { \
NS_ERROR("Using observer service off the main thread!"); \
return NS_ERROR_UNEXPECTED; \
} \
if (mShuttingDown) { \
NS_ERROR("Using observer service after XPCOM shutdown!"); \
return NS_ERROR_ILLEGAL_DURING_SHUTDOWN; \
}
*anObserverList = topicObservers;
mObserverTopicTable->Put(&key, topicObservers);
return NS_OK;
NS_IMETHODIMP
nsObserverService::AddObserver(nsIObserver* anObserver, const char* aTopic,
PRBool ownsWeak)
{
NS_ENSURE_VALIDCALL
NS_ENSURE_ARG(anObserver && aTopic);
nsObserverList *observerList = mObserverTopicTable.PutEntry(aTopic);
if (!observerList)
return NS_ERROR_OUT_OF_MEMORY;
return observerList->AddObserver(anObserver, ownsWeak);
}
NS_IMETHODIMP nsObserverService::AddObserver(nsIObserver* anObserver, const char* aTopic, PRBool ownsWeak)
NS_IMETHODIMP
nsObserverService::RemoveObserver(nsIObserver* anObserver, const char* aTopic)
{
nsObserverList* anObserverList;
nsresult rv;
NS_ENSURE_VALIDCALL
NS_ENSURE_ARG(anObserver && aTopic);
if (anObserver == nsnull || aTopic == nsnull)
return NS_ERROR_NULL_POINTER;
nsObserverList *observerList = mObserverTopicTable.GetEntry(aTopic);
if (!observerList)
return NS_ERROR_FAILURE;
rv = GetObserverList(aTopic, &anObserverList);
if (NS_FAILED(rv)) return rv;
return anObserverList->AddObserver(anObserver, ownsWeak);
return observerList->RemoveObserver(anObserver);
}
NS_IMETHODIMP nsObserverService::RemoveObserver(nsIObserver* anObserver, const char* aTopic)
NS_IMETHODIMP
nsObserverService::EnumerateObservers(const char* aTopic,
nsISimpleEnumerator** anEnumerator)
{
nsObserverList* anObserverList;
nsresult rv;
NS_ENSURE_VALIDCALL
NS_ENSURE_ARG(aTopic && anEnumerator);
if (anObserver == nsnull || aTopic == nsnull)
return NS_ERROR_NULL_POINTER;
nsObserverList *observerList = mObserverTopicTable.GetEntry(aTopic);
if (!observerList)
return NS_NewEmptyEnumerator(anEnumerator);
rv = GetObserverList(aTopic, &anObserverList);
if (NS_FAILED(rv)) return rv;
return anObserverList->RemoveObserver(anObserver);
}
NS_IMETHODIMP nsObserverService::EnumerateObservers(const char* aTopic, nsISimpleEnumerator** anEnumerator)
{
nsObserverList* anObserverList;
nsresult rv;
if (anEnumerator == nsnull || aTopic == nsnull)
return NS_ERROR_NULL_POINTER;
rv = GetObserverList(aTopic, &anObserverList);
if (NS_FAILED(rv)) return rv;
return anObserverList->GetObserverList(anEnumerator);
return observerList->GetObserverList(anEnumerator);
}
// Enumerate observers of aTopic and call Observe on each.
NS_IMETHODIMP nsObserverService::NotifyObservers(nsISupports *aSubject,
const char *aTopic,
const PRUnichar *someData) {
nsresult rv = NS_OK;
#ifdef NOTIFY_GLOBAL_OBSERVERS
nsCOMPtr<nsISimpleEnumerator> globalObservers;
#endif
nsCOMPtr<nsISimpleEnumerator> observers;
nsCOMPtr<nsISupports> observerRef;
const PRUnichar *someData)
{
NS_ENSURE_VALIDCALL
NS_ENSURE_ARG(aTopic);
nsObserverList *observerList = mObserverTopicTable.GetEntry(aTopic);
if (observerList)
observerList->NotifyObservers(aSubject, aTopic, someData);
#ifdef NOTIFY_GLOBAL_OBSERVERS
EnumerateObservers("*", getter_AddRefs(globalObservers));
#endif
rv = EnumerateObservers(aTopic, getter_AddRefs(observers));
#ifdef NOTIFY_GLOBAL_OBSERVERS
/* If there are no global observers and we failed to get normal observers
* then we return the error indicating failure to get normal observers.
*/
if (!globalObservers && NS_FAILED(rv))
return rv;
observerList = mObserverTopicTable.GetEntry("*");
if (observerList)
observerList->NotifyObservers(aSubject, aTopic, someData);
#endif
do
{
PRBool more = PR_FALSE;
/* If observers is non null then null it out unless it really
* has more elements (i.e. that call doesn't fail).
*/
if (observers && NS_FAILED(observers->HasMoreElements(&more)) || !more)
{
#ifdef NOTIFY_GLOBAL_OBSERVERS
if (observers = globalObservers)
globalObservers = nsnull;
#else
observers = nsnull;
#endif
}
else
{
observers->GetNext(getter_AddRefs(observerRef));
nsCOMPtr<nsIObserver> observer = do_QueryInterface(observerRef);
if (observer)
observer->Observe(aSubject, aTopic, someData);
}
} while (observers);
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////

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

@ -39,37 +39,37 @@
#define nsObserverService_h___
#include "nsIObserverService.h"
#include "nsObserverList.h"
#include "nsTHashtable.h"
#define NS_OBSERVERSERVICE_CONTRACTID "@mozilla.org/observer-service;1"
#define NS_OBSERVERSERVICE_CLASSNAME "Observer Service"
class nsObserverList;
class nsObjectHashtable;
// {D07F5195-E3D1-11d2-8ACD-00105A1B8860}
#define NS_OBSERVERSERVICE_CID \
{ 0xd07f5195, 0xe3d1, 0x11d2, { 0x8a, 0xcd, 0x0, 0x10, 0x5a, 0x1b, 0x88, 0x60 } }
class nsObserverService : public nsIObserverService {
public:
NS_DEFINE_STATIC_CID_ACCESSOR( NS_OBSERVERSERVICE_CID )
NS_DECLARE_STATIC_IID_ACCESSOR(NS_OBSERVERSERVICE_CID)
nsObserverService();
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVERSERVICE
void Shutdown();
static NS_METHOD
Create(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr);
private:
~nsObserverService(void);
nsObjectHashtable* mObserverTopicTable;
nsresult GetObserverList(const char* aTopic, nsObserverList** anObserverList);
PRBool mShuttingDown;
nsTHashtable<nsObserverList> mObserverTopicTable;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsObserverService, NS_OBSERVERSERVICE_CID)
#endif /* nsObserverService_h___ */

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

@ -482,18 +482,22 @@ class nsTArray : public nsTArray_base {
// and destroy" the first element that is equal to the given element.
// @param item The item to search for.
// @param comp The Comparator used to determine element equality.
// @return PR_TRUE if the element was found
template<class Item, class Comparator>
void RemoveElement(const Item& item, const Comparator& comp) {
PRBool RemoveElement(const Item& item, const Comparator& comp) {
index_type i = IndexOf(item, 0, comp);
if (i != NoIndex)
RemoveElementAt(i);
if (i == NoIndex)
return PR_FALSE;
RemoveElementAt(i);
return PR_TRUE;
}
// A variation on the RemoveElement method defined above that assumes
// that 'operator==' is defined for elem_type.
template<class Item>
void RemoveElement(const Item& item) {
RemoveElement(item, nsDefaultComparator<elem_type, Item>());
PRBool RemoveElement(const Item& item) {
return RemoveElement(item, nsDefaultComparator<elem_type, Item>());
}
//

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

@ -41,8 +41,9 @@
#include "nsIRDFContainerUtils.h"
#include "nsIServiceManager.h"
#include "nsReadableUtils.h"
#include "nsObserverService.h"
#include "nsIObserverService.h"
#include "nsIWindowMediator.h"
#include "nsXPCOMCID.h"
// just to do the reverse-lookup! sheesh.
#include "nsIInterfaceRequestorUtils.h"