Make sure that we push a null JSContext on the current thread's XPConnect stack
Make sure that we push a null JSContext on the current thread's XPConnect stack
before processing events.
Bug 326777, r=bsmedberg, sr=jst
--- 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
@@ -47,16 +47,18 @@
#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 +461,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 +490,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 +622,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);
new file mode 100644
--- /dev/null
+++ b/xpcom/threads/nsThreadUtilsInternal.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Boris Zbarsky <bzbarsky@mit.edu>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsThreadUtilsInternal_h_
+#define nsThreadUtilsInternal_h_
+
+#ifdef MOZILLA_INTERNAL_API
+
+class nsIThreadObserver;
+
+/**
+ * Function to set a "global" thread observer that all threads will notify when
+ * they process an event. This observer will not be notified when events are
+ * posted to threads. Only one global observer can be set at a time; an
+ * attempt to change the value without setting it to null first will throw.
+ * This function does NOT take a reference to the observer; the caller of this
+ * function is responsible for setting the observer to null when it goes away.
+ * This method may only be called on the main thread; attempts to do it on
+ * other threads will return an error.
+ */
+extern nsresult
+NS_COM NS_SetGlobalThreadObserver(nsIThreadObserver* aObserver);
+
+#endif // MOZILLA_INTERNAL_API
+
+#endif // nsThreadUtilsInternal_h_