Fix typo in 9b54bd30006c008b4a951331b273613d5bac3abf
[pm.git] / ipc / glue / MessagePump.cpp
blob16e8c7cdd54000f1795d5b8a69a7034257b0c21e
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"
8 #include "nsIThread.h"
9 #include "nsITimer.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"
18 #include "nsDebug.h"
19 #include "nsServiceManagerUtils.h"
20 #include "nsString.h"
21 #include "nsThreadUtils.h"
22 #include "nsTimerImpl.h"
23 #include "nsXULAppAPI.h"
24 #include "prthread.h"
26 #ifdef MOZ_WIDGET_ANDROID
27 #include "AndroidBridge.h"
28 #endif
30 #ifdef MOZ_NUWA_PROCESS
31 #include "ipc/Nuwa.h"
32 #endif
34 using base::TimeTicks;
35 using namespace mozilla::ipc;
37 NS_DEFINE_NAMED_CID(NS_TIMER_CID);
39 static mozilla::DebugOnly<MessagePump::Delegate*> gFirstDelegate;
41 namespace mozilla {
42 namespace ipc {
44 class DoWorkRunnable final : public nsICancelableRunnable,
45 public nsITimerCallback
47 public:
48 explicit DoWorkRunnable(MessagePump* aPump)
49 : mPump(aPump)
51 MOZ_ASSERT(aPump);
54 NS_DECL_THREADSAFE_ISUPPORTS
55 NS_DECL_NSIRUNNABLE
56 NS_DECL_NSITIMERCALLBACK
57 NS_DECL_NSICANCELABLERUNNABLE
59 private:
60 ~DoWorkRunnable()
61 { }
63 MessagePump* mPump;
64 // DoWorkRunnable is designed as a stateless singleton. Do not add stateful
65 // members here!
68 } /* namespace ipc */
69 } /* namespace mozilla */
71 MessagePump::MessagePump()
72 : mThread(nullptr)
74 mDoWorkEvent = new DoWorkRunnable(this);
77 MessagePump::~MessagePump()
81 void
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.
94 mThread = thisThread;
97 mDelayedWorkTimer = do_CreateInstance(kNS_TIMER_CID);
98 MOZ_ASSERT(mDelayedWorkTimer);
100 base::ScopedNSAutoreleasePool autoReleasePool;
102 for (;;) {
103 autoReleasePool.Recycle();
105 bool did_work = NS_ProcessNextEvent(thisThread, false) ? true : false;
106 if (!keep_running_)
107 break;
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.
117 // Bug 750713
118 if (MOZ_LIKELY(AndroidBridge::HasEnv())) {
119 did_work |= mozilla::widget::GoannaAppShell::PumpMessageLoop();
121 #endif
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())
128 #endif
130 mDelayedWorkTimer->Cancel();
132 if (!keep_running_)
133 break;
135 if (did_work)
136 continue;
138 did_work = aDelegate->DoIdleWork();
139 if (!keep_running_)
140 break;
142 if (did_work)
143 continue;
145 // This will either sleep or process an event.
146 NS_ProcessNextEvent(thisThread, true);
149 #ifdef MOZ_NUWA_PROCESS
150 if (!IsNuwaReady() || !IsNuwaProcess())
151 #endif
152 mDelayedWorkTimer->Cancel();
154 keep_running_ = true;
157 void
158 MessagePump::ScheduleWork()
160 // Make sure the event loop wakes up.
161 if (mThread) {
162 mThread->Dispatch(mDoWorkEvent, NS_DISPATCH_NORMAL);
164 else {
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);
169 event_.Signal();
172 void
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).
181 void
182 MessagePump::ScheduleDelayedWork(const base::TimeTicks& aDelayedTime)
184 #ifdef MOZ_NUWA_PROCESS
185 if (IsNuwaReady() && IsNuwaProcess())
186 return;
187 #endif
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;
195 return;
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);
215 void
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)
227 NS_IMETHODIMP
228 DoWorkRunnable::Run()
230 MessageLoop* loop = MessageLoop::current();
231 MOZ_ASSERT(loop);
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);
239 loop->DoWork();
240 loop->SetNestableTasksAllowed(nestableTasksAllowed);
242 return NS_OK;
245 NS_IMETHODIMP
246 DoWorkRunnable::Notify(nsITimer* aTimer)
248 MessageLoop* loop = MessageLoop::current();
249 MOZ_ASSERT(loop);
251 mPump->DoDelayedWork(loop);
253 return NS_OK;
256 NS_IMETHODIMP
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()));
267 return NS_OK;
270 void
271 MessagePumpForChildProcess::Run(base::MessagePump::Delegate* aDelegate)
273 if (mFirstRun) {
274 MOZ_ASSERT(aDelegate && !gFirstDelegate);
275 gFirstDelegate = aDelegate;
277 mFirstRun = false;
278 if (NS_FAILED(XRE_RunAppShell())) {
279 NS_WARNING("Failed to run app shell?!");
282 MOZ_ASSERT(aDelegate && aDelegate == gFirstDelegate);
283 gFirstDelegate = nullptr;
285 return;
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);
307 // Really run.
308 mozilla::ipc::MessagePump::Run(aDelegate);
311 void
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();
318 MOZ_ASSERT(mThread);
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;
342 for (;;) {
343 autoReleasePool.Recycle();
345 bool didWork = NS_ProcessNextEvent(mThread, false) ? true : false;
346 if (!keep_running_) {
347 break;
350 didWork |= aDelegate->DoDelayedWork(&delayed_work_time_);
352 if (didWork && delayed_work_time_.is_null()) {
353 mDelayedWorkTimer->Cancel();
356 if (!keep_running_) {
357 break;
360 if (didWork) {
361 continue;
364 didWork = aDelegate->DoIdleWork();
365 if (!keep_running_) {
366 break;
369 if (didWork) {
370 continue;
373 // This will either sleep or process an event.
374 NS_ProcessNextEvent(mThread, true);
377 mDelayedWorkTimer->Cancel();
379 keep_running_ = true;
382 #if defined(XP_WIN)
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();
393 MOZ_ASSERT(mThread);
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));
398 MOZ_ASSERT(ti);
399 ti->SetObserver(this);
401 base::ScopedNSAutoreleasePool autoReleasePool;
402 for (;;) {
403 autoReleasePool.Recycle();
405 bool didWork = NS_ProcessNextEvent(mThread, false);
407 didWork |= ProcessNextWindowsMessage();
408 CHECK_QUIT_STATE
410 didWork |= state_->delegate->DoWork();
411 CHECK_QUIT_STATE
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));
417 CHECK_QUIT_STATE
419 if (didWork) {
420 continue;
423 didWork = state_->delegate->DoIdleWork();
424 CHECK_QUIT_STATE
426 SetInWait();
427 bool hasWork = NS_HasPendingEvents(mThread);
428 if (didWork || hasWork) {
429 ClearInWait();
430 continue;
432 WaitForWork(); // Calls MsgWaitForMultipleObjectsEx(QS_ALLINPUT)
433 ClearInWait();
436 ClearInWait();
438 ti->SetObserver(nullptr);
441 NS_IMETHODIMP
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.
447 if (GetInWait()) {
448 ScheduleWork();
450 return NS_OK;
453 NS_IMETHODIMP
454 MessagePumpForNonMainUIThreads::OnProcessNextEvent(nsIThreadInternal *thread,
455 bool mayWait,
456 uint32_t recursionDepth)
458 return NS_OK;
461 NS_IMETHODIMP
462 MessagePumpForNonMainUIThreads::AfterProcessNextEvent(nsIThreadInternal *thread,
463 uint32_t recursionDepth,
464 bool eventWasProcessed)
466 return NS_OK;
469 #endif // XP_WIN