Unreviewed, rolling out r235784.
[WebKit-https.git] / Source / WTF / wtf / generic / RunLoopGeneric.cpp
1 /*
2  * Copyright (C) 2016 Konstantin Tokavev <annulen@yandex.ru>
3  * Copyright (C) 2016 Yusuke Suzuki <utatane.tea@gmail.com>
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 "RunLoop.h"
29
30 namespace WTF {
31
32 class RunLoop::TimerBase::ScheduledTask : public ThreadSafeRefCounted<ScheduledTask> {
33 WTF_MAKE_NONCOPYABLE(ScheduledTask);
34 public:
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
92 private:
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
100 RunLoop::RunLoop()
101 {
102 }
103
104 RunLoop::~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
115 inline 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
153 void 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
182 void RunLoop::run()
183 {
184     RunLoop::current().runImpl(RunMode::Drain);
185 }
186
187 void 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
196 void 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
209 void RunLoop::wakeUp(const AbstractLocker&)
210 {
211     m_pendingTasks = true;
212     m_readyToRun.notifyOne();
213 }
214
215 void RunLoop::wakeUp()
216 {
217     LockHolder locker(m_loopLock);
218     wakeUp(locker);
219 }
220
221 void RunLoop::schedule(const AbstractLocker&, Ref<TimerBase::ScheduledTask>&& task)
222 {
223     m_schedules.append(task.ptr());
224     std::push_heap(m_schedules.begin(), m_schedules.end(), TimerBase::ScheduledTask::EarliestSchedule());
225 }
226
227 void RunLoop::schedule(Ref<TimerBase::ScheduledTask>&& task)
228 {
229     LockHolder locker(m_loopLock);
230     schedule(locker, WTFMove(task));
231 }
232
233 void RunLoop::scheduleAndWakeUp(const AbstractLocker& locker, Ref<TimerBase::ScheduledTask>&& task)
234 {
235     schedule(locker, WTFMove(task));
236     wakeUp(locker);
237 }
238
239 void RunLoop::dispatchAfter(Seconds delay, Function<void()>&& function)
240 {
241     LockHolder locker(m_loopLock);
242     bool repeating = false;
243     schedule(locker, TimerBase::ScheduledTask::create(WTFMove(function), delay, repeating));
244     wakeUp(locker);
245 }
246
247 // Since RunLoop does not own the registered TimerBase,
248 // TimerBase and its owner should manage these lifetime.
249 RunLoop::TimerBase::TimerBase(RunLoop& runLoop)
250     : m_runLoop(runLoop)
251     , m_scheduledTask(nullptr)
252 {
253 }
254
255 RunLoop::TimerBase::~TimerBase()
256 {
257     LockHolder locker(m_runLoop->m_loopLock);
258     stop(locker);
259 }
260
261 void RunLoop::TimerBase::start(Seconds interval, bool repeating)
262 {
263     LockHolder locker(m_runLoop->m_loopLock);
264     stop(locker);
265     m_scheduledTask = ScheduledTask::create([this] {
266         fired();
267     }, interval, repeating);
268     m_runLoop->scheduleAndWakeUp(locker, *m_scheduledTask);
269 }
270
271 void RunLoop::TimerBase::stop(const AbstractLocker&)
272 {
273     if (m_scheduledTask) {
274         m_scheduledTask->deactivate();
275         m_scheduledTask = nullptr;
276     }
277 }
278
279 void RunLoop::TimerBase::stop()
280 {
281     LockHolder locker(m_runLoop->m_loopLock);
282     stop(locker);
283 }
284
285 bool RunLoop::TimerBase::isActive() const
286 {
287     LockHolder locker(m_runLoop->m_loopLock);
288     return isActive(locker);
289 }
290
291 bool RunLoop::TimerBase::isActive(const AbstractLocker&) const
292 {
293     return m_scheduledTask;
294 }
295
296 Seconds RunLoop::TimerBase::secondsUntilFire() const
297 {
298     LockHolder locker(m_runLoop->m_loopLock);
299     if (isActive(locker))
300         return std::max<Seconds>(m_scheduledTask->scheduledTimePoint() - MonotonicTime::now(), 0_s);
301     return 0_s;
302 }
303
304 } // namespace WTF