console-heapallocate
author Benjamin Smedberg <benjamin@smedbergs.us>
Sat, 26 Jul 2008 22:49:39 -0400
changeset 167 a4da40849f5436e629c5732f4368c6c48189637f
parent 58 8ecbd849199dbe6e9e281aa564c617fd9eba4899
permissions -rw-r--r--
State as of now

diff --git a/xpcom/base/nsConsoleService.cpp b/xpcom/base/nsConsoleService.cpp
--- a/xpcom/base/nsConsoleService.cpp
+++ b/xpcom/base/nsConsoleService.cpp
@@ -46,7 +46,7 @@
 #include "nsMemory.h"
 #include "nsIServiceManager.h"
 #include "nsIProxyObjectManager.h"
-#include "nsSupportsArray.h"
+#include "nsCOMArray.h"
 #include "nsThreadUtils.h"
 
 #include "nsConsoleService.h"
@@ -75,16 +75,6 @@ nsConsoleService::~nsConsoleService()
         i++;
     }
 
-#ifdef DEBUG_mccabe
-    if (mListeners.Count() != 0) {
-        fprintf(stderr, 
-            "WARNING - %d console error listeners still registered!\n"
-            "More calls to nsIConsoleService::UnregisterListener needed.\n",
-            mListeners.Count());
-    }
-    
-#endif
-
     if (mMessages)
         nsMemory::Free(mMessages);
     if (mLock)
@@ -109,15 +99,6 @@ nsConsoleService::Init()
     return NS_OK;
 }
 
-static PRBool PR_CALLBACK snapshot_enum_func(nsHashKey *key, void *data, void* closure)
-{
-    nsISupportsArray *array = (nsISupportsArray *)closure;
-
-    // Copy each element into the temporary nsSupportsArray...
-    array->AppendElement((nsISupports*)data);
-    return PR_TRUE;
-}
-
 // nsIConsoleService methods
 NS_IMETHODIMP
 nsConsoleService::LogMessage(nsIConsoleMessage *message)
@@ -125,78 +106,50 @@ nsConsoleService::LogMessage(nsIConsoleM
     if (message == nsnull)
         return NS_ERROR_INVALID_ARG;
 
-    nsSupportsArray listenersSnapshot;
-    nsIConsoleMessage *retiredMessage;
-
-    NS_ADDREF(message); // early, in case it's same as replaced below.
+    nsCOMArray<nsIConsoleListener> listeners;
+
 
     /*
      * Lock while updating buffer, and while taking snapshot of
      * listeners array.
-     */
-    {
-        nsAutoLock lock(mLock);
-
-        /*
-         * If there's already a message in the slot we're about to replace,
-         * we've wrapped around, and we need to release the old message.  We
-         * save a pointer to it, so we can release below outside the lock.
-         */
-        retiredMessage = mMessages[mCurrent];
-        
-        mMessages[mCurrent++] = message;
-        if (mCurrent == mBufferSize) {
-            mCurrent = 0; // wrap around.
-            mFull = PR_TRUE;
-        }
-
-        /*
-         * Copy the listeners into the snapshot array - in case a listener
-         * is removed during an Observe(...) notification...
-         */
-        mListeners.Enumerate(snapshot_enum_func, &listenersSnapshot);
-    }
-    if (retiredMessage != nsnull)
-        NS_RELEASE(retiredMessage);
-
-    /*
+     *
      * Iterate through any registered listeners and tell them about
      * the message.  We use the mListening flag to guard against
      * recursive message logs.  This could sometimes result in
      * listeners being skipped because of activity on other threads,
      * when we only care about the recursive case.
      */
-    nsCOMPtr<nsIConsoleListener> listener;
-    nsresult rv;
-    nsresult returned_rv;
-    PRUint32 snapshotCount;
-    rv = listenersSnapshot.Count(&snapshotCount);
-    if (NS_FAILED(rv))
-        return rv;
-
-    {
-        nsAutoLock lock(mLock);
+    {
+        nsAutoLock lock(mLock);
+
+        mMessages[mCurrent++] = message;
+        if (mCurrent == mBufferSize) {
+            mCurrent = 0; // wrap around.
+            mFull = PR_TRUE;
+        }
+
+        /*
+         * Copy the listeners into the snapshot array - in case a listener
+         * is removed during an Observe(...) notification...
+         */
+        for (PRUint32 i = 0; i < mListeners.Length(); ++i)
+            listeners.AppendObject(mListeners[i].proxy);
+
         if (mListening)
             return NS_OK;
+
         mListening = PR_TRUE;
     }
 
-    returned_rv = NS_OK;
-    for (PRUint32 i = 0; i < snapshotCount; i++) {
-        rv = listenersSnapshot.GetElementAt(i, getter_AddRefs(listener));
-        if (NS_FAILED(rv)) {
-            returned_rv = rv;
-            break; // fall thru to mListening restore code below.
-        }
-        listener->Observe(message);
-    }
+    for (PRInt32 i = 0; i < listeners.Count(); ++i)
+        listeners[i]->Observe(message);
     
     {
         nsAutoLock lock(mLock);
         mListening = PR_FALSE;
     }
 
-    return returned_rv;
+    return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -249,12 +202,10 @@ nsConsoleService::GetMessageArray(nsICon
             // if full, fill the buffer starting from mCurrent (which'll be
             // oldest) wrapping around the buffer to the most recent.
             messageArray[i] = mMessages[(mCurrent + i) % mBufferSize];
-            NS_ADDREF(messageArray[i]);
         }
     } else {
         for (i = 0; i < mCurrent; i++) {
             messageArray[i] = mMessages[i];
-            NS_ADDREF(messageArray[i]);
         }
     }
     *count = resultSize;
@@ -267,6 +218,7 @@ nsConsoleService::RegisterListener(nsICo
 nsConsoleService::RegisterListener(nsIConsoleListener *listener) {
     nsresult rv;
 
+    nsISupports* canonical = do_QueryInterface(listener);
     /*
      * Store a threadsafe proxy to the listener rather than the
      * listener itself; we want the console service to be callable
@@ -274,26 +226,19 @@ nsConsoleService::RegisterListener(nsICo
      * thread-specific ways, and we always want to call them on their
      * originating thread.  JavaScript is the motivating example.
      */
-    nsCOMPtr<nsIConsoleListener> proxiedListener;
-
-    rv = GetProxyForListener(listener, getter_AddRefs(proxiedListener));
+    nsIConsoleListener* proxiedListener = nsnull;
+    rv = GetProxyForListener(listener, &proxiedListener);
     if (NS_FAILED(rv))
         return rv;
 
     {
         nsAutoLock lock(mLock);
-        nsISupportsKey key(listener);
-
-        /*
-         * Put the proxy event listener into a hashtable using the *real* 
-         * listener as the key.
-         *
-         * This is necessary because proxy objects do *not* maintain
-         * nsISupports identity.  Therefore, since GetProxyForListener(...)
-         * can return different proxies for the same object (see bug #85831)
-         * we need to use the real object as the unique key...
-         */
-        mListeners.Put(&key, proxiedListener);
+
+        if (mListeners.IndexOf(canonical, 0, ListenerProxyCompare()) == (PRUint32) -1) {
+            ListenerProxy *item = mListeners.AppendElement();
+            item->canonical = canonical;
+            item->proxy = proxiedListener;
+        }
     }
     return NS_OK;
 }
@@ -302,8 +247,10 @@ nsConsoleService::UnregisterListener(nsI
 nsConsoleService::UnregisterListener(nsIConsoleListener *listener) {
     nsAutoLock lock(mLock);
 
-    nsISupportsKey key(listener);
-    mListeners.Remove(&key);
+    nsISupports* canonical = do_QueryInterface(listener);
+
+    mListeners.RemoveElement(canonical, ListenerProxyCompare());
+
     return NS_OK;
 }
 
diff --git a/xpcom/base/nsConsoleService.h b/xpcom/base/nsConsoleService.h
--- a/xpcom/base/nsConsoleService.h
+++ b/xpcom/base/nsConsoleService.h
@@ -43,12 +43,12 @@
 #define __nsconsoleservice_h__
 
 #include "nsCOMPtr.h"
-#include "nsHashtable.h"
 #include "nsAutoLock.h"
+#include "nsTArray.h"
 
 #include "nsIConsoleService.h"
 
-class nsConsoleService : public nsIConsoleService
+class nsConsoleService : public XPCOMGCFinalizedObject, public nsIConsoleService
 {
 public:
     nsConsoleService();
@@ -77,7 +77,21 @@ private:
     PRBool mFull;
 
     // Listeners to notify whenever a new message is logged.
-    nsSupportsHashtable mListeners;
+    struct ListenerProxy
+    {
+        nsISupports* canonical;
+        nsIConsoleListener* proxy;
+    };
+
+    struct ListenerProxyCompare
+    {
+        PRBool Equals(const ListenerProxy &a, const nsISupports* b) const
+        {
+            return a.canonical == b;
+        }
+    };
+
+    nsTArray<ListenerProxy, GCAllocator> mListeners;
 
     // Current listener being notified of a logged error - to prevent
     // stack overflows.
diff --git a/xpcom/glue/nsTArray.h b/xpcom/glue/nsTArray.h
--- a/xpcom/glue/nsTArray.h
+++ b/xpcom/glue/nsTArray.h
@@ -695,6 +695,13 @@ class nsTArray : public nsTArray_base<Al
       Sort(nsDefaultComparator<elem_type, elem_type>());
     }
 
+    // Fire a write-barrier using the canonical address of the allocated buffer
+    void WriteBarrier(void *address, const void *value)
+    {
+      NS_ASSERTION(mHdr, "Write-barrier on empty array");
+      WB(NS_GetGC(), mHdr, address, value);
+    }
+
   protected:
 
     // This method invokes elem_type's destructor on a range of elements.