1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "IdleTaskRunner.h"
#include "nsRefreshDriver.h"
#include "nsComponentManagerUtils.h"
namespace mozilla {
already_AddRefed<IdleTaskRunner> IdleTaskRunner::Create(
const CallbackType& aCallback, const char* aRunnableName,
uint32_t aMaxDelay, int64_t aNonIdleBudget, bool aRepeating,
const MayStopProcessingCallbackType& aMayStopProcessing) {
if (aMayStopProcessing && aMayStopProcessing()) {
return nullptr;
}
RefPtr<IdleTaskRunner> runner =
new IdleTaskRunner(aCallback, aRunnableName, aMaxDelay, aNonIdleBudget,
aRepeating, aMayStopProcessing);
runner->Schedule(false); // Initial scheduling shouldn't use idle dispatch.
return runner.forget();
}
IdleTaskRunner::IdleTaskRunner(
const CallbackType& aCallback, const char* aRunnableName,
uint32_t aMaxDelay, int64_t aNonIdleBudget, bool aRepeating,
const MayStopProcessingCallbackType& aMayStopProcessing)
: CancelableIdleRunnable(aRunnableName),
mCallback(aCallback),
mDelay(aMaxDelay),
mBudget(TimeDuration::FromMilliseconds(aNonIdleBudget)),
mRepeating(aRepeating),
mTimerActive(false),
mMayStopProcessing(aMayStopProcessing),
mName(aRunnableName) {}
NS_IMETHODIMP
IdleTaskRunner::Run() {
if (!mCallback) {
return NS_OK;
}
// Deadline is null when called from timer.
TimeStamp now = TimeStamp::Now();
bool deadLineWasNull = mDeadline.IsNull();
bool didRun = false;
bool allowIdleDispatch = false;
if (deadLineWasNull || ((now + mBudget) < mDeadline)) {
CancelTimer();
didRun = mCallback(mDeadline);
// If we didn't do meaningful work, don't schedule using immediate
// idle dispatch, since that could lead to a loop until the idle
// period ends.
allowIdleDispatch = didRun;
} else if (now >= mDeadline) {
allowIdleDispatch = true;
}
if (mCallback && (mRepeating || !didRun)) {
Schedule(allowIdleDispatch);
} else {
mCallback = nullptr;
}
return NS_OK;
}
static void TimedOut(nsITimer* aTimer, void* aClosure) {
RefPtr<IdleTaskRunner> runnable = static_cast<IdleTaskRunner*>(aClosure);
runnable->Run();
}
void IdleTaskRunner::SetDeadline(mozilla::TimeStamp aDeadline) {
mDeadline = aDeadline;
}
void IdleTaskRunner::SetBudget(int64_t aBudget) {
mBudget = TimeDuration::FromMilliseconds(aBudget);
}
void IdleTaskRunner::SetTimer(uint32_t aDelay, nsIEventTarget* aTarget) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aTarget->IsOnCurrentThread());
// aTarget is always the main thread event target provided from
// NS_DispatchToCurrentThreadQueue(). We ignore aTarget here to ensure that
// CollectorRunner always run specifically the main thread.
SetTimerInternal(aDelay);
}
nsresult IdleTaskRunner::Cancel() {
CancelTimer();
mTimer = nullptr;
mScheduleTimer = nullptr;
mCallback = nullptr;
return NS_OK;
}
static void ScheduleTimedOut(nsITimer* aTimer, void* aClosure) {
RefPtr<IdleTaskRunner> runnable = static_cast<IdleTaskRunner*>(aClosure);
runnable->Schedule(true);
}
void IdleTaskRunner::Schedule(bool aAllowIdleDispatch) {
if (!mCallback) {
return;
}
if (mMayStopProcessing && mMayStopProcessing()) {
Cancel();
return;
}
mDeadline = TimeStamp();
TimeStamp now = TimeStamp::Now();
TimeStamp hint = nsRefreshDriver::GetIdleDeadlineHint(now);
if (hint != now) {
// RefreshDriver is ticking, let it schedule the idle dispatch.
nsRefreshDriver::DispatchIdleRunnableAfterTickUnlessExists(this, mDelay);
// Ensure we get called at some point, even if RefreshDriver is stopped.
SetTimerInternal(mDelay);
} else {
// RefreshDriver doesn't seem to be running.
if (aAllowIdleDispatch) {
nsCOMPtr<nsIRunnable> runnable = this;
SetTimerInternal(mDelay);
NS_DispatchToCurrentThreadQueue(runnable.forget(),
EventQueuePriority::Idle);
} else {
if (!mScheduleTimer) {
mScheduleTimer = NS_NewTimer();
if (!mScheduleTimer) {
return;
}
} else {
mScheduleTimer->Cancel();
}
// We weren't allowed to do idle dispatch immediately, do it after a
// short timeout.
mScheduleTimer->InitWithNamedFuncCallback(
ScheduleTimedOut, this, 16 /* ms */,
nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY, mName);
}
}
}
IdleTaskRunner::~IdleTaskRunner() { CancelTimer(); }
void IdleTaskRunner::CancelTimer() {
nsRefreshDriver::CancelIdleRunnable(this);
if (mTimer) {
mTimer->Cancel();
}
if (mScheduleTimer) {
mScheduleTimer->Cancel();
}
mTimerActive = false;
}
void IdleTaskRunner::SetTimerInternal(uint32_t aDelay) {
if (mTimerActive) {
return;
}
if (!mTimer) {
mTimer = NS_NewTimer();
} else {
mTimer->Cancel();
}
if (mTimer) {
mTimer->InitWithNamedFuncCallback(TimedOut, this, aDelay,
nsITimer::TYPE_ONE_SHOT, mName);
mTimerActive = true;
}
}
} // end of namespace mozilla
|