Coverage Report

Created: 2018-09-25 14:53

/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)