Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/threads/IdleTaskRunner.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 "IdleTaskRunner.h"
8
#include "nsRefreshDriver.h"
9
#include "mozilla/SystemGroup.h"
10
#include "nsComponentManagerUtils.h"
11
12
namespace mozilla {
13
14
already_AddRefed<IdleTaskRunner>
15
IdleTaskRunner::Create(const CallbackType& aCallback,
16
                       const char* aRunnableName, uint32_t aDelay,
17
                       int64_t aBudget, bool aRepeating,
18
                       const MayStopProcessingCallbackType& aMayStopProcessing,
19
                       TaskCategory aTaskCategory)
20
83
{
21
83
  if (aMayStopProcessing && aMayStopProcessing()) {
22
0
    return nullptr;
23
0
  }
24
83
25
83
  RefPtr<IdleTaskRunner> runner =
26
83
    new IdleTaskRunner(aCallback, aRunnableName, aDelay,
27
83
                       aBudget, aRepeating, aMayStopProcessing,
28
83
                       aTaskCategory);
29
83
  runner->Schedule(false); // Initial scheduling shouldn't use idle dispatch.
30
83
  return runner.forget();
31
83
}
32
33
IdleTaskRunner::IdleTaskRunner(const CallbackType& aCallback,
34
                               const char* aRunnableName,
35
                               uint32_t aDelay, int64_t aBudget,
36
                               bool aRepeating,
37
                               const MayStopProcessingCallbackType& aMayStopProcessing,
38
                               TaskCategory aTaskCategory)
39
  : IdleRunnable(aRunnableName)
40
  , mCallback(aCallback), mDelay(aDelay)
41
  , mBudget(TimeDuration::FromMilliseconds(aBudget))
42
  , mRepeating(aRepeating), mTimerActive(false)
43
  , mMayStopProcessing(aMayStopProcessing)
44
  , mTaskCategory(aTaskCategory)
45
  , mName(aRunnableName)
46
83
{
47
83
}
48
49
NS_IMETHODIMP
50
IdleTaskRunner::Run()
51
0
{
52
0
  if (!mCallback) {
53
0
    return NS_OK;
54
0
  }
55
0
56
0
  // Deadline is null when called from timer.
57
0
  TimeStamp now = TimeStamp::Now();
58
0
  bool deadLineWasNull = mDeadline.IsNull();
59
0
  bool didRun = false;
60
0
  bool allowIdleDispatch = false;
61
0
  if (deadLineWasNull || ((now + mBudget) < mDeadline)) {
62
0
    CancelTimer();
63
0
    didRun = mCallback(mDeadline);
64
0
    // If we didn't do meaningful work, don't schedule using immediate
65
0
    // idle dispatch, since that could lead to a loop until the idle
66
0
    // period ends.
67
0
    allowIdleDispatch = didRun;
68
0
  } else if (now >= mDeadline) {
69
0
    allowIdleDispatch = true;
70
0
  }
71
0
72
0
  if (mCallback && (mRepeating || !didRun)) {
73
0
    Schedule(allowIdleDispatch);
74
0
  } else {
75
0
    mCallback = nullptr;
76
0
  }
77
0
78
0
  return NS_OK;
79
0
}
80
81
static void
82
TimedOut(nsITimer* aTimer, void* aClosure)
83
0
{
84
0
  RefPtr<IdleTaskRunner> runnable = static_cast<IdleTaskRunner*>(aClosure);
85
0
  runnable->Run();
86
0
}
87
88
void
89
IdleTaskRunner::SetDeadline(mozilla::TimeStamp aDeadline)
90
0
{
91
0
  mDeadline = aDeadline;
92
0
};
93
94
void IdleTaskRunner::SetTimer(uint32_t aDelay, nsIEventTarget* aTarget)
95
0
{
96
0
  MOZ_ASSERT(NS_IsMainThread());
97
0
  // aTarget is always the main thread event target provided from
98
0
  // NS_IdleDispatchToCurrentThread(). We ignore aTarget here to ensure that
99
0
  // CollectorRunner always run specifically on SystemGroup::EventTargetFor(
100
0
  // TaskCategory::GarbageCollection) of the main thread.
101
0
  SetTimerInternal(aDelay);
102
0
}
103
104
nsresult
105
IdleTaskRunner::Cancel()
106
82
{
107
82
  CancelTimer();
108
82
  mTimer = nullptr;
109
82
  mScheduleTimer = nullptr;
110
82
  mCallback = nullptr;
111
82
  return NS_OK;
112
82
}
113
114
static void
115
ScheduleTimedOut(nsITimer* aTimer, void* aClosure)
116
0
{
117
0
  RefPtr<IdleTaskRunner> runnable = static_cast<IdleTaskRunner*>(aClosure);
118
0
  runnable->Schedule(true);
119
0
}
120
121
void
122
IdleTaskRunner::Schedule(bool aAllowIdleDispatch)
123
83
{
124
83
  if (!mCallback) {
125
0
    return;
126
0
  }
127
83
128
83
  if (mMayStopProcessing && mMayStopProcessing()) {
129
0
    Cancel();
130
0
    return;
131
0
  }
132
83
133
83
  mDeadline = TimeStamp();
134
83
  TimeStamp now = TimeStamp::Now();
135
83
  TimeStamp hint = nsRefreshDriver::GetIdleDeadlineHint(now);
136
83
  if (hint != now) {
137
0
    // RefreshDriver is ticking, let it schedule the idle dispatch.
138
0
    nsRefreshDriver::DispatchIdleRunnableAfterTick(this, mDelay);
139
0
    // Ensure we get called at some point, even if RefreshDriver is stopped.
140
0
    SetTimerInternal(mDelay);
141
83
  } else {
142
83
    // RefreshDriver doesn't seem to be running.
143
83
    if (aAllowIdleDispatch) {
144
0
      nsCOMPtr<nsIRunnable> runnable = this;
145
0
      SetTimerInternal(mDelay);
146
0
      NS_IdleDispatchToCurrentThread(runnable.forget());
147
83
    } else {
148
83
      if (!mScheduleTimer) {
149
83
        mScheduleTimer = NS_NewTimer();
150
83
        if (!mScheduleTimer) {
151
0
          return;
152
0
        }
153
0
      } else {
154
0
        mScheduleTimer->Cancel();
155
0
      }
156
83
      if (TaskCategory::Count != mTaskCategory) {
157
83
        mScheduleTimer->SetTarget(SystemGroup::EventTargetFor(mTaskCategory));
158
83
      }
159
83
      // We weren't allowed to do idle dispatch immediately, do it after a
160
83
      // short timeout.
161
83
      mScheduleTimer->InitWithNamedFuncCallback(ScheduleTimedOut, this, 16,
162
83
                                                nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY,
163
83
                                                mName);
164
83
    }
165
83
  }
166
83
}
167
168
IdleTaskRunner::~IdleTaskRunner()
169
82
{
170
82
  CancelTimer();
171
82
}
172
173
void
174
IdleTaskRunner::CancelTimer()
175
164
{
176
164
  nsRefreshDriver::CancelIdleRunnable(this);
177
164
  if (mTimer) {
178
0
    mTimer->Cancel();
179
0
  }
180
164
  if (mScheduleTimer) {
181
82
    mScheduleTimer->Cancel();
182
82
  }
183
164
  mTimerActive = false;
184
164
}
185
186
void
187
IdleTaskRunner::SetTimerInternal(uint32_t aDelay)
188
0
{
189
0
  if (mTimerActive) {
190
0
    return;
191
0
  }
192
0
193
0
  if (!mTimer) {
194
0
    mTimer = NS_NewTimer();
195
0
  } else {
196
0
    mTimer->Cancel();
197
0
  }
198
0
199
0
  if (mTimer) {
200
0
    if (TaskCategory::Count != mTaskCategory) {
201
0
      mTimer->SetTarget(SystemGroup::EventTargetFor(mTaskCategory));
202
0
    }
203
0
    mTimer->InitWithNamedFuncCallback(TimedOut, this, aDelay,
204
0
                                      nsITimer::TYPE_ONE_SHOT,
205
0
                                      mName);
206
0
    mTimerActive = true;
207
0
  }
208
0
}
209
210
} // end of namespace mozilla