Relanding bug 326777, r=bsmedberg, sr=jst
authorbzbarsky@mit.edu
Tue, 10 Jul 2007 17:57:17 -0700
changeset 3310 69a8c4fff8350c52bf2052863ef2a4a1710e96f5
parent 3309 fcd1b085546bc371b5f76ea078fcd09ebdfde6c8
child 3311 b5f66d17a428af88183ea285b138e6533f7060db
push idunknown
push userunknown
push dateunknown
reviewersbsmedberg, jst
bugs326777
milestone1.9a7pre
Relanding bug 326777, r=bsmedberg, sr=jst
js/src/xpconnect/src/nsXPConnect.cpp
js/src/xpconnect/src/xpcprivate.h
xpcom/threads/Makefile.in
xpcom/threads/nsThread.cpp
xpcom/threads/nsThread.h
--- a/js/src/xpconnect/src/nsXPConnect.cpp
+++ b/js/src/xpconnect/src/nsXPConnect.cpp
@@ -45,18 +45,22 @@
 #include "xpcprivate.h"
 #include "XPCNativeWrapper.h"
 #include "nsBaseHashtable.h"
 #include "nsHashKeys.h"
 #include "jsatom.h"
 #include "jsfun.h"
 #include "jsobj.h"
 #include "jsscript.h"
+#include "nsThreadUtilsInternal.h"
 
-NS_IMPL_THREADSAFE_ISUPPORTS2(nsXPConnect,nsIXPConnect,nsISupportsWeakReference)
+NS_IMPL_THREADSAFE_ISUPPORTS3(nsXPConnect,
+                              nsIXPConnect,
+                              nsISupportsWeakReference,
+                              nsIThreadObserver)
 
 nsXPConnect* nsXPConnect::gSelf = nsnull;
 JSBool       nsXPConnect::gOnceAliveNowDead = JS_FALSE;
 
 const char XPC_CONTEXT_STACK_CONTRACTID[] = "@mozilla.org/js/xpc/ContextStack;1";
 const char XPC_RUNTIME_CONTRACTID[]       = "@mozilla.org/js/xpc/RuntimeService;1";
 const char XPC_EXCEPTION_CONTRACTID[]     = "@mozilla.org/js/xpc/Exception;1";
 const char XPC_CONSOLE_CONTRACTID[]       = "@mozilla.org/consoleservice;1";
@@ -281,16 +285,20 @@ nsXPConnect::GetXPConnect()
             delete gSelf;
             gSelf = nsnull;
         }
         else
         {
             // Initial extra ref to keep the singleton alive
             // balanced by explicit call to ReleaseXPConnectSingleton()
             NS_ADDREF(gSelf);
+            if (NS_FAILED(NS_SetGlobalThreadObserver(gSelf))) {
+                NS_RELEASE(gSelf);
+                // Fall through to returning null
+            }
         }
     }
     return gSelf;
 }
 
 // static
 nsXPConnect*
 nsXPConnect::GetSingleton()
@@ -302,16 +310,17 @@ nsXPConnect::GetSingleton()
 
 // static
 void
 nsXPConnect::ReleaseXPConnectSingleton()
 {
     nsXPConnect* xpc = gSelf;
     if(xpc)
     {
+        NS_SetGlobalThreadObserver(nsnull);
 
 #ifdef XPC_TOOLS_SUPPORT
         if(xpc->mProfiler)
         {
             xpc->mProfiler->Stop();
             xpc->mProfiler->WriteResults(xpc->mProfilerOutputFile);
         }
 #endif
@@ -2009,16 +2018,41 @@ nsXPConnect::FlagSystemFilenamePrefix(co
     if(NS_FAILED(rv))
         return rv;
 
     if(!JS_FlagScriptFilenamePrefix(rt, aFilenamePrefix, JSFILENAME_SYSTEM))
         return NS_ERROR_OUT_OF_MEMORY;
     return NS_OK;
 }
 
+NS_IMETHODIMP
+nsXPConnect::OnProcessNextEvent(nsIThreadInternal *aThread, PRBool aMayWait,
+                                PRUint32 aRecursionDepth)
+{
+    // Push a null JSContext so that we don't see any script during
+    // event processing.
+    NS_ENSURE_STATE(mContextStack);
+    return mContextStack->Push(nsnull);
+}
+
+NS_IMETHODIMP
+nsXPConnect::AfterProcessNextEvent(nsIThreadInternal *aThread,
+                                   PRUint32 aRecursionDepth)
+{
+    NS_ENSURE_STATE(mContextStack);
+    return mContextStack->Pop(nsnull);
+}
+
+NS_IMETHODIMP
+nsXPConnect::OnDispatchedEvent(nsIThreadInternal* aThread)
+{
+    NS_NOTREACHED("Why tell us?");
+    return NS_ERROR_UNEXPECTED;
+}
+
 #ifdef DEBUG
 /* These are here to be callable from a debugger */
 JS_BEGIN_EXTERN_C
 void DumpJSStack()
 {
     nsresult rv;
     nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv));
     if(NS_SUCCEEDED(rv) && xpc)
--- a/js/src/xpconnect/src/xpcprivate.h
+++ b/js/src/xpconnect/src/xpcprivate.h
@@ -121,16 +121,18 @@
 #ifdef XPC_USE_SECURITY_CHECKED_COMPONENT
 #include "nsISecurityCheckedComponent.h"
 #endif
 
 #ifdef XPC_TOOLS_SUPPORT
 #include "nsIXPCToolsProfiler.h"
 #endif
 
+#include "nsIThreadInternal.h"
+
 #ifdef XPC_IDISPATCH_SUPPORT
 // This goop was added because of EXCEPINFO in ThrowCOMError
 // This include is here, because it needs to occur before the undefines below
 #include <atlbase.h>
 #include "oaidl.h"
 // Nasty MS defines
 #undef GetClassInfo
 #undef GetClassName
@@ -421,24 +423,26 @@ private:
 // returned as function call result values they are not addref'd. Exceptions
 // to this rule are noted explicitly.
 
 const PRBool OBJ_IS_GLOBAL = PR_TRUE;
 const PRBool OBJ_IS_NOT_GLOBAL = PR_FALSE;
 struct JSObjectRefcounts;
 
 class nsXPConnect : public nsIXPConnect,
+                    public nsIThreadObserver,
                     public nsSupportsWeakReference,
                     public nsCycleCollectionLanguageRuntime,
                     public nsCycleCollectionParticipant
 {
 public:
     // all the interface method declarations...
     NS_DECL_ISUPPORTS
     NS_DECL_NSIXPCONNECT
+    NS_DECL_NSITHREADOBSERVER
 
     // non-interface implementation
 public:
     // These get non-addref'd pointers
     static nsXPConnect*  GetXPConnect();
     static XPCJSRuntime* GetRuntime(nsXPConnect* xpc = nsnull);
     static XPCContext*   GetContext(JSContext* cx, nsXPConnect* xpc = nsnull);
     static nsIJSRuntimeService* GetJSRuntimeService(nsXPConnect* xpc = nsnull);
--- a/xpcom/threads/Makefile.in
+++ b/xpcom/threads/Makefile.in
@@ -64,16 +64,17 @@ CPPSRCS		= \
 		nsProcessCommon.cpp \
 		nsTimerImpl.cpp \
 		TimerThread.cpp \
 		$(NULL)
 
 EXPORTS		= \
 		nsProcess.h \
 		nsEventQueue.h \
+		nsThreadUtilsInternal.h \
 		$(NULL)
 
 XPIDLSRCS	= \
 		nsIEventTarget.idl \
 		nsIThread.idl \
 		nsIThreadInternal.idl \
 		nsIThreadManager.idl \
 		nsIThreadPool.idl \
--- a/xpcom/threads/nsThread.cpp
+++ b/xpcom/threads/nsThread.cpp
@@ -39,24 +39,27 @@
 #include "nsThread.h"
 #include "nsThreadManager.h"
 #include "nsIClassInfoImpl.h"
 #include "nsIProgrammingLanguage.h"
 #include "nsAutoLock.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "prlog.h"
+#include "nsThreadUtilsInternal.h"
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo *sLog = PR_NewLogModule("nsThread");
 #endif
 #define LOG(args) PR_LOG(sLog, PR_LOG_DEBUG, args)
 
 NS_DECL_CI_INTERFACE_GETTER(nsThread)
 
+nsIThreadObserver* nsThread::sGlobalObserver;
+
 //-----------------------------------------------------------------------------
 // Because we do not have our own nsIFactory, we have to implement nsIClassInfo
 // somewhat manually.
 
 class nsThreadClassInfo : public nsIClassInfo {
 public:
   NS_DECL_ISUPPORTS_INHERITED  // no mRefCnt
   NS_DECL_NSICLASSINFO
@@ -459,16 +462,20 @@ nsThread::HasPendingEvents(PRBool *resul
 
 NS_IMETHODIMP
 nsThread::ProcessNextEvent(PRBool mayWait, PRBool *result)
 {
   LOG(("THRD(%p) ProcessNextEvent [%u %u]\n", this, mayWait, mRunningEvent));
 
   NS_ENSURE_STATE(PR_GetCurrentThread() == mThread);
 
+  if (sGlobalObserver) 
+    sGlobalObserver->OnProcessNextEvent(this, mayWait && !ShuttingDown(),
+                                        mRunningEvent);
+
   nsCOMPtr<nsIThreadObserver> obs = mObserver;
   if (obs)
     obs->OnProcessNextEvent(this, mayWait && !ShuttingDown(), mRunningEvent);
 
   // If we are shutting down, then do not wait for new events.
   nsCOMPtr<nsIRunnable> event; 
   mEvents->GetEvent(mayWait && !ShuttingDown(), getter_AddRefs(event));
 
@@ -484,16 +491,19 @@ nsThread::ProcessNextEvent(PRBool mayWai
   } else if (mayWait) {
     NS_ASSERTION(ShuttingDown(), "This should only happen when shutting down");
     rv = NS_ERROR_UNEXPECTED;
   }
 
   if (obs)
     obs->AfterProcessNextEvent(this, mRunningEvent);
 
+  if (sGlobalObserver)
+    sGlobalObserver->AfterProcessNextEvent(this, mRunningEvent);
+
   return rv;
 }
 
 //-----------------------------------------------------------------------------
 // nsISupportsPriority
 
 NS_IMETHODIMP
 nsThread::GetPriority(PRInt32 *priority)
@@ -613,8 +623,23 @@ nsThreadSyncDispatch::Run()
   if (mSyncTask) {
     mSyncTask->Run();
     mSyncTask = nsnull;
     // unblock the origin thread
     mOrigin->Dispatch(this, NS_DISPATCH_NORMAL);
   }
   return NS_OK;
 }
+
+nsresult
+NS_SetGlobalThreadObserver(nsIThreadObserver* aObserver)
+{
+  if (aObserver && nsThread::sGlobalObserver) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  if (!NS_IsMainThread()) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  nsThread::sGlobalObserver = aObserver;
+  return NS_OK;
+}
--- a/xpcom/threads/nsThread.h
+++ b/xpcom/threads/nsThread.h
@@ -67,16 +67,19 @@ public:
 
   // The PRThread corresponding to this thread.
   PRThread *GetPRThread() { return mThread; }
 
   // If this flag is true, then the nsThread was created using
   // nsIThreadManager::NewThread.
   PRBool ShutdownRequired() { return mShutdownRequired; }
 
+  // The global thread observer
+  static nsIThreadObserver* sGlobalObserver;
+
 private:
   friend class nsThreadShutdownEvent;
 
   ~nsThread();
 
   PRBool ShuttingDown() { return mShutdownContext != nsnull; }
 
   PR_STATIC_CALLBACK(void) ThreadFunc(void *arg);