/src/mozilla-central/ipc/glue/MessagePump.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | #include "MessagePump.h" |
8 | | |
9 | | #include "nsIRunnable.h" |
10 | | #include "nsIThread.h" |
11 | | #include "nsITimer.h" |
12 | | #include "nsICancelableRunnable.h" |
13 | | |
14 | | #include "base/basictypes.h" |
15 | | #include "base/logging.h" |
16 | | #include "base/scoped_nsautorelease_pool.h" |
17 | | #include "mozilla/Assertions.h" |
18 | | #include "mozilla/DebugOnly.h" |
19 | | #include "nsComponentManagerUtils.h" |
20 | | #include "nsDebug.h" |
21 | | #include "nsServiceManagerUtils.h" |
22 | | #include "nsString.h" |
23 | | #include "nsThreadUtils.h" |
24 | | #include "nsTimerImpl.h" |
25 | | #include "nsXULAppAPI.h" |
26 | | #include "prthread.h" |
27 | | |
28 | | using base::TimeTicks; |
29 | | using namespace mozilla::ipc; |
30 | | |
31 | | NS_DEFINE_NAMED_CID(NS_TIMER_CID); |
32 | | |
33 | | #ifdef DEBUG |
34 | | static MessagePump::Delegate* gFirstDelegate; |
35 | | #endif |
36 | | |
37 | | namespace mozilla { |
38 | | namespace ipc { |
39 | | |
40 | | class DoWorkRunnable final : public CancelableRunnable, |
41 | | public nsITimerCallback |
42 | | { |
43 | | public: |
44 | | explicit DoWorkRunnable(MessagePump* aPump) |
45 | | : CancelableRunnable("ipc::DoWorkRunnable") |
46 | | , mPump(aPump) |
47 | 16 | { |
48 | 16 | MOZ_ASSERT(aPump); |
49 | 16 | } |
50 | | |
51 | | NS_DECL_ISUPPORTS_INHERITED |
52 | | NS_DECL_NSIRUNNABLE |
53 | | NS_DECL_NSITIMERCALLBACK |
54 | | nsresult Cancel() override; |
55 | | |
56 | | private: |
57 | | ~DoWorkRunnable() |
58 | 0 | { } |
59 | | |
60 | | MessagePump* mPump; |
61 | | // DoWorkRunnable is designed as a stateless singleton. Do not add stateful |
62 | | // members here! |
63 | | }; |
64 | | |
65 | | } /* namespace ipc */ |
66 | | } /* namespace mozilla */ |
67 | | |
68 | | MessagePump::MessagePump(nsIEventTarget* aEventTarget) |
69 | | : mEventTarget(aEventTarget) |
70 | 16 | { |
71 | 16 | mDoWorkEvent = new DoWorkRunnable(this); |
72 | 16 | } |
73 | | |
74 | | MessagePump::~MessagePump() |
75 | 0 | { |
76 | 0 | } |
77 | | |
78 | | void |
79 | | MessagePump::Run(MessagePump::Delegate* aDelegate) |
80 | 0 | { |
81 | 0 | MOZ_ASSERT(keep_running_); |
82 | 0 | MOZ_RELEASE_ASSERT(NS_IsMainThread(), |
83 | 0 | "Use mozilla::ipc::MessagePumpForNonMainThreads instead!"); |
84 | 0 | MOZ_RELEASE_ASSERT(!mEventTarget); |
85 | 0 |
|
86 | 0 | nsIThread* thisThread = NS_GetCurrentThread(); |
87 | 0 | MOZ_ASSERT(thisThread); |
88 | 0 |
|
89 | 0 | mDelayedWorkTimer = NS_NewTimer(); |
90 | 0 | MOZ_ASSERT(mDelayedWorkTimer); |
91 | 0 |
|
92 | 0 | base::ScopedNSAutoreleasePool autoReleasePool; |
93 | 0 |
|
94 | 0 | for (;;) { |
95 | 0 | autoReleasePool.Recycle(); |
96 | 0 |
|
97 | 0 | bool did_work = NS_ProcessNextEvent(thisThread, false) ? true : false; |
98 | 0 | if (!keep_running_) |
99 | 0 | break; |
100 | 0 | |
101 | 0 | // NB: it is crucial *not* to directly call |aDelegate->DoWork()| |
102 | 0 | // here. To ensure that MessageLoop tasks and XPCOM events have |
103 | 0 | // equal priority, we sensitively rely on processing exactly one |
104 | 0 | // Task per DoWorkRunnable XPCOM event. |
105 | 0 | |
106 | 0 | did_work |= aDelegate->DoDelayedWork(&delayed_work_time_); |
107 | 0 |
|
108 | 0 | if (did_work && delayed_work_time_.is_null()) |
109 | 0 | mDelayedWorkTimer->Cancel(); |
110 | 0 |
|
111 | 0 | if (!keep_running_) |
112 | 0 | break; |
113 | 0 | |
114 | 0 | if (did_work) |
115 | 0 | continue; |
116 | 0 | |
117 | 0 | did_work = aDelegate->DoIdleWork(); |
118 | 0 | if (!keep_running_) |
119 | 0 | break; |
120 | 0 | |
121 | 0 | if (did_work) |
122 | 0 | continue; |
123 | 0 | |
124 | 0 | // This will either sleep or process an event. |
125 | 0 | NS_ProcessNextEvent(thisThread, true); |
126 | 0 | } |
127 | 0 |
|
128 | 0 | mDelayedWorkTimer->Cancel(); |
129 | 0 |
|
130 | 0 | keep_running_ = true; |
131 | 0 | } |
132 | | |
133 | | void |
134 | | MessagePump::ScheduleWork() |
135 | 0 | { |
136 | 0 | // Make sure the event loop wakes up. |
137 | 0 | if (mEventTarget) { |
138 | 0 | mEventTarget->Dispatch(mDoWorkEvent, NS_DISPATCH_NORMAL); |
139 | 0 | } else { |
140 | 0 | // Some things (like xpcshell) don't use the app shell and so Run hasn't |
141 | 0 | // been called. We still need to wake up the main thread. |
142 | 0 | NS_DispatchToMainThread(mDoWorkEvent); |
143 | 0 | } |
144 | 0 | event_.Signal(); |
145 | 0 | } |
146 | | |
147 | | void |
148 | | MessagePump::ScheduleWorkForNestedLoop() |
149 | 0 | { |
150 | 0 | // This method is called when our MessageLoop has just allowed |
151 | 0 | // nested tasks. In our setup, whenever that happens we know that |
152 | 0 | // DoWork() will be called "soon", so there's no need to pay the |
153 | 0 | // cost of what will be a no-op nsThread::Dispatch(mDoWorkEvent). |
154 | 0 | } |
155 | | |
156 | | void |
157 | | MessagePump::ScheduleDelayedWork(const base::TimeTicks& aDelayedTime) |
158 | 0 | { |
159 | 0 | // To avoid racing on mDelayedWorkTimer, we need to be on the same thread as |
160 | 0 | // ::Run(). |
161 | 0 | MOZ_RELEASE_ASSERT((!mEventTarget && NS_IsMainThread()) |
162 | 0 | || mEventTarget->IsOnCurrentThread()); |
163 | 0 |
|
164 | 0 | if (!mDelayedWorkTimer) { |
165 | 0 | mDelayedWorkTimer = NS_NewTimer(); |
166 | 0 | if (!mDelayedWorkTimer) { |
167 | 0 | // Called before XPCOM has started up? We can't do this correctly. |
168 | 0 | NS_WARNING("Delayed task might not run!"); |
169 | 0 | delayed_work_time_ = aDelayedTime; |
170 | 0 | return; |
171 | 0 | } |
172 | 0 | } |
173 | 0 |
|
174 | 0 | if (!delayed_work_time_.is_null()) { |
175 | 0 | mDelayedWorkTimer->Cancel(); |
176 | 0 | } |
177 | 0 |
|
178 | 0 | delayed_work_time_ = aDelayedTime; |
179 | 0 |
|
180 | 0 | // TimeDelta's constructor initializes to 0 |
181 | 0 | base::TimeDelta delay; |
182 | 0 | if (aDelayedTime > base::TimeTicks::Now()) |
183 | 0 | delay = aDelayedTime - base::TimeTicks::Now(); |
184 | 0 |
|
185 | 0 | uint32_t delayMS = uint32_t(delay.InMilliseconds()); |
186 | 0 | mDelayedWorkTimer->InitWithCallback(mDoWorkEvent, delayMS, |
187 | 0 | nsITimer::TYPE_ONE_SHOT); |
188 | 0 | } |
189 | | |
190 | | nsIEventTarget* |
191 | | MessagePump::GetXPCOMThread() |
192 | 0 | { |
193 | 0 | if (mEventTarget) { |
194 | 0 | return mEventTarget; |
195 | 0 | } |
196 | 0 | |
197 | 0 | // Main thread |
198 | 0 | return GetMainThreadEventTarget(); |
199 | 0 | } |
200 | | |
201 | | void |
202 | | MessagePump::DoDelayedWork(base::MessagePump::Delegate* aDelegate) |
203 | 0 | { |
204 | 0 | aDelegate->DoDelayedWork(&delayed_work_time_); |
205 | 0 | if (!delayed_work_time_.is_null()) { |
206 | 0 | ScheduleDelayedWork(delayed_work_time_); |
207 | 0 | } |
208 | 0 | } |
209 | | |
210 | | NS_IMPL_ISUPPORTS_INHERITED(DoWorkRunnable, CancelableRunnable, |
211 | | nsITimerCallback) |
212 | | |
213 | | NS_IMETHODIMP |
214 | | DoWorkRunnable::Run() |
215 | 0 | { |
216 | 0 | MessageLoop* loop = MessageLoop::current(); |
217 | 0 | MOZ_ASSERT(loop); |
218 | 0 |
|
219 | 0 | bool nestableTasksAllowed = loop->NestableTasksAllowed(); |
220 | 0 |
|
221 | 0 | // MessageLoop::RunTask() disallows nesting, but our Frankenventloop will |
222 | 0 | // always dispatch DoWork() below from what looks to MessageLoop like a nested |
223 | 0 | // context. So we unconditionally allow nesting here. |
224 | 0 | loop->SetNestableTasksAllowed(true); |
225 | 0 | loop->DoWork(); |
226 | 0 | loop->SetNestableTasksAllowed(nestableTasksAllowed); |
227 | 0 |
|
228 | 0 | return NS_OK; |
229 | 0 | } |
230 | | |
231 | | NS_IMETHODIMP |
232 | | DoWorkRunnable::Notify(nsITimer* aTimer) |
233 | 0 | { |
234 | 0 | MessageLoop* loop = MessageLoop::current(); |
235 | 0 | MOZ_ASSERT(loop); |
236 | 0 |
|
237 | 0 | bool nestableTasksAllowed = loop->NestableTasksAllowed(); |
238 | 0 | loop->SetNestableTasksAllowed(true); |
239 | 0 | mPump->DoDelayedWork(loop); |
240 | 0 | loop->SetNestableTasksAllowed(nestableTasksAllowed); |
241 | 0 |
|
242 | 0 | return NS_OK; |
243 | 0 | } |
244 | | |
245 | | nsresult |
246 | | DoWorkRunnable::Cancel() |
247 | 0 | { |
248 | 0 | // Workers require cancelable runnables, but we can't really cancel cleanly |
249 | 0 | // here. If we don't process this runnable then we will leave something |
250 | 0 | // unprocessed in the message_loop. Therefore, eagerly complete our work |
251 | 0 | // instead by immediately calling Run(). Run() should be called separately |
252 | 0 | // after this. Unfortunately we cannot use flags to verify this because |
253 | 0 | // DoWorkRunnable is a stateless singleton that can be in the event queue |
254 | 0 | // multiple times simultaneously. |
255 | 0 | MOZ_ALWAYS_SUCCEEDS(Run()); |
256 | 0 | return NS_OK; |
257 | 0 | } |
258 | | |
259 | | void |
260 | | MessagePumpForChildProcess::Run(base::MessagePump::Delegate* aDelegate) |
261 | 0 | { |
262 | 0 | if (mFirstRun) { |
263 | 0 | MOZ_ASSERT(aDelegate && !gFirstDelegate); |
264 | | #ifdef DEBUG |
265 | | gFirstDelegate = aDelegate; |
266 | | #endif |
267 | |
|
268 | 0 | mFirstRun = false; |
269 | 0 | if (NS_FAILED(XRE_RunAppShell())) { |
270 | 0 | NS_WARNING("Failed to run app shell?!"); |
271 | 0 | } |
272 | 0 |
|
273 | 0 | MOZ_ASSERT(aDelegate && aDelegate == gFirstDelegate); |
274 | | #ifdef DEBUG |
275 | | gFirstDelegate = nullptr; |
276 | | #endif |
277 | |
|
278 | 0 | return; |
279 | 0 | } |
280 | 0 |
|
281 | 0 | MOZ_ASSERT(aDelegate && aDelegate == gFirstDelegate); |
282 | 0 |
|
283 | 0 | // We can get to this point in startup with Tasks in our loop's |
284 | 0 | // incoming_queue_ or pending_queue_, but without a matching |
285 | 0 | // DoWorkRunnable(). In MessagePump::Run() above, we sensitively |
286 | 0 | // depend on *not* directly calling delegate->DoWork(), because that |
287 | 0 | // prioritizes Tasks above XPCOM events. However, from this point |
288 | 0 | // forward, any Task posted to our loop is guaranteed to have a |
289 | 0 | // DoWorkRunnable enqueued for it. |
290 | 0 | // |
291 | 0 | // So we just flush the pending work here and move on. |
292 | 0 | MessageLoop* loop = MessageLoop::current(); |
293 | 0 | bool nestableTasksAllowed = loop->NestableTasksAllowed(); |
294 | 0 | loop->SetNestableTasksAllowed(true); |
295 | 0 |
|
296 | 0 | while (aDelegate->DoWork()); |
297 | 0 |
|
298 | 0 | loop->SetNestableTasksAllowed(nestableTasksAllowed); |
299 | 0 |
|
300 | 0 | // Really run. |
301 | 0 | mozilla::ipc::MessagePump::Run(aDelegate); |
302 | 0 | } |
303 | | |
304 | | void |
305 | | MessagePumpForNonMainThreads::Run(base::MessagePump::Delegate* aDelegate) |
306 | 13 | { |
307 | 13 | MOZ_ASSERT(keep_running_); |
308 | 13 | MOZ_RELEASE_ASSERT(!NS_IsMainThread(), "Use mozilla::ipc::MessagePump instead!"); |
309 | 13 | |
310 | 13 | nsIThread* thread = NS_GetCurrentThread(); |
311 | 13 | MOZ_RELEASE_ASSERT(mEventTarget->IsOnCurrentThread()); |
312 | 13 | |
313 | 13 | mDelayedWorkTimer = NS_NewTimer(mEventTarget); |
314 | 13 | MOZ_ASSERT(mDelayedWorkTimer); |
315 | 13 | |
316 | 13 | // Chromium event notifications to be processed will be received by this |
317 | 13 | // event loop as a DoWorkRunnables via ScheduleWork. Chromium events that |
318 | 13 | // were received before our thread is valid, however, will not generate |
319 | 13 | // runnable wrappers. We must process any of these before we enter this |
320 | 13 | // loop, or we will forever have unprocessed chromium messages in our queue. |
321 | 13 | // |
322 | 13 | // Note we would like to request a flush of the chromium event queue |
323 | 13 | // using a runnable on the xpcom side, but some thread implementations |
324 | 13 | // (dom workers) get cranky if we call ScheduleWork here (ScheduleWork |
325 | 13 | // calls dispatch on mEventTarget) before the thread processes an event. As |
326 | 13 | // such, clear the queue manually. |
327 | 13 | while (aDelegate->DoWork()) { |
328 | 0 | } |
329 | 13 | |
330 | 13 | base::ScopedNSAutoreleasePool autoReleasePool; |
331 | 16 | for (;;) { |
332 | 16 | autoReleasePool.Recycle(); |
333 | 16 | |
334 | 16 | bool didWork = NS_ProcessNextEvent(thread, false) ? true : false; |
335 | 16 | if (!keep_running_) { |
336 | 0 | break; |
337 | 0 | } |
338 | 16 | |
339 | 16 | didWork |= aDelegate->DoDelayedWork(&delayed_work_time_); |
340 | 16 | |
341 | 16 | if (didWork && delayed_work_time_.is_null()) { |
342 | 0 | mDelayedWorkTimer->Cancel(); |
343 | 0 | } |
344 | 16 | |
345 | 16 | if (!keep_running_) { |
346 | 0 | break; |
347 | 0 | } |
348 | 16 | |
349 | 16 | if (didWork) { |
350 | 0 | continue; |
351 | 0 | } |
352 | 16 | |
353 | 16 | DebugOnly<bool> didIdleWork = aDelegate->DoIdleWork(); |
354 | 16 | MOZ_ASSERT(!didIdleWork); |
355 | 16 | if (!keep_running_) { |
356 | 0 | break; |
357 | 0 | } |
358 | 16 | |
359 | 16 | if (didWork) { |
360 | 0 | continue; |
361 | 0 | } |
362 | 16 | |
363 | 16 | // This will either sleep or process an event. |
364 | 16 | NS_ProcessNextEvent(thread, true); |
365 | 16 | } |
366 | 13 | |
367 | 13 | mDelayedWorkTimer->Cancel(); |
368 | 13 | |
369 | 13 | keep_running_ = true; |
370 | 13 | } |
371 | | |
372 | | #if defined(XP_WIN) |
373 | | |
374 | | NS_IMPL_QUERY_INTERFACE(MessagePumpForNonMainUIThreads, nsIThreadObserver) |
375 | | |
376 | | #define CHECK_QUIT_STATE { if (state_->should_quit) { break; } } |
377 | | |
378 | | void |
379 | | MessagePumpForNonMainUIThreads::DoRunLoop() |
380 | | { |
381 | | MOZ_RELEASE_ASSERT(!NS_IsMainThread(), "Use mozilla::ipc::MessagePump instead!"); |
382 | | |
383 | | // If this is a chromium thread and no nsThread is associated |
384 | | // with it, this call will create a new nsThread. |
385 | | nsIThread* thread = NS_GetCurrentThread(); |
386 | | MOZ_ASSERT(thread); |
387 | | |
388 | | // Set the main thread observer so we can wake up when |
389 | | // xpcom events need to get processed. |
390 | | nsCOMPtr<nsIThreadInternal> ti(do_QueryInterface(thread)); |
391 | | MOZ_ASSERT(ti); |
392 | | ti->SetObserver(this); |
393 | | |
394 | | base::ScopedNSAutoreleasePool autoReleasePool; |
395 | | for (;;) { |
396 | | autoReleasePool.Recycle(); |
397 | | |
398 | | bool didWork = NS_ProcessNextEvent(thread, false); |
399 | | |
400 | | didWork |= ProcessNextWindowsMessage(); |
401 | | CHECK_QUIT_STATE |
402 | | |
403 | | didWork |= state_->delegate->DoWork(); |
404 | | CHECK_QUIT_STATE |
405 | | |
406 | | didWork |= state_->delegate->DoDelayedWork(&delayed_work_time_); |
407 | | if (didWork && delayed_work_time_.is_null()) { |
408 | | KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this)); |
409 | | } |
410 | | CHECK_QUIT_STATE |
411 | | |
412 | | if (didWork) { |
413 | | continue; |
414 | | } |
415 | | |
416 | | DebugOnly<bool> didIdleWork = state_->delegate->DoIdleWork(); |
417 | | MOZ_ASSERT(!didIdleWork); |
418 | | CHECK_QUIT_STATE |
419 | | |
420 | | SetInWait(); |
421 | | bool hasWork = NS_HasPendingEvents(thread); |
422 | | if (didWork || hasWork) { |
423 | | ClearInWait(); |
424 | | continue; |
425 | | } |
426 | | WaitForWork(); // Calls MsgWaitForMultipleObjectsEx(QS_ALLINPUT) |
427 | | ClearInWait(); |
428 | | } |
429 | | |
430 | | ClearInWait(); |
431 | | |
432 | | ti->SetObserver(nullptr); |
433 | | } |
434 | | |
435 | | NS_IMETHODIMP |
436 | | MessagePumpForNonMainUIThreads::OnDispatchedEvent() |
437 | | { |
438 | | // If our thread is sleeping in DoRunLoop's call to WaitForWork() and an |
439 | | // event posts to the nsIThread event queue - break our thread out of |
440 | | // chromium's WaitForWork. |
441 | | if (GetInWait()) { |
442 | | ScheduleWork(); |
443 | | } |
444 | | return NS_OK; |
445 | | } |
446 | | |
447 | | NS_IMETHODIMP |
448 | | MessagePumpForNonMainUIThreads::OnProcessNextEvent(nsIThreadInternal *thread, |
449 | | bool mayWait) |
450 | | { |
451 | | return NS_OK; |
452 | | } |
453 | | |
454 | | NS_IMETHODIMP |
455 | | MessagePumpForNonMainUIThreads::AfterProcessNextEvent(nsIThreadInternal *thread, |
456 | | bool eventWasProcessed) |
457 | | { |
458 | | return NS_OK; |
459 | | } |
460 | | |
461 | | #endif // XP_WIN |
462 | | |
463 | | #if defined(MOZ_WIDGET_ANDROID) |
464 | | void |
465 | | MessagePumpForAndroidUI::Run(Delegate* delegate) |
466 | | { |
467 | | MOZ_CRASH("MessagePumpForAndroidUI should never be Run."); |
468 | | } |
469 | | |
470 | | void |
471 | | MessagePumpForAndroidUI::Quit() |
472 | | { |
473 | | MOZ_CRASH("MessagePumpForAndroidUI should never be Quit."); |
474 | | } |
475 | | |
476 | | void |
477 | | MessagePumpForAndroidUI::ScheduleWork() |
478 | | { |
479 | | MOZ_CRASH("MessagePumpForAndroidUI should never ScheduleWork"); |
480 | | } |
481 | | |
482 | | void |
483 | | MessagePumpForAndroidUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) |
484 | | { |
485 | | MOZ_CRASH("MessagePumpForAndroidUI should never ScheduleDelayedWork"); |
486 | | } |
487 | | #endif // defined(MOZ_WIDGET_ANDROID) |