michael@0: /* -*- Mode: C++; tab-width: 12; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef mozilla_SyncRunnable_h michael@0: #define mozilla_SyncRunnable_h michael@0: michael@0: #include "nsThreadUtils.h" michael@0: #include "mozilla/Monitor.h" michael@0: michael@0: namespace mozilla { michael@0: michael@0: /** michael@0: * This class will wrap a nsIRunnable and dispatch it to the main thread michael@0: * synchronously. This is different from nsIEventTarget.DISPATCH_SYNC: michael@0: * this class does not spin the event loop waiting for the event to be michael@0: * dispatched. This means that you don't risk reentrance from pending michael@0: * messages, but you must be sure that the target thread does not ever block michael@0: * on this thread, or else you will deadlock. michael@0: * michael@0: * Typical usage: michael@0: * nsRefPtr sr = new SyncRunnable(new myrunnable...()); michael@0: * sr->DispatchToThread(t); michael@0: * michael@0: * We also provide a convenience wrapper: michael@0: * SyncRunnable::DispatchToThread(new myrunnable...()); michael@0: * michael@0: */ michael@0: class SyncRunnable : public nsRunnable michael@0: { michael@0: public: michael@0: SyncRunnable(nsIRunnable* r) michael@0: : mRunnable(r) michael@0: , mMonitor("SyncRunnable") michael@0: , mDone(false) michael@0: { } michael@0: michael@0: void DispatchToThread(nsIEventTarget* thread, michael@0: bool forceDispatch = false) michael@0: { michael@0: nsresult rv; michael@0: bool on; michael@0: michael@0: if (!forceDispatch) { michael@0: rv = thread->IsOnCurrentThread(&on); michael@0: MOZ_ASSERT(NS_SUCCEEDED(rv)); michael@0: if (NS_SUCCEEDED(rv) && on) { michael@0: mRunnable->Run(); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: rv = thread->Dispatch(this, NS_DISPATCH_NORMAL); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mozilla::MonitorAutoLock lock(mMonitor); michael@0: while (!mDone) { michael@0: lock.Wait(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: static void DispatchToThread(nsIEventTarget* thread, michael@0: nsIRunnable* r, michael@0: bool forceDispatch = false) michael@0: { michael@0: nsRefPtr s(new SyncRunnable(r)); michael@0: s->DispatchToThread(thread, forceDispatch); michael@0: } michael@0: michael@0: protected: michael@0: NS_IMETHODIMP Run() michael@0: { michael@0: mRunnable->Run(); michael@0: michael@0: mozilla::MonitorAutoLock lock(mMonitor); michael@0: MOZ_ASSERT(!mDone); michael@0: michael@0: mDone = true; michael@0: mMonitor.Notify(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: nsCOMPtr mRunnable; michael@0: mozilla::Monitor mMonitor; michael@0: bool mDone; michael@0: }; michael@0: michael@0: } // namespace mozilla michael@0: michael@0: #endif // mozilla_SyncRunnable_h