1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_SyncRunnable_h 8 #define mozilla_SyncRunnable_h 9 10 #include <utility> 11 12 #include "mozilla/AbstractThread.h" 13 #include "mozilla/dom/JSExecutionManager.h" 14 #include "mozilla/Monitor.h" 15 #include "nsThreadUtils.h" 16 17 namespace mozilla { 18 19 /** 20 * This class will wrap a nsIRunnable and dispatch it to the main thread 21 * synchronously. This is different from nsIEventTarget.DISPATCH_SYNC: 22 * this class does not spin the event loop waiting for the event to be 23 * dispatched. This means that you don't risk reentrance from pending 24 * messages, but you must be sure that the target thread does not ever block 25 * on this thread, or else you will deadlock. 26 * 27 * Typical usage: 28 * RefPtr<SyncRunnable> sr = new SyncRunnable(new myrunnable...()); 29 * sr->DispatchToThread(t); 30 * 31 * We also provide a convenience wrapper: 32 * SyncRunnable::DispatchToThread(new myrunnable...()); 33 * 34 */ 35 class SyncRunnable : public Runnable { 36 public: SyncRunnable(nsIRunnable * aRunnable)37 explicit SyncRunnable(nsIRunnable* aRunnable) 38 : Runnable("SyncRunnable"), 39 mRunnable(aRunnable), 40 mMonitor("SyncRunnable"), 41 mDone(false) {} 42 SyncRunnable(already_AddRefed<nsIRunnable> aRunnable)43 explicit SyncRunnable(already_AddRefed<nsIRunnable> aRunnable) 44 : Runnable("SyncRunnable"), 45 mRunnable(std::move(aRunnable)), 46 mMonitor("SyncRunnable"), 47 mDone(false) {} 48 49 void DispatchToThread(nsIEventTarget* aThread, bool aForceDispatch = false) { 50 nsresult rv; 51 bool on; 52 53 if (!aForceDispatch) { 54 rv = aThread->IsOnCurrentThread(&on); 55 MOZ_ASSERT(NS_SUCCEEDED(rv)); 56 if (NS_SUCCEEDED(rv) && on) { 57 mRunnable->Run(); 58 return; 59 } 60 } 61 62 rv = aThread->Dispatch(this, NS_DISPATCH_NORMAL); 63 if (NS_SUCCEEDED(rv)) { 64 mozilla::MonitorAutoLock lock(mMonitor); 65 // This could be synchronously dispatching to a thread currently waiting 66 // for JS execution clearance. Yield JS execution. 67 dom::AutoYieldJSThreadExecution yield; 68 69 while (!mDone) { 70 lock.Wait(); 71 } 72 } 73 } 74 75 void DispatchToThread(AbstractThread* aThread, bool aForceDispatch = false) { 76 if (!aForceDispatch && aThread->IsCurrentThreadIn()) { 77 mRunnable->Run(); 78 return; 79 } 80 81 // Check we don't have tail dispatching here. Otherwise we will deadlock 82 // ourself when spinning the loop below. 83 MOZ_ASSERT(!aThread->RequiresTailDispatchFromCurrentThread()); 84 85 aThread->Dispatch(RefPtr<nsIRunnable>(this).forget()); 86 mozilla::MonitorAutoLock lock(mMonitor); 87 while (!mDone) { 88 lock.Wait(); 89 } 90 } 91 92 static void DispatchToThread(nsIEventTarget* aThread, nsIRunnable* aRunnable, 93 bool aForceDispatch = false) { 94 RefPtr<SyncRunnable> s(new SyncRunnable(aRunnable)); 95 s->DispatchToThread(aThread, aForceDispatch); 96 } 97 98 static void DispatchToThread(AbstractThread* aThread, nsIRunnable* aRunnable, 99 bool aForceDispatch = false) { 100 RefPtr<SyncRunnable> s(new SyncRunnable(aRunnable)); 101 s->DispatchToThread(aThread, aForceDispatch); 102 } 103 104 protected: Run()105 NS_IMETHOD Run() override { 106 mRunnable->Run(); 107 108 mozilla::MonitorAutoLock lock(mMonitor); 109 MOZ_ASSERT(!mDone); 110 111 mDone = true; 112 mMonitor.Notify(); 113 114 return NS_OK; 115 } 116 117 private: 118 nsCOMPtr<nsIRunnable> mRunnable; 119 mozilla::Monitor mMonitor; 120 bool mDone; 121 }; 122 123 } // namespace mozilla 124 125 #endif // mozilla_SyncRunnable_h 126