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