1/*
2 * Copyright (C) 2016 Konstantin Tokavev <[email protected]>
3 * Copyright (C) 2016 Yusuke Suzuki <[email protected]>
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include <wtf/RunLoop.h>
29
30namespace WTF {
31
32class RunLoop::TimerBase::ScheduledTask : public ThreadSafeRefCounted<ScheduledTask> {
33WTF_MAKE_NONCOPYABLE(ScheduledTask);
34public:
35 static Ref<ScheduledTask> create(Function<void()>&& function, Seconds interval, bool repeating)
36 {
37 return adoptRef(*new ScheduledTask(WTFMove(function), interval, repeating));
38 }
39
40 ScheduledTask(Function<void()>&& function, Seconds interval, bool repeating)
41 : m_function(WTFMove(function))
42 , m_fireInterval(interval)
43 , m_isRepeating(repeating)
44 {
45 updateReadyTime();
46 }
47
48 bool fired()
49 {
50 if (!isActive())
51 return false;
52
53 m_function();
54
55 if (!m_isRepeating)
56 return false;
57
58 updateReadyTime();
59 return isActive();
60 }
61
62 MonotonicTime scheduledTimePoint() const
63 {
64 return m_scheduledTimePoint;
65 }
66
67 void updateReadyTime()
68 {
69 m_scheduledTimePoint = MonotonicTime::now();
70 if (!m_fireInterval)
71 return;
72 m_scheduledTimePoint += m_fireInterval;
73 }
74
75 struct EarliestSchedule {
76 bool operator()(const RefPtr<ScheduledTask>& lhs, const RefPtr<ScheduledTask>& rhs)
77 {
78 return lhs->scheduledTimePoint() > rhs->scheduledTimePoint();
79 }
80 };
81
82 bool isActive() const
83 {
84 return m_isActive.load();
85 }
86
87 void deactivate()
88 {
89 m_isActive.store(false);
90 }
91
92private:
93 Function<void ()> m_function;
94 MonotonicTime m_scheduledTimePoint;
95 Seconds m_fireInterval;
96 std::atomic<bool> m_isActive { true };
97 bool m_isRepeating;
98};
99
100RunLoop::RunLoop()
101{
102}
103
104RunLoop::~RunLoop()
105{
106 LockHolder locker(m_loopLock);
107 m_shutdown = true;
108 m_readyToRun.notifyOne();
109
110 // Here is running main loops. Wait until all the main loops are destroyed.
111 if (!m_mainLoops.isEmpty())
112 m_stopCondition.wait(m_loopLock);
113}
114
115inline bool RunLoop::populateTasks(RunMode runMode, Status& statusOfThisLoop, Deque<RefPtr<TimerBase::ScheduledTask>>& firedTimers)
116{
117 LockHolder locker(m_loopLock);
118
119 if (runMode == RunMode::Drain) {
120 MonotonicTime sleepUntil = MonotonicTime::infinity();
121 if (!m_schedules.isEmpty())
122 sleepUntil = m_schedules.first()->scheduledTimePoint();
123
124 m_readyToRun.waitUntil(m_loopLock, sleepUntil, [&] {
125 return m_shutdown || m_pendingTasks || statusOfThisLoop == Status::Stopping;
126 });
127 }
128
129 if (statusOfThisLoop == Status::Stopping || m_shutdown) {
130 m_mainLoops.removeLast();
131 if (m_mainLoops.isEmpty())
132 m_stopCondition.notifyOne();
133 return false;
134 }
135 m_pendingTasks = false;
136 if (runMode == RunMode::Iterate)
137 statusOfThisLoop = Status::Stopping;
138
139 // Check expired timers.
140 MonotonicTime now = MonotonicTime::now();
141 while (!m_schedules.isEmpty()) {
142 RefPtr<TimerBase::ScheduledTask> earliest = m_schedules.first();
143 if (earliest->scheduledTimePoint() > now)
144 break;
145 std::pop_heap(m_schedules.begin(), m_schedules.end(), TimerBase::ScheduledTask::EarliestSchedule());
146 m_schedules.removeLast();
147 firedTimers.append(WTFMove(earliest));
148 }
149
150 return true;
151}
152
153void RunLoop::runImpl(RunMode runMode)
154{
155 ASSERT(this == &RunLoop::current());
156
157 Status statusOfThisLoop = Status::Clear;
158 {
159 LockHolder locker(m_loopLock);
160 m_mainLoops.append(&statusOfThisLoop);
161 }
162
163 Deque<RefPtr<TimerBase::ScheduledTask>> firedTimers;
164 while (true) {
165 if (!populateTasks(runMode, statusOfThisLoop, firedTimers))
166 return;
167
168 // Dispatch scheduled timers.
169 while (!firedTimers.isEmpty()) {
170 RefPtr<TimerBase::ScheduledTask> task = firedTimers.takeFirst();
171 if (task->fired()) {
172 // Reschedule because the timer requires repeating.
173 // Since we will query the timers' time points before sleeping,
174 // we do not call wakeUp() here.
175 schedule(*task);
176 }
177 }
178 performWork();
179 }
180}
181
182void RunLoop::run()
183{
184 RunLoop::current().runImpl(RunMode::Drain);
185}
186
187void RunLoop::iterate()
188{
189 RunLoop::current().runImpl(RunMode::Iterate);
190}
191
192// RunLoop operations are thread-safe. These operations can be called from outside of the RunLoop's thread.
193// For example, WorkQueue::{dispatch, dispatchAfter} call the operations of the WorkQueue thread's RunLoop
194// from the caller's thread.
195
196void RunLoop::stop()
197{
198 LockHolder locker(m_loopLock);
199 if (m_mainLoops.isEmpty())
200 return;
201
202 Status* status = m_mainLoops.last();
203 if (*status != Status::Stopping) {
204 *status = Status::Stopping;
205 m_readyToRun.notifyOne();
206 }
207}
208
209void RunLoop::wakeUp(const AbstractLocker&)
210{
211 m_pendingTasks = true;
212 m_readyToRun.notifyOne();
213}
214
215void RunLoop::wakeUp()
216{
217 LockHolder locker(m_loopLock);
218 wakeUp(locker);
219}
220
221RunLoop::CycleResult RunLoop::cycle(RunLoopMode)
222{
223 iterate();
224}
225
226void RunLoop::schedule(const AbstractLocker&, Ref<TimerBase::ScheduledTask>&& task)
227{
228 m_schedules.append(task.ptr());
229 std::push_heap(m_schedules.begin(), m_schedules.end(), TimerBase::ScheduledTask::EarliestSchedule());
230}
231
232void RunLoop::schedule(Ref<TimerBase::ScheduledTask>&& task)
233{
234 LockHolder locker(m_loopLock);
235 schedule(locker, WTFMove(task));
236}
237
238void RunLoop::scheduleAndWakeUp(const AbstractLocker& locker, Ref<TimerBase::ScheduledTask>&& task)
239{
240 schedule(locker, WTFMove(task));
241 wakeUp(locker);
242}
243
244void RunLoop::dispatchAfter(Seconds delay, Function<void()>&& function)
245{
246 LockHolder locker(m_loopLock);
247 bool repeating = false;
248 schedule(locker, TimerBase::ScheduledTask::create(WTFMove(function), delay, repeating));
249 wakeUp(locker);
250}
251
252// Since RunLoop does not own the registered TimerBase,
253// TimerBase and its owner should manage these lifetime.
254RunLoop::TimerBase::TimerBase(RunLoop& runLoop)
255 : m_runLoop(runLoop)
256 , m_scheduledTask(nullptr)
257{
258}
259
260RunLoop::TimerBase::~TimerBase()
261{
262 LockHolder locker(m_runLoop->m_loopLock);
263 stop(locker);
264}
265
266void RunLoop::TimerBase::start(Seconds interval, bool repeating)
267{
268 LockHolder locker(m_runLoop->m_loopLock);
269 stop(locker);
270 m_scheduledTask = ScheduledTask::create([this] {
271 fired();
272 }, interval, repeating);
273 m_runLoop->scheduleAndWakeUp(locker, *m_scheduledTask);
274}
275
276void RunLoop::TimerBase::stop(const AbstractLocker&)
277{
278 if (m_scheduledTask) {
279 m_scheduledTask->deactivate();
280 m_scheduledTask = nullptr;
281 }
282}
283
284void RunLoop::TimerBase::stop()
285{
286 LockHolder locker(m_runLoop->m_loopLock);
287 stop(locker);
288}
289
290bool RunLoop::TimerBase::isActive() const
291{
292 LockHolder locker(m_runLoop->m_loopLock);
293 return isActive(locker);
294}
295
296bool RunLoop::TimerBase::isActive(const AbstractLocker&) const
297{
298 return m_scheduledTask;
299}
300
301Seconds RunLoop::TimerBase::secondsUntilFire() const
302{
303 LockHolder locker(m_runLoop->m_loopLock);
304 if (isActive(locker))
305 return std::max<Seconds>(m_scheduledTask->scheduledTimePoint() - MonotonicTime::now(), 0_s);
306 return 0_s;
307}
308
309} // namespace WTF
310