xpcom-request-contexts
author Benjamin Smedberg <benjamin@smedbergs.us>
Sat, 26 Jul 2008 22:49:39 -0400
changeset 167 a4da40849f5436e629c5732f4368c6c48189637f
parent 151 02eb136bf089b07658139feb7f266e0e1a79b823
permissions -rw-r--r--
State as of now

* * *

diff --git a/config/config.mk b/config/config.mk
--- a/config/config.mk
+++ b/config/config.mk
@@ -510,6 +510,8 @@ JAVA_DIST_DIR = $(DEPTH)/$(JAVA_GEN_DIR)
 JAVA_DIST_DIR = $(DEPTH)/$(JAVA_GEN_DIR)
 JAVA_IFACES_PKG_NAME = org/mozilla/interfaces
 
+REQUIRES += js
+
 REQ_INCLUDES	= -I$(srcdir) -I. $(foreach d,$(REQUIRES),-I$(DIST)/include/$d) -I$(DIST)/include 
 ifdef LIBXUL_SDK
 REQ_INCLUDES_SDK = $(foreach d,$(REQUIRES),-I$(LIBXUL_SDK)/include/$d) -I$(LIBXUL_SDK)/include
diff --git a/xpcom/base/nsTraceRefcntImpl.cpp b/xpcom/base/nsTraceRefcntImpl.cpp
--- a/xpcom/base/nsTraceRefcntImpl.cpp
+++ b/xpcom/base/nsTraceRefcntImpl.cpp
@@ -50,6 +50,7 @@
 #include <math.h>
 #include "nsStackWalk.h"
 #include "nsXPCOMPrivate.h"
+#include "nsXPCOMRequests.h"
 
 #ifdef HAVE_LIBDL
 #include <dlfcn.h>
@@ -144,6 +145,8 @@ NS_LogInit()
     // to cause period, and we hope hygienic, last-ditch GCs from within
     // the GC's allocator.
     JS_SetGCParameter(gJSRuntime, JSGC_MAX_BYTES, 0xffffffff);
+
+    NS_BeginRequest();
   }
 }
 
@@ -160,6 +163,8 @@ NS_LogTerm()
     nsTraceRefcntImpl::SetActivityIsLegal(PR_FALSE);
     gActivityTLS = BAD_TLS_INDEX;
 #endif
+    NS_EndRequest();
+
     JS_DestroyRuntime(gJSRuntime);
     gJSRuntime = NULL;
   }
diff --git a/xpcom/threads/Makefile.in b/xpcom/threads/Makefile.in
--- a/xpcom/threads/Makefile.in
+++ b/xpcom/threads/Makefile.in
@@ -53,6 +53,7 @@ MOZILLA_INTERNAL_API = 1
 MOZILLA_INTERNAL_API = 1
 
 REQUIRES	= string \
+		  js \
 		  $(NULL)
 
 CPPSRCS		= \
@@ -64,12 +65,14 @@ CPPSRCS		= \
 		nsProcessCommon.cpp \
 		nsTimerImpl.cpp \
 		TimerThread.cpp \
+		nsXPCOMRequests.cpp \
 		$(NULL)
 
 EXPORTS		= \
 		nsProcess.h \
 		nsEventQueue.h \
 		nsThreadUtilsInternal.h \
+		nsXPCOMRequests.h \
 		$(NULL)
 
 XPIDLSRCS	= \
diff --git a/xpcom/threads/nsThread.cpp b/xpcom/threads/nsThread.cpp
--- a/xpcom/threads/nsThread.cpp
+++ b/xpcom/threads/nsThread.cpp
@@ -45,6 +45,7 @@
 #include "nsCOMPtr.h"
 #include "prlog.h"
 #include "nsThreadUtilsInternal.h"
+#include "nsXPCOMRequests.h"
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo *sLog = PR_NewLogModule("nsThread");
@@ -232,6 +233,8 @@ private:
 /*static*/ void
 nsThread::ThreadFunc(void *arg)
 {
+  NS_BeginRequest();
+
   nsThread *self = static_cast<nsThread *>(arg);  // strong reference
   self->mThread = PR_GetCurrentThread();
 
@@ -239,9 +242,10 @@ nsThread::ThreadFunc(void *arg)
   nsThreadManager::get()->RegisterCurrentThread(self);
 
   // Wait for and process startup event
-  nsCOMPtr<nsIRunnable> event;
-  if (!self->GetEvent(PR_TRUE, getter_AddRefs(event))) {
+  nsIRunnable* event = nsnull;
+  if (!self->GetEvent(PR_TRUE, &event)) {
     NS_WARNING("failed waiting for thread startup event");
+    NS_EndRequest();
     return;
   }
   event->Run();  // unblocks nsThread::Init
@@ -278,7 +282,7 @@ nsThread::ThreadFunc(void *arg)
   event = new nsThreadShutdownAckEvent(self->mShutdownContext);
   self->mShutdownContext->joiningThread->Dispatch(event, NS_DISPATCH_NORMAL);
 
-  NS_RELEASE(self);
+  NS_EndRequest();
 }
 
 //-----------------------------------------------------------------------------
@@ -307,10 +311,9 @@ nsThread::Init()
   NS_ENSURE_TRUE(mLock, NS_ERROR_OUT_OF_MEMORY);
 
   // spawn thread and wait until it is fully setup
-  nsRefPtr<nsThreadStartupEvent> startup = nsThreadStartupEvent::Create();
+  nsThreadStartupEvent* startup = nsThreadStartupEvent::Create();
   NS_ENSURE_TRUE(startup, NS_ERROR_OUT_OF_MEMORY);
  
-  NS_ADDREF_THIS();
  
   mShutdownRequired = PR_TRUE;
 
@@ -361,7 +364,7 @@ nsThread::PutEvent(nsIRunnable *event)
       return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  nsCOMPtr<nsIThreadObserver> obs = GetObserver();
+  nsIThreadObserver* obs = GetObserver();
   if (obs)
     obs->OnDispatchedEvent(this);
 
@@ -386,7 +389,7 @@ nsThread::Dispatch(nsIRunnable *event, P
     //     be able to monitor the slot occupied by this event and use
     //     that to tell us when the event has been processed.
  
-    nsRefPtr<nsThreadSyncDispatch> wrapper =
+    nsThreadSyncDispatch* wrapper =
         new nsThreadSyncDispatch(thread, event);
     if (!wrapper)
       return NS_ERROR_OUT_OF_MEMORY;
@@ -448,7 +451,7 @@ nsThread::Shutdown()
 
   // Set mShutdownContext and wake up the thread in case it is waiting for
   // events to process.
-  nsCOMPtr<nsIRunnable> event = new nsThreadShutdownEvent(this, &context);
+  nsIRunnable* event = new nsThreadShutdownEvent(this, &context);
   if (!event)
     return NS_ERROR_OUT_OF_MEMORY;
   // XXXroc What if posting the event fails due to OOM?
@@ -490,15 +493,15 @@ nsThread::ProcessNextEvent(PRBool mayWai
     sGlobalObserver->OnProcessNextEvent(this, mayWait && !ShuttingDown(),
                                         mRunningEvent);
 
-  nsCOMPtr<nsIThreadObserver> obs = mObserver;
+  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));
+  nsIRunnable* event = nsnull; 
+  mEvents->GetEvent(mayWait && !ShuttingDown(), &event);
 
-  *result = (event.get() != nsnull);
+  *result = (event != nsnull);
 
   nsresult rv = NS_OK;
 
@@ -517,6 +520,8 @@ nsThread::ProcessNextEvent(PRBool mayWai
 
   if (notifyGlobalObserver && sGlobalObserver)
     sGlobalObserver->AfterProcessNextEvent(this, mRunningEvent);
+
+  NS_YieldRequest();
 
   return rv;
 }
@@ -573,7 +578,7 @@ nsThread::GetObserver(nsIThreadObserver 
 nsThread::GetObserver(nsIThreadObserver **obs)
 {
   nsAutoLock lock(mLock);
-  NS_IF_ADDREF(*obs = mObserver);
+  *obs = mObserver;
   return NS_OK;
 }
 
@@ -613,8 +618,8 @@ nsThread::PopEventQueue()
   nsChainedEventQueue *queue = mEvents;
   mEvents = mEvents->mNext;
 
-  nsCOMPtr<nsIRunnable> event;
-  while (queue->GetEvent(PR_FALSE, getter_AddRefs(event)))
+  nsIRunnable* event = nsnull;
+  while (queue->GetEvent(PR_FALSE, &event))
     mEvents->PutEvent(event);
 
   delete queue;
diff --git a/xpcom/threads/nsXPCOMRequests.cpp b/xpcom/threads/nsXPCOMRequests.cpp
new file mode 100644
--- /dev/null
+++ b/xpcom/threads/nsXPCOMRequests.cpp
@@ -0,0 +1,78 @@
+#include "nsXPCOMRequests.h"
+
+#include "nsXPCOMPrivate.h"
+#include "prthread.h"
+
+static const PRUintn kINVALID_TPI = (PRUintn) -1;
+static const size_t kStackSize = 8192;
+
+static PRUintn gXPCOMRequestThreadIndex = kINVALID_TPI;
+
+static void
+XPCOMContextDestroyer(void *val)
+{
+  JSContext *cx = (JSContext*) val;
+
+  JS_EndRequest(cx);
+  JS_DestroyContext(cx);
+}
+
+NS_COM void
+NS_BeginRequest()
+{
+  NS_ASSERTION(gJSRuntime, "XPCOM logging not initialized.");
+
+  if (gXPCOMRequestThreadIndex == kINVALID_TPI) {
+    PRStatus ok = PR_NewThreadPrivateIndex(&gXPCOMRequestThreadIndex,
+					   XPCOMContextDestroyer);
+    NS_ASSERTION(ok == PR_SUCCESS, "Couldn't create TPI");
+  }
+
+  NS_ASSERTION(!PR_GetThreadPrivate(gXPCOMRequestThreadIndex),
+	       "NS_BeginRequest called twice on the same thread?");
+
+  JSContext *cx = JS_NewContext(gJSRuntime, kStackSize);
+  PR_SetThreadPrivate(gXPCOMRequestThreadIndex, cx);
+
+  JS_BeginRequest(cx);
+}
+
+NS_COM void
+NS_EndRequest()
+{
+  NS_ASSERTION(gXPCOMRequestThreadIndex != kINVALID_TPI,
+	       "No XPCOMRequest TPI?");
+
+  // Setting to null automatically calls XPCOMContextDestroyer
+  PR_SetThreadPrivate(gXPCOMRequestThreadIndex, nsnull);
+}
+
+NS_COM void
+NS_YieldRequest()
+{
+  NS_ASSERTION(gXPCOMRequestThreadIndex != kINVALID_TPI,
+	       "No XPCOMRequest TPI?");
+
+  JSContext *cx =
+    (JSContext*) PR_GetThreadPrivate(gXPCOMRequestThreadIndex);
+  NS_ASSERTION(cx, "Suspending a request without creating it.");
+
+  JS_YieldRequest(cx);
+}
+
+nsAutoSuspendRequest::nsAutoSuspendRequest()
+{
+  NS_ASSERTION(gXPCOMRequestThreadIndex != kINVALID_TPI,
+	       "No XPCOMRequest TPI?");
+
+  mCX = (JSContext*) PR_GetThreadPrivate(gXPCOMRequestThreadIndex);
+  NS_ASSERTION(mCX, "Suspending a request without creating it.");
+
+  mSaveDepth = JS_SuspendRequest(mCX);
+}
+
+nsAutoSuspendRequest::~nsAutoSuspendRequest()
+{
+  JS_ResumeRequest(mCX, mSaveDepth);
+}
+
diff --git a/xpcom/threads/nsXPCOMRequests.h b/xpcom/threads/nsXPCOMRequests.h
new file mode 100644
--- /dev/null
+++ b/xpcom/threads/nsXPCOMRequests.h
@@ -0,0 +1,39 @@
+#ifndef nsXPCOMRequests_h__
+#define nsXPCOMRequests_h__
+
+#include "nscore.h"
+#include "jsapi.h"
+
+// Begin/EndRequest are normally called by NS_LogInit/NS_LogTerm for the main
+// thread and the thread initialization code for worker threads intialized
+// using the threadmanager.
+//
+// Code which creates new threads manually will need to enter a request before
+// using XPCOM.
+
+NS_COM void
+NS_BeginRequest();
+
+NS_COM void
+NS_EndRequest();
+
+// Temporarily yield a request so GC may proceed if necessary,
+// but immediately re-enter the request
+
+NS_COM void
+NS_YieldRequest();
+
+// Suspend a request for blocking activity such as I/O
+
+class NS_COM nsAutoSuspendRequest
+{
+public:
+  nsAutoSuspendRequest();
+  ~nsAutoSuspendRequest();
+
+private:
+  JSContext *mCX;
+  jsrefcount mSaveDepth;
+};
+
+#endif // nsXPCOMRequests.h