Line data Source code
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 "nsThreadUtils.h"
11 : #include "mozilla/AbstractThread.h"
12 : #include "mozilla/Monitor.h"
13 : #include "mozilla/Move.h"
14 :
15 : namespace mozilla {
16 :
17 : /**
18 : * This class will wrap a nsIRunnable and dispatch it to the main thread
19 : * synchronously. This is different from nsIEventTarget.DISPATCH_SYNC:
20 : * this class does not spin the event loop waiting for the event to be
21 : * dispatched. This means that you don't risk reentrance from pending
22 : * messages, but you must be sure that the target thread does not ever block
23 : * on this thread, or else you will deadlock.
24 : *
25 : * Typical usage:
26 : * RefPtr<SyncRunnable> sr = new SyncRunnable(new myrunnable...());
27 : * sr->DispatchToThread(t);
28 : *
29 : * We also provide a convenience wrapper:
30 : * SyncRunnable::DispatchToThread(new myrunnable...());
31 : *
32 : */
33 6 : class SyncRunnable : public Runnable
34 : {
35 : public:
36 1 : explicit SyncRunnable(nsIRunnable* aRunnable)
37 1 : : Runnable("SyncRunnable")
38 : , mRunnable(aRunnable)
39 : , mMonitor("SyncRunnable")
40 1 : , mDone(false)
41 : {
42 1 : }
43 :
44 1 : explicit SyncRunnable(already_AddRefed<nsIRunnable> aRunnable)
45 1 : : Runnable("SyncRunnable")
46 1 : , mRunnable(Move(aRunnable))
47 : , mMonitor("SyncRunnable")
48 2 : , mDone(false)
49 : {
50 1 : }
51 :
52 1 : void DispatchToThread(nsIEventTarget* aThread, bool aForceDispatch = false)
53 : {
54 : nsresult rv;
55 : bool on;
56 :
57 1 : if (!aForceDispatch) {
58 1 : rv = aThread->IsOnCurrentThread(&on);
59 1 : MOZ_ASSERT(NS_SUCCEEDED(rv));
60 1 : if (NS_SUCCEEDED(rv) && on) {
61 0 : mRunnable->Run();
62 0 : return;
63 : }
64 : }
65 :
66 1 : rv = aThread->Dispatch(this, NS_DISPATCH_NORMAL);
67 1 : if (NS_SUCCEEDED(rv)) {
68 2 : mozilla::MonitorAutoLock lock(mMonitor);
69 3 : while (!mDone) {
70 1 : lock.Wait();
71 : }
72 : }
73 : }
74 :
75 : void DispatchToThread(AbstractThread* aThread, bool aForceDispatch = false)
76 : {
77 : if (!aForceDispatch && aThread->IsCurrentThreadIn()) {
78 : mRunnable->Run();
79 : return;
80 : }
81 :
82 : // Check we don't have tail dispatching here. Otherwise we will deadlock
83 : // ourself when spinning the loop below.
84 : MOZ_ASSERT(!aThread->RequiresTailDispatchFromCurrentThread());
85 :
86 : aThread->Dispatch(RefPtr<nsIRunnable>(this).forget());
87 : mozilla::MonitorAutoLock lock(mMonitor);
88 : while (!mDone) {
89 : lock.Wait();
90 : }
91 : }
92 :
93 1 : static void DispatchToThread(nsIEventTarget* aThread,
94 : nsIRunnable* aRunnable,
95 : bool aForceDispatch = false)
96 : {
97 2 : RefPtr<SyncRunnable> s(new SyncRunnable(aRunnable));
98 1 : s->DispatchToThread(aThread, aForceDispatch);
99 1 : }
100 :
101 : static void DispatchToThread(AbstractThread* aThread,
102 : nsIRunnable* aRunnable,
103 : bool aForceDispatch = false)
104 : {
105 : RefPtr<SyncRunnable> s(new SyncRunnable(aRunnable));
106 : s->DispatchToThread(aThread, aForceDispatch);
107 : }
108 :
109 : protected:
110 2 : NS_IMETHOD Run() override
111 : {
112 2 : mRunnable->Run();
113 :
114 4 : mozilla::MonitorAutoLock lock(mMonitor);
115 2 : MOZ_ASSERT(!mDone);
116 :
117 2 : mDone = true;
118 2 : mMonitor.Notify();
119 :
120 4 : return NS_OK;
121 : }
122 :
123 : private:
124 : nsCOMPtr<nsIRunnable> mRunnable;
125 : mozilla::Monitor mMonitor;
126 : bool mDone;
127 : };
128 :
129 : } // namespace mozilla
130 :
131 : #endif // mozilla_SyncRunnable_h
|