|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 #include "MessagePump.h" |
|
6 |
|
7 #include "nsIRunnable.h" |
|
8 #include "nsIThread.h" |
|
9 #include "nsITimer.h" |
|
10 |
|
11 #include "base/basictypes.h" |
|
12 #include "base/logging.h" |
|
13 #include "base/scoped_nsautorelease_pool.h" |
|
14 #include "mozilla/Assertions.h" |
|
15 #include "mozilla/DebugOnly.h" |
|
16 #include "nsComponentManagerUtils.h" |
|
17 #include "nsDebug.h" |
|
18 #include "nsServiceManagerUtils.h" |
|
19 #include "nsString.h" |
|
20 #include "nsThreadUtils.h" |
|
21 #include "nsTimerImpl.h" |
|
22 #include "nsXULAppAPI.h" |
|
23 #include "prthread.h" |
|
24 |
|
25 #ifdef MOZ_WIDGET_ANDROID |
|
26 #include "AndroidBridge.h" |
|
27 #endif |
|
28 |
|
29 #ifdef MOZ_NUWA_PROCESS |
|
30 #include "ipc/Nuwa.h" |
|
31 #endif |
|
32 |
|
33 using base::TimeTicks; |
|
34 using namespace mozilla::ipc; |
|
35 |
|
36 NS_DEFINE_NAMED_CID(NS_TIMER_CID); |
|
37 |
|
38 static mozilla::DebugOnly<MessagePump::Delegate*> gFirstDelegate; |
|
39 |
|
40 namespace mozilla { |
|
41 namespace ipc { |
|
42 |
|
43 class DoWorkRunnable MOZ_FINAL : public nsIRunnable, |
|
44 public nsITimerCallback |
|
45 { |
|
46 public: |
|
47 DoWorkRunnable(MessagePump* aPump) |
|
48 : mPump(aPump) |
|
49 { |
|
50 MOZ_ASSERT(aPump); |
|
51 } |
|
52 |
|
53 NS_DECL_THREADSAFE_ISUPPORTS |
|
54 NS_DECL_NSIRUNNABLE |
|
55 NS_DECL_NSITIMERCALLBACK |
|
56 |
|
57 private: |
|
58 ~DoWorkRunnable() |
|
59 { } |
|
60 |
|
61 MessagePump* mPump; |
|
62 }; |
|
63 |
|
64 } /* namespace ipc */ |
|
65 } /* namespace mozilla */ |
|
66 |
|
67 MessagePump::MessagePump() |
|
68 : mThread(nullptr) |
|
69 { |
|
70 mDoWorkEvent = new DoWorkRunnable(this); |
|
71 } |
|
72 |
|
73 MessagePump::~MessagePump() |
|
74 { |
|
75 } |
|
76 |
|
77 void |
|
78 MessagePump::Run(MessagePump::Delegate* aDelegate) |
|
79 { |
|
80 MOZ_ASSERT(keep_running_); |
|
81 MOZ_ASSERT(NS_IsMainThread(), |
|
82 "Use mozilla::ipc::MessagePumpForNonMainThreads instead!"); |
|
83 |
|
84 mThread = NS_GetCurrentThread(); |
|
85 MOZ_ASSERT(mThread); |
|
86 |
|
87 mDelayedWorkTimer = do_CreateInstance(kNS_TIMER_CID); |
|
88 MOZ_ASSERT(mDelayedWorkTimer); |
|
89 |
|
90 base::ScopedNSAutoreleasePool autoReleasePool; |
|
91 |
|
92 for (;;) { |
|
93 autoReleasePool.Recycle(); |
|
94 |
|
95 bool did_work = NS_ProcessNextEvent(mThread, false) ? true : false; |
|
96 if (!keep_running_) |
|
97 break; |
|
98 |
|
99 // NB: it is crucial *not* to directly call |aDelegate->DoWork()| |
|
100 // here. To ensure that MessageLoop tasks and XPCOM events have |
|
101 // equal priority, we sensitively rely on processing exactly one |
|
102 // Task per DoWorkRunnable XPCOM event. |
|
103 |
|
104 #ifdef MOZ_WIDGET_ANDROID |
|
105 // This processes messages in the Android Looper. Note that we only |
|
106 // get here if the normal Gecko event loop has been awoken above. |
|
107 // Bug 750713 |
|
108 if (MOZ_LIKELY(AndroidBridge::HasEnv())) { |
|
109 did_work |= mozilla::widget::android::GeckoAppShell::PumpMessageLoop(); |
|
110 } |
|
111 #endif |
|
112 |
|
113 did_work |= aDelegate->DoDelayedWork(&delayed_work_time_); |
|
114 |
|
115 if (did_work && delayed_work_time_.is_null() |
|
116 #ifdef MOZ_NUWA_PROCESS |
|
117 && (!IsNuwaReady() || !IsNuwaProcess()) |
|
118 #endif |
|
119 ) |
|
120 mDelayedWorkTimer->Cancel(); |
|
121 |
|
122 if (!keep_running_) |
|
123 break; |
|
124 |
|
125 if (did_work) |
|
126 continue; |
|
127 |
|
128 did_work = aDelegate->DoIdleWork(); |
|
129 if (!keep_running_) |
|
130 break; |
|
131 |
|
132 if (did_work) |
|
133 continue; |
|
134 |
|
135 // This will either sleep or process an event. |
|
136 NS_ProcessNextEvent(mThread, true); |
|
137 } |
|
138 |
|
139 #ifdef MOZ_NUWA_PROCESS |
|
140 if (!IsNuwaReady() || !IsNuwaProcess()) |
|
141 #endif |
|
142 mDelayedWorkTimer->Cancel(); |
|
143 |
|
144 keep_running_ = true; |
|
145 } |
|
146 |
|
147 void |
|
148 MessagePump::ScheduleWork() |
|
149 { |
|
150 // Make sure the event loop wakes up. |
|
151 if (mThread) { |
|
152 mThread->Dispatch(mDoWorkEvent, NS_DISPATCH_NORMAL); |
|
153 } |
|
154 else { |
|
155 // Some things (like xpcshell) don't use the app shell and so Run hasn't |
|
156 // been called. We still need to wake up the main thread. |
|
157 NS_DispatchToMainThread(mDoWorkEvent, NS_DISPATCH_NORMAL); |
|
158 } |
|
159 event_.Signal(); |
|
160 } |
|
161 |
|
162 void |
|
163 MessagePump::ScheduleWorkForNestedLoop() |
|
164 { |
|
165 // This method is called when our MessageLoop has just allowed |
|
166 // nested tasks. In our setup, whenever that happens we know that |
|
167 // DoWork() will be called "soon", so there's no need to pay the |
|
168 // cost of what will be a no-op nsThread::Dispatch(mDoWorkEvent). |
|
169 } |
|
170 |
|
171 void |
|
172 MessagePump::ScheduleDelayedWork(const base::TimeTicks& aDelayedTime) |
|
173 { |
|
174 #ifdef MOZ_NUWA_PROCESS |
|
175 if (IsNuwaReady() && IsNuwaProcess()) |
|
176 return; |
|
177 #endif |
|
178 |
|
179 if (!mDelayedWorkTimer) { |
|
180 mDelayedWorkTimer = do_CreateInstance(kNS_TIMER_CID); |
|
181 if (!mDelayedWorkTimer) { |
|
182 // Called before XPCOM has started up? We can't do this correctly. |
|
183 NS_WARNING("Delayed task might not run!"); |
|
184 delayed_work_time_ = aDelayedTime; |
|
185 return; |
|
186 } |
|
187 } |
|
188 |
|
189 if (!delayed_work_time_.is_null()) { |
|
190 mDelayedWorkTimer->Cancel(); |
|
191 } |
|
192 |
|
193 delayed_work_time_ = aDelayedTime; |
|
194 |
|
195 // TimeDelta's constructor initializes to 0 |
|
196 base::TimeDelta delay; |
|
197 if (aDelayedTime > base::TimeTicks::Now()) |
|
198 delay = aDelayedTime - base::TimeTicks::Now(); |
|
199 |
|
200 uint32_t delayMS = uint32_t(delay.InMilliseconds()); |
|
201 mDelayedWorkTimer->InitWithCallback(mDoWorkEvent, delayMS, |
|
202 nsITimer::TYPE_ONE_SHOT); |
|
203 } |
|
204 |
|
205 void |
|
206 MessagePump::DoDelayedWork(base::MessagePump::Delegate* aDelegate) |
|
207 { |
|
208 aDelegate->DoDelayedWork(&delayed_work_time_); |
|
209 if (!delayed_work_time_.is_null()) { |
|
210 ScheduleDelayedWork(delayed_work_time_); |
|
211 } |
|
212 } |
|
213 |
|
214 NS_IMPL_ISUPPORTS(DoWorkRunnable, nsIRunnable, nsITimerCallback) |
|
215 |
|
216 NS_IMETHODIMP |
|
217 DoWorkRunnable::Run() |
|
218 { |
|
219 MessageLoop* loop = MessageLoop::current(); |
|
220 MOZ_ASSERT(loop); |
|
221 |
|
222 bool nestableTasksAllowed = loop->NestableTasksAllowed(); |
|
223 |
|
224 // MessageLoop::RunTask() disallows nesting, but our Frankenventloop will |
|
225 // always dispatch DoWork() below from what looks to MessageLoop like a nested |
|
226 // context. So we unconditionally allow nesting here. |
|
227 loop->SetNestableTasksAllowed(true); |
|
228 loop->DoWork(); |
|
229 loop->SetNestableTasksAllowed(nestableTasksAllowed); |
|
230 |
|
231 return NS_OK; |
|
232 } |
|
233 |
|
234 NS_IMETHODIMP |
|
235 DoWorkRunnable::Notify(nsITimer* aTimer) |
|
236 { |
|
237 MessageLoop* loop = MessageLoop::current(); |
|
238 MOZ_ASSERT(loop); |
|
239 |
|
240 mPump->DoDelayedWork(loop); |
|
241 |
|
242 return NS_OK; |
|
243 } |
|
244 |
|
245 void |
|
246 MessagePumpForChildProcess::Run(base::MessagePump::Delegate* aDelegate) |
|
247 { |
|
248 if (mFirstRun) { |
|
249 MOZ_ASSERT(aDelegate && !gFirstDelegate); |
|
250 gFirstDelegate = aDelegate; |
|
251 |
|
252 mFirstRun = false; |
|
253 if (NS_FAILED(XRE_RunAppShell())) { |
|
254 NS_WARNING("Failed to run app shell?!"); |
|
255 } |
|
256 |
|
257 MOZ_ASSERT(aDelegate && aDelegate == gFirstDelegate); |
|
258 gFirstDelegate = nullptr; |
|
259 |
|
260 return; |
|
261 } |
|
262 |
|
263 MOZ_ASSERT(aDelegate && aDelegate == gFirstDelegate); |
|
264 |
|
265 // We can get to this point in startup with Tasks in our loop's |
|
266 // incoming_queue_ or pending_queue_, but without a matching |
|
267 // DoWorkRunnable(). In MessagePump::Run() above, we sensitively |
|
268 // depend on *not* directly calling delegate->DoWork(), because that |
|
269 // prioritizes Tasks above XPCOM events. However, from this point |
|
270 // forward, any Task posted to our loop is guaranteed to have a |
|
271 // DoWorkRunnable enqueued for it. |
|
272 // |
|
273 // So we just flush the pending work here and move on. |
|
274 MessageLoop* loop = MessageLoop::current(); |
|
275 bool nestableTasksAllowed = loop->NestableTasksAllowed(); |
|
276 loop->SetNestableTasksAllowed(true); |
|
277 |
|
278 while (aDelegate->DoWork()); |
|
279 |
|
280 loop->SetNestableTasksAllowed(nestableTasksAllowed); |
|
281 |
|
282 // Really run. |
|
283 mozilla::ipc::MessagePump::Run(aDelegate); |
|
284 } |
|
285 |
|
286 void |
|
287 MessagePumpForNonMainThreads::Run(base::MessagePump::Delegate* aDelegate) |
|
288 { |
|
289 MOZ_ASSERT(keep_running_); |
|
290 MOZ_ASSERT(!NS_IsMainThread(), "Use mozilla::ipc::MessagePump instead!"); |
|
291 |
|
292 mThread = NS_GetCurrentThread(); |
|
293 MOZ_ASSERT(mThread); |
|
294 |
|
295 mDelayedWorkTimer = do_CreateInstance(kNS_TIMER_CID); |
|
296 MOZ_ASSERT(mDelayedWorkTimer); |
|
297 |
|
298 if (NS_FAILED(mDelayedWorkTimer->SetTarget(mThread))) { |
|
299 MOZ_CRASH("Failed to set timer target!"); |
|
300 } |
|
301 |
|
302 base::ScopedNSAutoreleasePool autoReleasePool; |
|
303 |
|
304 for (;;) { |
|
305 autoReleasePool.Recycle(); |
|
306 |
|
307 bool didWork = NS_ProcessNextEvent(mThread, false) ? true : false; |
|
308 if (!keep_running_) { |
|
309 break; |
|
310 } |
|
311 |
|
312 didWork |= aDelegate->DoDelayedWork(&delayed_work_time_); |
|
313 |
|
314 if (didWork && delayed_work_time_.is_null()) { |
|
315 mDelayedWorkTimer->Cancel(); |
|
316 } |
|
317 |
|
318 if (!keep_running_) { |
|
319 break; |
|
320 } |
|
321 |
|
322 if (didWork) { |
|
323 continue; |
|
324 } |
|
325 |
|
326 didWork = aDelegate->DoIdleWork(); |
|
327 if (!keep_running_) { |
|
328 break; |
|
329 } |
|
330 |
|
331 if (didWork) { |
|
332 continue; |
|
333 } |
|
334 |
|
335 // This will either sleep or process an event. |
|
336 NS_ProcessNextEvent(mThread, true); |
|
337 } |
|
338 |
|
339 mDelayedWorkTimer->Cancel(); |
|
340 |
|
341 keep_running_ = true; |
|
342 } |