Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/base/nsMessageLoop.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 "nsMessageLoop.h"
8
#include "mozilla/WeakPtr.h"
9
#include "base/message_loop.h"
10
#include "base/task.h"
11
#include "nsINamed.h"
12
#include "nsIRunnable.h"
13
#include "nsITimer.h"
14
#include "nsCOMPtr.h"
15
#include "nsAutoPtr.h"
16
#include "nsComponentManagerUtils.h"
17
#include "nsThreadUtils.h"
18
19
using namespace mozilla;
20
21
namespace {
22
23
/**
24
 * This Task runs its nsIRunnable when Run() is called, or after
25
 * aEnsureRunsAfterMS milliseconds have elapsed since the object was
26
 * constructed.
27
 *
28
 * Note that the MessageLoop owns this object and will delete it after it calls
29
 * Run().  Tread lightly.
30
 */
31
class MessageLoopIdleTask
32
  : public Runnable
33
  , public SupportsWeakPtr<MessageLoopIdleTask>
34
{
35
public:
36
  MOZ_DECLARE_WEAKREFERENCE_TYPENAME(MessageLoopIdleTask)
37
  MessageLoopIdleTask(nsIRunnable* aTask, uint32_t aEnsureRunsAfterMS);
38
  NS_IMETHOD Run() override;
39
40
private:
41
  nsresult Init(uint32_t aEnsureRunsAfterMS);
42
43
  nsCOMPtr<nsIRunnable> mTask;
44
  nsCOMPtr<nsITimer> mTimer;
45
46
0
  virtual ~MessageLoopIdleTask() {}
47
};
48
49
/**
50
 * This timer callback calls MessageLoopIdleTask::Run() when its timer fires.
51
 * (The timer can't call back into MessageLoopIdleTask directly since that's
52
 * not a refcounted object; it's owned by the MessageLoop.)
53
 *
54
 * We keep a weak reference to the MessageLoopIdleTask, although a raw pointer
55
 * should in theory suffice: When the MessageLoopIdleTask runs (right before
56
 * the MessageLoop deletes it), it cancels its timer.  But the weak pointer
57
 * saves us from worrying about an edge case somehow messing us up here.
58
 */
59
class MessageLoopTimerCallback
60
  : public nsITimerCallback, public nsINamed
61
{
62
public:
63
  explicit MessageLoopTimerCallback(MessageLoopIdleTask* aTask);
64
65
  NS_DECL_ISUPPORTS
66
  NS_DECL_NSITIMERCALLBACK
67
68
  NS_IMETHOD GetName(nsACString& aName) override
69
0
  {
70
0
    aName.AssignLiteral("MessageLoopTimerCallback");
71
0
    return NS_OK;
72
0
  }
73
74
private:
75
  WeakPtr<MessageLoopIdleTask> mTask;
76
77
0
  virtual ~MessageLoopTimerCallback() {}
78
};
79
80
MessageLoopIdleTask::MessageLoopIdleTask(nsIRunnable* aTask,
81
                                         uint32_t aEnsureRunsAfterMS)
82
  : mozilla::Runnable("MessageLoopIdleTask")
83
  , mTask(aTask)
84
0
{
85
0
  // Init() really shouldn't fail, but if it does, we schedule our runnable
86
0
  // immediately, because it's more important to guarantee that we run the task
87
0
  // eventually than it is to run the task when we're idle.
88
0
  nsresult rv = Init(aEnsureRunsAfterMS);
89
0
  if (NS_FAILED(rv)) {
90
0
    NS_WARNING("Running idle task early because we couldn't initialize our timer.");
91
0
    NS_DispatchToCurrentThread(mTask);
92
0
93
0
    mTask = nullptr;
94
0
    mTimer = nullptr;
95
0
  }
96
0
}
97
98
nsresult
99
MessageLoopIdleTask::Init(uint32_t aEnsureRunsAfterMS)
100
0
{
101
0
  RefPtr<MessageLoopTimerCallback> callback =
102
0
    new MessageLoopTimerCallback(this);
103
0
  return NS_NewTimerWithCallback(getter_AddRefs(mTimer),
104
0
                                 callback, aEnsureRunsAfterMS,
105
0
                                 nsITimer::TYPE_ONE_SHOT);
106
0
}
107
108
NS_IMETHODIMP
109
MessageLoopIdleTask::Run()
110
0
{
111
0
  // Null out our pointers because if Run() was called by the timer, this
112
0
  // object will be kept alive by the MessageLoop until the MessageLoop calls
113
0
  // Run().
114
0
115
0
  if (mTimer) {
116
0
    mTimer->Cancel();
117
0
    mTimer = nullptr;
118
0
  }
119
0
120
0
  if (mTask) {
121
0
    mTask->Run();
122
0
    mTask = nullptr;
123
0
  }
124
0
125
0
  return NS_OK;
126
0
}
127
128
MessageLoopTimerCallback::MessageLoopTimerCallback(MessageLoopIdleTask* aTask)
129
  : mTask(aTask)
130
0
{
131
0
}
132
133
NS_IMETHODIMP
134
MessageLoopTimerCallback::Notify(nsITimer* aTimer)
135
0
{
136
0
  // We don't expect to hit the case when the timer fires but mTask has been
137
0
  // deleted, because mTask should cancel the timer before the mTask is
138
0
  // deleted.  But you never know...
139
0
  NS_WARNING_ASSERTION(mTask, "This timer shouldn't have fired.");
140
0
141
0
  if (mTask) {
142
0
    mTask->Run();
143
0
  }
144
0
  return NS_OK;
145
0
}
146
147
NS_IMPL_ISUPPORTS(MessageLoopTimerCallback, nsITimerCallback, nsINamed)
148
149
} // namespace
150
151
NS_IMPL_ISUPPORTS(nsMessageLoop, nsIMessageLoop)
152
153
NS_IMETHODIMP
154
nsMessageLoop::PostIdleTask(nsIRunnable* aTask, uint32_t aEnsureRunsAfterMS)
155
0
{
156
0
  // The message loop owns MessageLoopIdleTask and deletes it after calling
157
0
  // Run().  Be careful...
158
0
  RefPtr<MessageLoopIdleTask> idle =
159
0
    new MessageLoopIdleTask(aTask, aEnsureRunsAfterMS);
160
0
  MessageLoop::current()->PostIdleTask(idle.forget());
161
0
162
0
  return NS_OK;
163
0
}
164
165
nsresult
166
nsMessageLoopConstructor(nsISupports* aOuter,
167
                         const nsIID& aIID,
168
                         void** aInstancePtr)
169
0
{
170
0
  if (NS_WARN_IF(aOuter)) {
171
0
    return NS_ERROR_NO_AGGREGATION;
172
0
  }
173
0
  nsISupports* messageLoop = new nsMessageLoop();
174
0
  return messageLoop->QueryInterface(aIID, aInstancePtr);
175
0
}