Bug 1858509 add thread-safety annotations around MediaSourceDemuxer::mMonitor r=alwu
[gecko.git] / ipc / glue / MessagePump.cpp
blob50c916608ca2cf032d48f6914c56c3cc0ace7af4
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/. */
7 #include "MessagePump.h"
9 #include "nsIThread.h"
10 #include "nsITimer.h"
11 #include "nsICancelableRunnable.h"
13 #include "base/basictypes.h"
14 #include "base/logging.h"
15 #include "base/scoped_nsautorelease_pool.h"
16 #include "mozilla/Assertions.h"
17 #include "mozilla/DebugOnly.h"
18 #include "nsComponentManagerUtils.h"
19 #include "nsDebug.h"
20 #include "nsServiceManagerUtils.h"
21 #include "nsString.h"
22 #include "nsThreadUtils.h"
23 #include "nsTimerImpl.h"
24 #include "nsXULAppAPI.h"
25 #include "prthread.h"
27 using base::TimeTicks;
28 using namespace mozilla::ipc;
30 #ifdef DEBUG
31 static MessagePump::Delegate* gFirstDelegate;
32 #endif
34 namespace mozilla {
35 namespace ipc {
37 class DoWorkRunnable final : public CancelableRunnable,
38 public nsITimerCallback {
39 public:
40 explicit DoWorkRunnable(MessagePump* aPump)
41 : CancelableRunnable("ipc::DoWorkRunnable"), mPump(aPump) {
42 MOZ_ASSERT(aPump);
45 NS_DECL_ISUPPORTS_INHERITED
46 NS_DECL_NSIRUNNABLE
47 NS_DECL_NSITIMERCALLBACK
48 nsresult Cancel() override;
50 private:
51 ~DoWorkRunnable() = default;
53 MessagePump* mPump;
54 // DoWorkRunnable is designed as a stateless singleton. Do not add stateful
55 // members here!
58 } /* namespace ipc */
59 } /* namespace mozilla */
61 MessagePump::MessagePump(nsISerialEventTarget* aEventTarget)
62 : mEventTarget(aEventTarget) {
63 mDoWorkEvent = new DoWorkRunnable(this);
66 MessagePump::~MessagePump() = default;
68 void MessagePump::Run(MessagePump::Delegate* aDelegate) {
69 MOZ_ASSERT(keep_running_);
70 MOZ_RELEASE_ASSERT(NS_IsMainThread(),
71 "Use mozilla::ipc::MessagePumpForNonMainThreads instead!");
72 MOZ_RELEASE_ASSERT(!mEventTarget);
74 nsIThread* thisThread = NS_GetCurrentThread();
75 MOZ_ASSERT(thisThread);
77 mDelayedWorkTimer = NS_NewTimer();
78 MOZ_ASSERT(mDelayedWorkTimer);
80 base::ScopedNSAutoreleasePool autoReleasePool;
82 for (;;) {
83 autoReleasePool.Recycle();
85 bool did_work = NS_ProcessNextEvent(thisThread, false) ? true : false;
86 if (!keep_running_) break;
88 // NB: it is crucial *not* to directly call |aDelegate->DoWork()|
89 // here. To ensure that MessageLoop tasks and XPCOM events have
90 // equal priority, we sensitively rely on processing exactly one
91 // Task per DoWorkRunnable XPCOM event.
93 did_work |= aDelegate->DoDelayedWork(&delayed_work_time_);
95 if (did_work && delayed_work_time_.is_null()) mDelayedWorkTimer->Cancel();
97 if (!keep_running_) break;
99 if (did_work) continue;
101 did_work = aDelegate->DoIdleWork();
102 if (!keep_running_) break;
104 if (did_work) continue;
106 // This will either sleep or process an event.
107 NS_ProcessNextEvent(thisThread, true);
110 mDelayedWorkTimer->Cancel();
112 keep_running_ = true;
115 void MessagePump::ScheduleWork() {
116 // Make sure the event loop wakes up.
117 if (mEventTarget) {
118 mEventTarget->Dispatch(mDoWorkEvent, NS_DISPATCH_NORMAL);
119 } else {
120 // Some things (like xpcshell) don't use the app shell and so Run hasn't
121 // been called. We still need to wake up the main thread.
122 NS_DispatchToMainThread(mDoWorkEvent);
124 event_.Signal();
127 void MessagePump::ScheduleWorkForNestedLoop() {
128 // This method is called when our MessageLoop has just allowed
129 // nested tasks. In our setup, whenever that happens we know that
130 // DoWork() will be called "soon", so there's no need to pay the
131 // cost of what will be a no-op nsThread::Dispatch(mDoWorkEvent).
134 void MessagePump::ScheduleDelayedWork(const base::TimeTicks& aDelayedTime) {
135 // To avoid racing on mDelayedWorkTimer, we need to be on the same thread as
136 // ::Run().
137 MOZ_RELEASE_ASSERT((!mEventTarget && NS_IsMainThread()) ||
138 mEventTarget->IsOnCurrentThread());
140 if (!mDelayedWorkTimer) {
141 mDelayedWorkTimer = NS_NewTimer();
142 if (!mDelayedWorkTimer) {
143 // Called before XPCOM has started up? We can't do this correctly.
144 NS_WARNING("Delayed task might not run!");
145 delayed_work_time_ = aDelayedTime;
146 return;
150 if (!delayed_work_time_.is_null()) {
151 mDelayedWorkTimer->Cancel();
154 delayed_work_time_ = aDelayedTime;
156 // TimeDelta's constructor initializes to 0
157 base::TimeDelta delay;
158 if (aDelayedTime > base::TimeTicks::Now())
159 delay = aDelayedTime - base::TimeTicks::Now();
161 uint32_t delayMS = uint32_t(delay.InMilliseconds());
162 mDelayedWorkTimer->InitWithCallback(mDoWorkEvent, delayMS,
163 nsITimer::TYPE_ONE_SHOT);
166 nsISerialEventTarget* MessagePump::GetXPCOMThread() {
167 if (mEventTarget) {
168 return mEventTarget;
171 // Main thread
172 return GetMainThreadSerialEventTarget();
175 void MessagePump::DoDelayedWork(base::MessagePump::Delegate* aDelegate) {
176 aDelegate->DoDelayedWork(&delayed_work_time_);
177 if (!delayed_work_time_.is_null()) {
178 ScheduleDelayedWork(delayed_work_time_);
182 NS_IMPL_ISUPPORTS_INHERITED(DoWorkRunnable, CancelableRunnable,
183 nsITimerCallback)
185 NS_IMETHODIMP
186 DoWorkRunnable::Run() {
187 MessageLoop* loop = MessageLoop::current();
188 MOZ_ASSERT(loop);
190 bool nestableTasksAllowed = loop->NestableTasksAllowed();
192 // MessageLoop::RunTask() disallows nesting, but our Frankenventloop will
193 // always dispatch DoWork() below from what looks to MessageLoop like a nested
194 // context. So we unconditionally allow nesting here.
195 loop->SetNestableTasksAllowed(true);
196 loop->DoWork();
197 loop->SetNestableTasksAllowed(nestableTasksAllowed);
199 return NS_OK;
202 NS_IMETHODIMP
203 DoWorkRunnable::Notify(nsITimer* aTimer) {
204 MessageLoop* loop = MessageLoop::current();
205 MOZ_ASSERT(loop);
207 bool nestableTasksAllowed = loop->NestableTasksAllowed();
208 loop->SetNestableTasksAllowed(true);
209 mPump->DoDelayedWork(loop);
210 loop->SetNestableTasksAllowed(nestableTasksAllowed);
212 return NS_OK;
215 nsresult DoWorkRunnable::Cancel() {
216 // Workers require cancelable runnables, but we can't really cancel cleanly
217 // here. If we don't process this runnable then we will leave something
218 // unprocessed in the message_loop. Therefore, eagerly complete our work
219 // instead by immediately calling Run(). Run() should be called separately
220 // after this. Unfortunately we cannot use flags to verify this because
221 // DoWorkRunnable is a stateless singleton that can be in the event queue
222 // multiple times simultaneously.
223 MOZ_ALWAYS_SUCCEEDS(Run());
224 return NS_OK;
227 void MessagePumpForChildProcess::Run(base::MessagePump::Delegate* aDelegate) {
228 if (mFirstRun) {
229 MOZ_ASSERT(aDelegate && !gFirstDelegate);
230 #ifdef DEBUG
231 gFirstDelegate = aDelegate;
232 #endif
234 mFirstRun = false;
235 if (NS_FAILED(XRE_RunAppShell())) {
236 NS_WARNING("Failed to run app shell?!");
239 MOZ_ASSERT(aDelegate && aDelegate == gFirstDelegate);
240 #ifdef DEBUG
241 gFirstDelegate = nullptr;
242 #endif
244 return;
247 MOZ_ASSERT(aDelegate && aDelegate == gFirstDelegate);
249 // We can get to this point in startup with Tasks in our loop's
250 // incoming_queue_ or pending_queue_, but without a matching
251 // DoWorkRunnable(). In MessagePump::Run() above, we sensitively
252 // depend on *not* directly calling delegate->DoWork(), because that
253 // prioritizes Tasks above XPCOM events. However, from this point
254 // forward, any Task posted to our loop is guaranteed to have a
255 // DoWorkRunnable enqueued for it.
257 // So we just flush the pending work here and move on.
258 MessageLoop* loop = MessageLoop::current();
259 bool nestableTasksAllowed = loop->NestableTasksAllowed();
260 loop->SetNestableTasksAllowed(true);
262 while (aDelegate->DoWork())
265 loop->SetNestableTasksAllowed(nestableTasksAllowed);
267 // Really run.
268 mozilla::ipc::MessagePump::Run(aDelegate);
271 void MessagePumpForNonMainThreads::Run(base::MessagePump::Delegate* aDelegate) {
272 MOZ_ASSERT(keep_running_);
273 MOZ_RELEASE_ASSERT(!NS_IsMainThread(),
274 "Use mozilla::ipc::MessagePump instead!");
276 nsIThread* thread = NS_GetCurrentThread();
277 MOZ_RELEASE_ASSERT(mEventTarget->IsOnCurrentThread());
279 mDelayedWorkTimer = NS_NewTimer(mEventTarget);
280 MOZ_ASSERT(mDelayedWorkTimer);
282 // Chromium event notifications to be processed will be received by this
283 // event loop as a DoWorkRunnables via ScheduleWork. Chromium events that
284 // were received before our thread is valid, however, will not generate
285 // runnable wrappers. We must process any of these before we enter this
286 // loop, or we will forever have unprocessed chromium messages in our queue.
288 // Note we would like to request a flush of the chromium event queue
289 // using a runnable on the xpcom side, but some thread implementations
290 // (dom workers) get cranky if we call ScheduleWork here (ScheduleWork
291 // calls dispatch on mEventTarget) before the thread processes an event. As
292 // such, clear the queue manually.
293 while (aDelegate->DoWork()) {
296 base::ScopedNSAutoreleasePool autoReleasePool;
297 for (;;) {
298 autoReleasePool.Recycle();
300 bool didWork = NS_ProcessNextEvent(thread, false) ? true : false;
301 if (!keep_running_) {
302 break;
305 didWork |= aDelegate->DoDelayedWork(&delayed_work_time_);
307 if (didWork && delayed_work_time_.is_null()) {
308 mDelayedWorkTimer->Cancel();
311 if (!keep_running_) {
312 break;
315 if (didWork) {
316 continue;
319 DebugOnly<bool> didIdleWork = aDelegate->DoIdleWork();
320 MOZ_ASSERT(!didIdleWork);
321 if (!keep_running_) {
322 break;
325 if (didWork) {
326 continue;
329 // This will either sleep or process an event.
330 NS_ProcessNextEvent(thread, true);
333 mDelayedWorkTimer->Cancel();
335 keep_running_ = true;