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