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/. */
5 #include "MessagePump.h"
7 #include "nsIRunnable.h"
10 #include "nsICancelableRunnable.h"
12 #include "base/basictypes.h"
13 #include "base/logging.h"
14 #include "base/scoped_nsautorelease_pool.h"
15 #include "mozilla/Assertions.h"
16 #include "mozilla/DebugOnly.h"
17 #include "nsComponentManagerUtils.h"
19 #include "nsServiceManagerUtils.h"
21 #include "nsThreadUtils.h"
22 #include "nsTimerImpl.h"
23 #include "nsXULAppAPI.h"
26 #ifdef MOZ_WIDGET_ANDROID
27 #include "AndroidBridge.h"
30 #ifdef MOZ_NUWA_PROCESS
34 using base::TimeTicks
;
35 using namespace mozilla::ipc
;
37 NS_DEFINE_NAMED_CID(NS_TIMER_CID
);
39 static mozilla::DebugOnly
<MessagePump::Delegate
*> gFirstDelegate
;
44 class DoWorkRunnable final
: public nsICancelableRunnable
,
45 public nsITimerCallback
48 explicit DoWorkRunnable(MessagePump
* aPump
)
54 NS_DECL_THREADSAFE_ISUPPORTS
56 NS_DECL_NSITIMERCALLBACK
57 NS_DECL_NSICANCELABLERUNNABLE
64 // DoWorkRunnable is designed as a stateless singleton. Do not add stateful
69 } /* namespace mozilla */
71 MessagePump::MessagePump()
74 mDoWorkEvent
= new DoWorkRunnable(this);
77 MessagePump::~MessagePump()
82 MessagePump::Run(MessagePump::Delegate
* aDelegate
)
84 MOZ_ASSERT(keep_running_
);
85 MOZ_ASSERT(NS_IsMainThread(),
86 "Use mozilla::ipc::MessagePumpForNonMainThreads instead!");
88 nsIThread
* thisThread
= NS_GetCurrentThread();
89 MOZ_ASSERT(thisThread
);
91 if (!NS_IsMainThread()) {
92 // Don't write to mThread if we don't have to. It can cause a race if
93 // ScheduleWork runs on another thread.
97 mDelayedWorkTimer
= do_CreateInstance(kNS_TIMER_CID
);
98 MOZ_ASSERT(mDelayedWorkTimer
);
100 base::ScopedNSAutoreleasePool autoReleasePool
;
103 autoReleasePool
.Recycle();
105 bool did_work
= NS_ProcessNextEvent(thisThread
, false) ? true : false;
109 // NB: it is crucial *not* to directly call |aDelegate->DoWork()|
110 // here. To ensure that MessageLoop tasks and XPCOM events have
111 // equal priority, we sensitively rely on processing exactly one
112 // Task per DoWorkRunnable XPCOM event.
114 #ifdef MOZ_WIDGET_ANDROID
115 // This processes messages in the Android Looper. Note that we only
116 // get here if the normal Goanna event loop has been awoken above.
118 if (MOZ_LIKELY(AndroidBridge::HasEnv())) {
119 did_work
|= mozilla::widget::GoannaAppShell::PumpMessageLoop();
123 did_work
|= aDelegate
->DoDelayedWork(&delayed_work_time_
);
125 if (did_work
&& delayed_work_time_
.is_null()
126 #ifdef MOZ_NUWA_PROCESS
127 && (!IsNuwaReady() || !IsNuwaProcess())
130 mDelayedWorkTimer
->Cancel();
138 did_work
= aDelegate
->DoIdleWork();
145 // This will either sleep or process an event.
146 NS_ProcessNextEvent(thisThread
, true);
149 #ifdef MOZ_NUWA_PROCESS
150 if (!IsNuwaReady() || !IsNuwaProcess())
152 mDelayedWorkTimer
->Cancel();
154 keep_running_
= true;
158 MessagePump::ScheduleWork()
160 // Make sure the event loop wakes up.
162 mThread
->Dispatch(mDoWorkEvent
, NS_DISPATCH_NORMAL
);
165 // Some things (like xpcshell) don't use the app shell and so Run hasn't
166 // been called. We still need to wake up the main thread.
167 NS_DispatchToMainThread(mDoWorkEvent
);
173 MessagePump::ScheduleWorkForNestedLoop()
175 // This method is called when our MessageLoop has just allowed
176 // nested tasks. In our setup, whenever that happens we know that
177 // DoWork() will be called "soon", so there's no need to pay the
178 // cost of what will be a no-op nsThread::Dispatch(mDoWorkEvent).
182 MessagePump::ScheduleDelayedWork(const base::TimeTicks
& aDelayedTime
)
184 #ifdef MOZ_NUWA_PROCESS
185 if (IsNuwaReady() && IsNuwaProcess())
189 if (!mDelayedWorkTimer
) {
190 mDelayedWorkTimer
= do_CreateInstance(kNS_TIMER_CID
);
191 if (!mDelayedWorkTimer
) {
192 // Called before XPCOM has started up? We can't do this correctly.
193 NS_WARNING("Delayed task might not run!");
194 delayed_work_time_
= aDelayedTime
;
199 if (!delayed_work_time_
.is_null()) {
200 mDelayedWorkTimer
->Cancel();
203 delayed_work_time_
= aDelayedTime
;
205 // TimeDelta's constructor initializes to 0
206 base::TimeDelta delay
;
207 if (aDelayedTime
> base::TimeTicks::Now())
208 delay
= aDelayedTime
- base::TimeTicks::Now();
210 uint32_t delayMS
= uint32_t(delay
.InMilliseconds());
211 mDelayedWorkTimer
->InitWithCallback(mDoWorkEvent
, delayMS
,
212 nsITimer::TYPE_ONE_SHOT
);
216 MessagePump::DoDelayedWork(base::MessagePump::Delegate
* aDelegate
)
218 aDelegate
->DoDelayedWork(&delayed_work_time_
);
219 if (!delayed_work_time_
.is_null()) {
220 ScheduleDelayedWork(delayed_work_time_
);
224 NS_IMPL_ISUPPORTS(DoWorkRunnable
, nsIRunnable
, nsITimerCallback
,
225 nsICancelableRunnable
)
228 DoWorkRunnable::Run()
230 MessageLoop
* loop
= MessageLoop::current();
233 bool nestableTasksAllowed
= loop
->NestableTasksAllowed();
235 // MessageLoop::RunTask() disallows nesting, but our Frankenventloop will
236 // always dispatch DoWork() below from what looks to MessageLoop like a nested
237 // context. So we unconditionally allow nesting here.
238 loop
->SetNestableTasksAllowed(true);
240 loop
->SetNestableTasksAllowed(nestableTasksAllowed
);
246 DoWorkRunnable::Notify(nsITimer
* aTimer
)
248 MessageLoop
* loop
= MessageLoop::current();
251 mPump
->DoDelayedWork(loop
);
257 DoWorkRunnable::Cancel()
259 // Workers require cancelable runnables, but we can't really cancel cleanly
260 // here. If we don't process this runnable then we will leave something
261 // unprocessed in the message_loop. Therefore, eagerly complete our work
262 // instead by immediately calling Run(). Run() should be called separately
263 // after this. Unfortunately we cannot use flags to verify this because
264 // DoWorkRunnable is a stateless singleton that can be in the event queue
265 // multiple times simultaneously.
266 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(Run()));
271 MessagePumpForChildProcess::Run(base::MessagePump::Delegate
* aDelegate
)
274 MOZ_ASSERT(aDelegate
&& !gFirstDelegate
);
275 gFirstDelegate
= aDelegate
;
278 if (NS_FAILED(XRE_RunAppShell())) {
279 NS_WARNING("Failed to run app shell?!");
282 MOZ_ASSERT(aDelegate
&& aDelegate
== gFirstDelegate
);
283 gFirstDelegate
= nullptr;
288 MOZ_ASSERT(aDelegate
&& aDelegate
== gFirstDelegate
);
290 // We can get to this point in startup with Tasks in our loop's
291 // incoming_queue_ or pending_queue_, but without a matching
292 // DoWorkRunnable(). In MessagePump::Run() above, we sensitively
293 // depend on *not* directly calling delegate->DoWork(), because that
294 // prioritizes Tasks above XPCOM events. However, from this point
295 // forward, any Task posted to our loop is guaranteed to have a
296 // DoWorkRunnable enqueued for it.
298 // So we just flush the pending work here and move on.
299 MessageLoop
* loop
= MessageLoop::current();
300 bool nestableTasksAllowed
= loop
->NestableTasksAllowed();
301 loop
->SetNestableTasksAllowed(true);
303 while (aDelegate
->DoWork());
305 loop
->SetNestableTasksAllowed(nestableTasksAllowed
);
308 mozilla::ipc::MessagePump::Run(aDelegate
);
312 MessagePumpForNonMainThreads::Run(base::MessagePump::Delegate
* aDelegate
)
314 MOZ_ASSERT(keep_running_
);
315 MOZ_ASSERT(!NS_IsMainThread(), "Use mozilla::ipc::MessagePump instead!");
317 mThread
= NS_GetCurrentThread();
320 mDelayedWorkTimer
= do_CreateInstance(kNS_TIMER_CID
);
321 MOZ_ASSERT(mDelayedWorkTimer
);
323 if (NS_FAILED(mDelayedWorkTimer
->SetTarget(mThread
))) {
324 MOZ_CRASH("Failed to set timer target!");
327 // Chromium event notifications to be processed will be received by this
328 // event loop as a DoWorkRunnables via ScheduleWork. Chromium events that
329 // were received before our mThread is valid, however, will not generate
330 // runnable wrappers. We must process any of these before we enter this
331 // loop, or we will forever have unprocessed chromium messages in our queue.
333 // Note we would like to request a flush of the chromium event queue
334 // using a runnable on the xpcom side, but some thread implementations
335 // (dom workers) get cranky if we call ScheduleWork here (ScheduleWork
336 // calls dispatch on mThread) before the thread processes an event. As
337 // such, clear the queue manually.
338 while (aDelegate
->DoWork()) {
341 base::ScopedNSAutoreleasePool autoReleasePool
;
343 autoReleasePool
.Recycle();
345 bool didWork
= NS_ProcessNextEvent(mThread
, false) ? true : false;
346 if (!keep_running_
) {
350 didWork
|= aDelegate
->DoDelayedWork(&delayed_work_time_
);
352 if (didWork
&& delayed_work_time_
.is_null()) {
353 mDelayedWorkTimer
->Cancel();
356 if (!keep_running_
) {
364 didWork
= aDelegate
->DoIdleWork();
365 if (!keep_running_
) {
373 // This will either sleep or process an event.
374 NS_ProcessNextEvent(mThread
, true);
377 mDelayedWorkTimer
->Cancel();
379 keep_running_
= true;
384 NS_IMPL_QUERY_INTERFACE(MessagePumpForNonMainUIThreads
, nsIThreadObserver
)
386 #define CHECK_QUIT_STATE { if (state_->should_quit) { break; } }
388 void MessagePumpForNonMainUIThreads::DoRunLoop()
390 // If this is a chromium thread and no nsThread is associated
391 // with it, this call will create a new nsThread.
392 mThread
= NS_GetCurrentThread();
395 // Set the main thread observer so we can wake up when
396 // xpcom events need to get processed.
397 nsCOMPtr
<nsIThreadInternal
> ti(do_QueryInterface(mThread
));
399 ti
->SetObserver(this);
401 base::ScopedNSAutoreleasePool autoReleasePool
;
403 autoReleasePool
.Recycle();
405 bool didWork
= NS_ProcessNextEvent(mThread
, false);
407 didWork
|= ProcessNextWindowsMessage();
410 didWork
|= state_
->delegate
->DoWork();
413 didWork
|= state_
->delegate
->DoDelayedWork(&delayed_work_time_
);
414 if (didWork
&& delayed_work_time_
.is_null()) {
415 KillTimer(message_hwnd_
, reinterpret_cast<UINT_PTR
>(this));
423 didWork
= state_
->delegate
->DoIdleWork();
427 bool hasWork
= NS_HasPendingEvents(mThread
);
428 if (didWork
|| hasWork
) {
432 WaitForWork(); // Calls MsgWaitForMultipleObjectsEx(QS_ALLINPUT)
438 ti
->SetObserver(nullptr);
442 MessagePumpForNonMainUIThreads::OnDispatchedEvent(nsIThreadInternal
*thread
)
444 // If our thread is sleeping in DoRunLoop's call to WaitForWork() and an
445 // event posts to the nsIThread event queue - break our thread out of
446 // chromium's WaitForWork.
454 MessagePumpForNonMainUIThreads::OnProcessNextEvent(nsIThreadInternal
*thread
,
456 uint32_t recursionDepth
)
462 MessagePumpForNonMainUIThreads::AfterProcessNextEvent(nsIThreadInternal
*thread
,
463 uint32_t recursionDepth
,
464 bool eventWasProcessed
)