Bug 1361164 - Add infallible IsOnCurrentThread to nsIEventTarget (r=froydnj)
authorBill McCloskey <billm@mozilla.com>
Mon, 22 May 2017 11:26:39 -0700
changeset 363548 a26040f4d439626f98509caa71009675721d12d7
parent 363547 5c652308e5697c237b746e595d056ecde471c4da
child 363549 5dae479fd477bd4ed51db736e259fc7a4542221a
push id91358
push userwmccloskey@mozilla.com
push dateTue, 13 Jun 2017 03:28:47 +0000
treeherdermozilla-inbound@23ad1168311b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1361164
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1361164 - Add infallible IsOnCurrentThread to nsIEventTarget (r=froydnj) MozReview-Commit-ID: 12bk9hQ7Wnv
dom/base/EventSource.cpp
dom/base/WebSocket.cpp
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerPrivate.h
netwerk/base/nsSocketTransportService2.cpp
netwerk/base/nsStreamTransportService.cpp
xpcom/threads/LazyIdleThread.cpp
xpcom/threads/SchedulerGroup.cpp
xpcom/threads/SharedThreadPool.h
xpcom/threads/TaskQueue.cpp
xpcom/threads/ThrottledEventQueue.cpp
xpcom/threads/nsIEventTarget.idl
xpcom/threads/nsIThread.idl
xpcom/threads/nsIThreadInternal.idl
xpcom/threads/nsIThreadPool.idl
xpcom/threads/nsThread.cpp
xpcom/threads/nsThreadPool.cpp
xpcom/threads/nsThreadUtils.cpp
--- a/dom/base/EventSource.cpp
+++ b/dom/base/EventSource.cpp
@@ -958,16 +958,22 @@ EventSourceImpl::GetInterface(const nsII
 
 NS_IMETHODIMP
 EventSourceImpl::IsOnCurrentThread(bool* aResult)
 {
   *aResult = IsTargetThread();
   return NS_OK;
 }
 
+NS_IMETHODIMP_(bool)
+EventSourceImpl::IsOnCurrentThreadInfallible()
+{
+  return IsTargetThread();
+}
+
 nsresult
 EventSourceImpl::GetBaseURI(nsIURI** aBaseURI)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(!IsShutDown());
   NS_ENSURE_ARG_POINTER(aBaseURI);
 
   *aBaseURI = nullptr;
--- a/dom/base/WebSocket.cpp
+++ b/dom/base/WebSocket.cpp
@@ -2875,16 +2875,22 @@ WebSocketImpl::DelayedDispatch(already_A
 
 NS_IMETHODIMP
 WebSocketImpl::IsOnCurrentThread(bool* aResult)
 {
   *aResult = IsTargetThread();
   return NS_OK;
 }
 
+NS_IMETHODIMP_(bool)
+WebSocketImpl::IsOnCurrentThreadInfallible()
+{
+  return IsTargetThread();
+}
+
 bool
 WebSocketImpl::IsTargetThread() const
 {
   return NS_IsMainThread() == mIsMainThread;
 }
 
 void
 WebSocket::AssertIsOnTargetThread() const
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -1713,28 +1713,33 @@ public:
   }
 
   NS_IMETHOD
   DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t) override
   {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
+  NS_IMETHOD_(bool) IsOnCurrentThreadInfallible() override
+  {
+    MutexAutoLock lock(mMutex);
+
+    if (!mWorkerPrivate) {
+      return false;
+    }
+
+    return mWorkerPrivate->IsOnCurrentThread();
+  }
+
   NS_IMETHOD
   IsOnCurrentThread(bool* aIsOnCurrentThread) override
   {
     MOZ_ASSERT(aIsOnCurrentThread);
-    MutexAutoLock lock(mMutex);
-
-    if (!mWorkerPrivate) {
-      *aIsOnCurrentThread = false;
-      return NS_OK;
-    }
-
-    return mWorkerPrivate->IsOnCurrentThread(aIsOnCurrentThread);
+    *aIsOnCurrentThread = IsOnCurrentThreadInfallible();
+    return NS_OK;
   }
 
   NS_DECL_THREADSAFE_ISUPPORTS
 };
 
 NS_IMPL_ISUPPORTS(WorkerControlEventTarget, nsIEventTarget)
 
 END_WORKERS_NAMESPACE
@@ -5355,26 +5360,23 @@ WorkerPrivate::InterruptCallback(JSConte
   }
 
   // Make sure the periodic timer gets turned back on here.
   SetGCTimerMode(PeriodicTimer);
 
   return true;
 }
 
-nsresult
-WorkerPrivate::IsOnCurrentThread(bool* aIsOnCurrentThread)
+bool
+WorkerPrivate::IsOnCurrentThread()
 {
   // May be called on any thread!
 
-  MOZ_ASSERT(aIsOnCurrentThread);
   MOZ_ASSERT(mPRThread);
-
-  *aIsOnCurrentThread = PR_GetCurrentThread() == mPRThread;
-  return NS_OK;
+  return PR_GetCurrentThread() == mPRThread;
 }
 
 void
 WorkerPrivate::ScheduleDeletion(WorkerRanOrNot aRanOrNot)
 {
   AssertIsOnWorkerThread();
   MOZ_ASSERT(mChildWorkers.IsEmpty());
   MOZ_ASSERT(mSyncLoopStack.IsEmpty());
@@ -7097,24 +7099,37 @@ EventTarget::IsOnCurrentThread(bool* aIs
 
   MutexAutoLock lock(mMutex);
 
   if (!mWorkerPrivate) {
     NS_WARNING("A worker's event target was used after the worker has !");
     return NS_ERROR_UNEXPECTED;
   }
 
-  nsresult rv = mWorkerPrivate->IsOnCurrentThread(aIsOnCurrentThread);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
+  *aIsOnCurrentThread = mWorkerPrivate->IsOnCurrentThread();
   return NS_OK;
 }
 
+template <class Derived>
+NS_IMETHODIMP_(bool)
+WorkerPrivateParent<Derived>::
+EventTarget::IsOnCurrentThreadInfallible()
+{
+  // May be called on any thread!
+
+  MutexAutoLock lock(mMutex);
+
+  if (!mWorkerPrivate) {
+    NS_WARNING("A worker's event target was used after the worker has !");
+    return false;
+  }
+
+  return mWorkerPrivate->IsOnCurrentThread();
+}
+
 BEGIN_WORKERS_NAMESPACE
 
 WorkerCrossThreadDispatcher*
 GetWorkerCrossThreadDispatcher(JSContext* aCx, const JS::Value& aWorker)
 {
   if (!aWorker.isObject()) {
     return nullptr;
   }
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -1153,18 +1153,18 @@ public:
   }
 
   void
   DoRunLoop(JSContext* aCx);
 
   bool
   InterruptCallback(JSContext* aCx);
 
-  nsresult
-  IsOnCurrentThread(bool* aIsOnCurrentThread);
+  bool
+  IsOnCurrentThread();
 
   bool
   CloseInternal(JSContext* aCx)
   {
     AssertIsOnWorkerThread();
     return NotifyInternal(aCx, Closing);
   }
 
--- a/netwerk/base/nsSocketTransportService2.cpp
+++ b/netwerk/base/nsSocketTransportService2.cpp
@@ -168,16 +168,24 @@ nsSocketTransportService::DelayedDispatc
 NS_IMETHODIMP
 nsSocketTransportService::IsOnCurrentThread(bool *result)
 {
     nsCOMPtr<nsIThread> thread = GetThreadSafely();
     NS_ENSURE_TRUE(thread, NS_ERROR_NOT_INITIALIZED);
     return thread->IsOnCurrentThread(result);
 }
 
+NS_IMETHODIMP_(bool)
+nsSocketTransportService::IsOnCurrentThreadInfallible()
+{
+    nsCOMPtr<nsIThread> thread = GetThreadSafely();
+    NS_ENSURE_TRUE(thread, false);
+    return thread->IsOnCurrentThread();
+}
+
 //-----------------------------------------------------------------------------
 // socket api (socket thread only)
 
 NS_IMETHODIMP
 nsSocketTransportService::NotifyWhenCanAttachSocket(nsIRunnable *event)
 {
     SOCKET_LOG(("nsSocketTransportService::NotifyWhenCanAttachSocket\n"));
 
--- a/netwerk/base/nsStreamTransportService.cpp
+++ b/netwerk/base/nsStreamTransportService.cpp
@@ -491,16 +491,30 @@ nsStreamTransportService::Dispatch(alrea
 }
 
 NS_IMETHODIMP
 nsStreamTransportService::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
+NS_IMETHODIMP_(bool)
+nsStreamTransportService::IsOnCurrentThreadInfallible()
+{
+    nsCOMPtr<nsIThreadPool> pool;
+    {
+        mozilla::MutexAutoLock lock(mShutdownLock);
+        pool = mPool;
+    }
+    if (!pool) {
+      return false;
+    }
+    return pool->IsOnCurrentThread();
+}
+
 NS_IMETHODIMP
 nsStreamTransportService::IsOnCurrentThread(bool *result)
 {
     nsCOMPtr<nsIThreadPool> pool;
     {
         mozilla::MutexAutoLock lock(mShutdownLock);
         if (mIsShutdown) {
             return NS_ERROR_NOT_INITIALIZED;
--- a/xpcom/threads/LazyIdleThread.cpp
+++ b/xpcom/threads/LazyIdleThread.cpp
@@ -436,16 +436,26 @@ LazyIdleThread::IsOnCurrentThread(bool* 
   if (mThread) {
     return mThread->IsOnCurrentThread(aIsOnCurrentThread);
   }
 
   *aIsOnCurrentThread = false;
   return NS_OK;
 }
 
+NS_IMETHODIMP_(bool)
+LazyIdleThread::IsOnCurrentThreadInfallible()
+{
+  if (mThread) {
+    return mThread->IsOnCurrentThread();
+  }
+
+  return false;
+}
+
 NS_IMETHODIMP
 LazyIdleThread::GetPRThread(PRThread** aPRThread)
 {
   if (mThread) {
     return mThread->GetPRThread(aPRThread);
   }
 
   *aPRThread = nullptr;
--- a/xpcom/threads/SchedulerGroup.cpp
+++ b/xpcom/threads/SchedulerGroup.cpp
@@ -156,16 +156,22 @@ SchedulerEventTarget::DelayedDispatch(al
 
 NS_IMETHODIMP
 SchedulerEventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread)
 {
   *aIsOnCurrentThread = NS_IsMainThread();
   return NS_OK;
 }
 
+NS_IMETHODIMP_(bool)
+SchedulerEventTarget::IsOnCurrentThreadInfallible()
+{
+  return NS_IsMainThread();
+}
+
 /* static */ nsresult
 SchedulerGroup::UnlabeledDispatch(const char* aName,
                                   TaskCategory aCategory,
                                   already_AddRefed<nsIRunnable>&& aRunnable)
 {
   nsCOMPtr<nsIRunnable> runnable(aRunnable);
   if (aName) {
     if (nsCOMPtr<nsINamed> named = do_QueryInterface(runnable)) {
--- a/xpcom/threads/SharedThreadPool.h
+++ b/xpcom/threads/SharedThreadPool.h
@@ -72,16 +72,18 @@ public:
 
   NS_IMETHOD DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t) override
     { return NS_ERROR_NOT_IMPLEMENTED; }
 
   using nsIEventTarget::Dispatch;
 
   NS_IMETHOD IsOnCurrentThread(bool *_retval) override { return !mEventTarget ? NS_ERROR_NULL_POINTER : mEventTarget->IsOnCurrentThread(_retval); }
 
+  NS_IMETHOD_(bool) IsOnCurrentThreadInfallible() override { return mEventTarget && mEventTarget->IsOnCurrentThread(); }
+
   // Creates necessary statics. Called once at startup.
   static void InitStatics();
 
   // Spins the event loop until all thread pools are shutdown.
   // *Must* be called on the main thread.
   static void SpinUntilEmpty();
 
 #if defined(MOZ_ASAN)
--- a/xpcom/threads/TaskQueue.cpp
+++ b/xpcom/threads/TaskQueue.cpp
@@ -51,16 +51,22 @@ public:
 
   NS_IMETHOD
   IsOnCurrentThread(bool* aResult) override
   {
     *aResult = mTaskQueue->IsCurrentThreadIn();
     return NS_OK;
   }
 
+  NS_IMETHOD_(bool)
+  IsOnCurrentThreadInfallible() override
+  {
+    return mTaskQueue->mTarget->IsOnCurrentThread();
+  }
+
   NS_DECL_THREADSAFE_ISUPPORTS
 };
 
 NS_IMPL_ISUPPORTS(TaskQueue::EventTargetWrapper, nsIEventTarget)
 
 TaskQueue::TaskQueue(already_AddRefed<nsIEventTarget> aTarget,
                      const char* aName,
                      bool aRequireTailDispatch)
--- a/xpcom/threads/ThrottledEventQueue.cpp
+++ b/xpcom/threads/ThrottledEventQueue.cpp
@@ -358,20 +358,20 @@ public:
   nsresult
   DelayedDispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aDelay)
   {
     // The base target may implement this, but we don't.  Always fail
     // to provide consistent behavior.
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
-  nsresult
-  IsOnCurrentThread(bool* aResult)
+  bool
+  IsOnCurrentThread()
   {
-    return mBaseTarget->IsOnCurrentThread(aResult);
+    return mBaseTarget->IsOnCurrentThread();
   }
 
   NS_DECL_THREADSAFE_ISUPPORTS
 };
 
 NS_IMPL_ISUPPORTS(ThrottledEventQueue::Inner, nsIObserver);
 
 NS_IMPL_ISUPPORTS(ThrottledEventQueue, ThrottledEventQueue, nsIEventTarget);
@@ -445,12 +445,19 @@ ThrottledEventQueue::DelayedDispatch(alr
                                             uint32_t aFlags)
 {
   return mInner->DelayedDispatch(Move(aEvent), aFlags);
 }
 
 NS_IMETHODIMP
 ThrottledEventQueue::IsOnCurrentThread(bool* aResult)
 {
-  return mInner->IsOnCurrentThread(aResult);
+  *aResult = mInner->IsOnCurrentThread();
+  return NS_OK;
+}
+
+NS_IMETHODIMP_(bool)
+ThrottledEventQueue::IsOnCurrentThreadInfallible()
+{
+  return mInner->IsOnCurrentThread();
 }
 
 } // namespace mozilla
--- a/xpcom/threads/nsIEventTarget.idl
+++ b/xpcom/threads/nsIEventTarget.idl
@@ -8,17 +8,17 @@
 #include "nsIRunnable.idl"
 %{C++
 #include "nsCOMPtr.h"
 #include "mozilla/AlreadyAddRefed.h"
 %}
 
 native alreadyAddRefed_nsIRunnable(already_AddRefed<nsIRunnable>);
 
-[scriptable, uuid(a03b8b63-af8b-4164-b0e5-c41e8b2b7cfa)]
+[builtinclass, scriptable, uuid(a03b8b63-af8b-4164-b0e5-c41e8b2b7cfa)]
 interface nsIEventTarget : nsISupports
 {
   /* until we can get rid of all uses, keep the non-alreadyAddRefed<> version */
 %{C++
     nsresult Dispatch(nsIRunnable* aEvent, uint32_t aFlags) {
       return Dispatch(nsCOMPtr<nsIRunnable>(aEvent).forget(), aFlags);
     }
 %}
@@ -48,23 +48,61 @@ interface nsIEventTarget : nsISupports
    * A thread pool can use this as an optimization hint to not spin up
    * another thread, since the current thread is about to become idle.
    *
    * These events are always async.
    */
   const unsigned long DISPATCH_AT_END = 2;
 
   /**
-   * Check to see if this event target is associated with the current thread.
+   * IsOnCurrentThread() should return true if events dispatched to this target
+   * can possibly run on the current thread, and false otherwise. In the case
+   * of an nsIEventTarget for a thread pool, it should return true on all
+   * threads in the pool. In the case of a non-thread nsIEventTarget such as
+   * ThrottledEventQueue, it should return true on the thread where events are
+   * expected to be processed, even if no events from the queue are actually
+   * being processed right now.
+   *
+   * When called on an nsISerialEventTarget, IsOnCurrentThread can be used to
+   * ensure that no other thread has "ownership" of the event target. As such,
+   * it's useful for asserting that an object is only used on a particular
+   * thread. IsOnCurrentThread can't guarantee that the current event has been
+   * dispatched through a particular event target.
+   *
+   * The infallible version of IsOnCurrentThread() is optimized to avoid a
+   * virtual call for non-thread event targets. Thread targets should set
+   * mVirtualThread to their virtual PRThread. Non-thread targets should leave
+   * mVirtualThread null and implement IsOnCurrentThreadInfallible() to
+   * return the correct answer.
    *
-   * @returns
-   *   A boolean value that if "true" indicates that events dispatched to this
-   *   event target will run on the current thread (i.e., the thread calling
-   *   this method).
+   * The fallible version of IsOnCurrentThread may return errors, such as during
+   * shutdown. If it does not return an error, it should return the same result
+   * as the infallible version. The infallible method should return the correct
+   * result regardless of whether the fallible method returns an error.
    */
+  %{C++
+public:
+  // Infallible. Defined in nsThreadUtils.cpp. Delegates to
+  // IsOnCurrentThreadInfallible when mVirtualThread is null.
+  bool IsOnCurrentThread();
+
+protected:
+  PRThread* mVirtualThread;
+
+  nsIEventTarget() : mVirtualThread(nullptr) {}
+  %}
+  // Note that this method is protected.  We define it through IDL, rather than
+  // in a %{C++ block, to ensure that the correct method indices are recorded
+  // for XPConnect purposes.
+  [noscript,notxpcom] boolean isOnCurrentThreadInfallible();
+  %{C++
+public:
+  %}
+
+  // Fallible version of IsOnCurrentThread.
   boolean isOnCurrentThread();
 
   /**
    * Dispatch an event to this event target.  This function may be called from
    * any thread, and it may be called re-entrantly.
    *
    * @param event
    *   The alreadyAddRefed<> event to dispatch.
@@ -124,11 +162,12 @@ interface nsIEventTarget : nsISupports
 // convenient aliases:
 #define NS_DISPATCH_NORMAL nsIEventTarget::DISPATCH_NORMAL
 #define NS_DISPATCH_SYNC   nsIEventTarget::DISPATCH_SYNC
 #define NS_DISPATCH_AT_END nsIEventTarget::DISPATCH_AT_END
 
 // Convenient NS_DECL variant that includes some C++-only methods.
 #define NS_DECL_NSIEVENTTARGET_FULL                                   \
     NS_DECL_NSIEVENTTARGET                                            \
-    /* Avoid hiding this method */                                    \
-    using nsIEventTarget::Dispatch;
+    /* Avoid hiding these methods */                                  \
+    using nsIEventTarget::Dispatch;                                   \
+    using nsIEventTarget::IsOnCurrentThread;
 %}
--- a/xpcom/threads/nsIThread.idl
+++ b/xpcom/threads/nsIThread.idl
@@ -19,17 +19,17 @@ native alreadyAddRefed_nsIIdlePeriod(alr
  * This interface provides a high-level abstraction for an operating system
  * thread.
  *
  * Threads have a built-in event queue, and a thread is an event target that
  * can receive nsIRunnable objects (events) to be processed on the thread.
  *
  * See nsIThreadManager for the API used to create and locate threads.
  */
-[scriptable, uuid(5801d193-29d1-4964-a6b7-70eb697ddf2b)]
+[builtinclass, scriptable, uuid(5801d193-29d1-4964-a6b7-70eb697ddf2b)]
 interface nsIThread : nsIEventTarget
 {
   /**
    * @returns
    *   The NSPR thread object corresponding to this nsIThread.
    */
   [noscript] readonly attribute PRThread PRThread;
 
--- a/xpcom/threads/nsIThreadInternal.idl
+++ b/xpcom/threads/nsIThreadInternal.idl
@@ -8,17 +8,17 @@
 
 interface nsIRunnable;
 interface nsIThreadObserver;
 
 /**
  * The XPCOM thread object implements this interface, which allows a consumer
  * to observe dispatch activity on the thread.
  */
-[scriptable, uuid(a3a72e5f-71d9-4add-8f30-59a78fb6d5eb)]
+[builtinclass, scriptable, uuid(a3a72e5f-71d9-4add-8f30-59a78fb6d5eb)]
 interface nsIThreadInternal : nsIThread
 {
   /**
    * Get/set the current thread observer (may be null).  This attribute may be
    * read from any thread, but must only be set on the thread corresponding to
    * this thread object.  The observer will be released on the thread
    * corresponding to this thread object after all other events have been
    * processed during a call to Shutdown.
--- a/xpcom/threads/nsIThreadPool.idl
+++ b/xpcom/threads/nsIThreadPool.idl
@@ -22,17 +22,17 @@ interface nsIThreadPoolListener : nsISup
   void onThreadShuttingDown();
 };
 
 /**
  * An interface to a thread pool.  A thread pool creates a limited number of
  * anonymous (unnamed) worker threads.  An event dispatched to the thread pool
  * will be run on the next available worker thread.
  */
-[scriptable, uuid(76ce99c9-8e43-489a-9789-f27cc4424965)]
+[builtinclass, scriptable, uuid(76ce99c9-8e43-489a-9789-f27cc4424965)]
 interface nsIThreadPool : nsIEventTarget
 {
   /**
    * Shutdown the thread pool.  This method may not be executed from any thread
    * in the thread pool.  Instead, it is meant to be executed from another
    * thread (usually the thread that created this thread pool).  When this
    * function returns, the thread pool and all of its threads will be shutdown,
    * and it will no longer be possible to dispatch tasks to the thread pool.
--- a/xpcom/threads/nsThread.cpp
+++ b/xpcom/threads/nsThread.cpp
@@ -454,16 +454,17 @@ nsThread::ThreadFunc(void* aArg)
   using mozilla::ipc::BackgroundChild;
 
   char stackTop;
 
   ThreadInitData* initData = static_cast<ThreadInitData*>(aArg);
   nsThread* self = initData->thread;  // strong reference
 
   self->mThread = PR_GetCurrentThread();
+  self->mVirtualThread = GetCurrentVirtualThread();
   SetupCurrentThreadForChaosMode();
 
   if (!initData->name.IsEmpty()) {
     NS_SetCurrentThreadName(initData->name.BeginReading());
   }
 
   // Inform the ThreadManager
   nsThreadManager::get().RegisterCurrentThread(*self);
@@ -700,16 +701,17 @@ nsThread::Init(const nsACString& aName)
   startup->Wait();
   return NS_OK;
 }
 
 nsresult
 nsThread::InitCurrentThread()
 {
   mThread = PR_GetCurrentThread();
+  mVirtualThread = GetCurrentVirtualThread();
   SetupCurrentThreadForChaosMode();
 
   mIdlePeriod = new IdlePeriod();
 
   nsThreadManager::get().RegisterCurrentThread(*this);
   return NS_OK;
 }
 
@@ -884,16 +886,23 @@ nsThread::DelayedDispatch(already_AddRef
 
 NS_IMETHODIMP
 nsThread::IsOnCurrentThread(bool* aResult)
 {
   *aResult = (PR_GetCurrentThread() == mThread);
   return NS_OK;
 }
 
+NS_IMETHODIMP_(bool)
+nsThread::IsOnCurrentThreadInfallible()
+{
+  // Rely on mVirtualThread being correct.
+  MOZ_CRASH("IsOnCurrentThreadInfallible should never be called on nsIThread");
+}
+
 //-----------------------------------------------------------------------------
 // nsIThread
 
 NS_IMETHODIMP
 nsThread::GetPRThread(PRThread** aResult)
 {
   *aResult = mThread;
   return NS_OK;
@@ -1680,8 +1689,14 @@ nsThread::nsNestedEventTarget::DelayedDi
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsThread::nsNestedEventTarget::IsOnCurrentThread(bool* aResult)
 {
   return mThread->IsOnCurrentThread(aResult);
 }
+
+NS_IMETHODIMP_(bool)
+nsThread::nsNestedEventTarget::IsOnCurrentThreadInfallible()
+{
+  return mThread->IsOnCurrentThread();
+}
--- a/xpcom/threads/nsThreadPool.cpp
+++ b/xpcom/threads/nsThreadPool.cpp
@@ -277,16 +277,30 @@ nsThreadPool::Dispatch(already_AddRefed<
 }
 
 NS_IMETHODIMP
 nsThreadPool::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
+NS_IMETHODIMP_(bool)
+nsThreadPool::IsOnCurrentThreadInfallible()
+{
+  MutexAutoLock lock(mMutex);
+
+  nsIThread* thread = NS_GetCurrentThread();
+  for (uint32_t i = 0; i < static_cast<uint32_t>(mThreads.Count()); ++i) {
+    if (mThreads[i] == thread) {
+      return true;
+    }
+  }
+  return false;
+}
+
 NS_IMETHODIMP
 nsThreadPool::IsOnCurrentThread(bool* aResult)
 {
   MutexAutoLock lock(mMutex);
   if (NS_WARN_IF(mShutdown)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
--- a/xpcom/threads/nsThreadUtils.cpp
+++ b/xpcom/threads/nsThreadUtils.cpp
@@ -540,8 +540,17 @@ GetCurrentVirtualThread()
 
 PRThread*
 GetCurrentPhysicalThread()
 {
   return PR_GetCurrentThread();
 }
 
 } // namespace mozilla
+
+bool
+nsIEventTarget::IsOnCurrentThread()
+{
+  if (mVirtualThread) {
+    return mVirtualThread == GetCurrentVirtualThread();
+  }
+  return IsOnCurrentThreadInfallible();
+}