Serenity Operating System
at master 232 lines 7.2 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/Singleton.h> 8#include <AK/Time.h> 9#include <Kernel/Scheduler.h> 10#include <Kernel/Sections.h> 11#include <Kernel/Time/TimeManagement.h> 12#include <Kernel/TimerQueue.h> 13 14namespace Kernel { 15 16static Singleton<TimerQueue> s_the; 17static Spinlock<LockRank::None> g_timerqueue_lock {}; 18 19Time Timer::remaining() const 20{ 21 return m_remaining; 22} 23 24Time Timer::now(bool is_firing) const 25{ 26 // NOTE: If is_firing is true then TimePrecision::Precise isn't really useful here. 27 // We already have a quite precise time stamp because we just updated the time in the 28 // interrupt handler. In those cases, just use coarse timestamps. 29 auto clock_id = m_clock_id; 30 if (is_firing) { 31 switch (clock_id) { 32 case CLOCK_MONOTONIC: 33 clock_id = CLOCK_MONOTONIC_COARSE; 34 break; 35 case CLOCK_MONOTONIC_RAW: 36 // TODO: use a special CLOCK_MONOTONIC_RAW_COARSE like mechanism here 37 break; 38 case CLOCK_REALTIME: 39 clock_id = CLOCK_REALTIME_COARSE; 40 break; 41 default: 42 break; 43 } 44 } 45 return TimeManagement::the().current_time(clock_id); 46} 47 48TimerQueue& TimerQueue::the() 49{ 50 return *s_the; 51} 52 53UNMAP_AFTER_INIT TimerQueue::TimerQueue() 54{ 55 m_ticks_per_second = TimeManagement::the().ticks_per_second(); 56} 57 58bool TimerQueue::add_timer_without_id(NonnullLockRefPtr<Timer> timer, clockid_t clock_id, Time const& deadline, Function<void()>&& callback) 59{ 60 if (deadline <= TimeManagement::the().current_time(clock_id)) 61 return false; 62 63 // Because timer handlers can execute on any processor and there is 64 // a race between executing a timer handler and cancel_timer() this 65 // *must* be a LockRefPtr<Timer>. Otherwise, calling cancel_timer() could 66 // inadvertently cancel another timer that has been created between 67 // returning from the timer handler and a call to cancel_timer(). 68 timer->setup(clock_id, deadline, move(callback)); 69 70 SpinlockLocker lock(g_timerqueue_lock); 71 timer->m_id = 0; // Don't generate a timer id 72 add_timer_locked(move(timer)); 73 return true; 74} 75 76TimerId TimerQueue::add_timer(NonnullLockRefPtr<Timer>&& timer) 77{ 78 SpinlockLocker lock(g_timerqueue_lock); 79 80 timer->m_id = ++m_timer_id_count; 81 VERIFY(timer->m_id != 0); // wrapped 82 auto id = timer->m_id; 83 add_timer_locked(move(timer)); 84 return id; 85} 86 87void TimerQueue::add_timer_locked(NonnullLockRefPtr<Timer> timer) 88{ 89 Time timer_expiration = timer->m_expires; 90 91 timer->clear_cancelled(); 92 timer->clear_callback_finished(); 93 timer->set_in_use(); 94 95 auto& queue = queue_for_timer(*timer); 96 if (queue.list.is_empty()) { 97 queue.list.append(timer.leak_ref()); 98 queue.next_timer_due = timer_expiration; 99 } else { 100 Timer* following_timer = nullptr; 101 for (auto& t : queue.list) { 102 if (t.m_expires > timer_expiration) { 103 following_timer = &t; 104 break; 105 } 106 } 107 if (following_timer) { 108 bool next_timer_needs_update = queue.list.first() == following_timer; 109 queue.list.insert_before(*following_timer, timer.leak_ref()); 110 if (next_timer_needs_update) 111 queue.next_timer_due = timer_expiration; 112 } else { 113 queue.list.append(timer.leak_ref()); 114 } 115 } 116} 117 118bool TimerQueue::cancel_timer(Timer& timer, bool* was_in_use) 119{ 120 bool in_use = timer.is_in_use(); 121 if (was_in_use) 122 *was_in_use = in_use; 123 124 // If the timer isn't in use, the cancellation is a no-op. 125 if (!in_use) { 126 VERIFY(!timer.m_list_node.is_in_list()); 127 return false; 128 } 129 130 bool did_already_run = timer.set_cancelled(); 131 auto& timer_queue = queue_for_timer(timer); 132 if (!did_already_run) { 133 timer.clear_in_use(); 134 135 SpinlockLocker lock(g_timerqueue_lock); 136 if (timer_queue.list.contains(timer)) { 137 // The timer has not fired, remove it 138 VERIFY(timer.ref_count() > 1); 139 remove_timer_locked(timer_queue, timer); 140 return true; 141 } 142 143 // The timer was queued to execute but hasn't had a chance 144 // to run. In this case, it should still be in m_timers_executing 145 // and we don't need to spin. It still holds a reference 146 // that will be dropped when it does get a chance to run, 147 // but since we called set_cancelled it will only drop its reference 148 VERIFY(m_timers_executing.contains(timer)); 149 m_timers_executing.remove(timer); 150 return true; 151 } 152 153 // At this point the deferred call is queued and is being executed 154 // on another processor. We need to wait until it's complete! 155 while (!timer.is_callback_finished()) 156 Processor::wait_check(); 157 158 return false; 159} 160 161void TimerQueue::remove_timer_locked(Queue& queue, Timer& timer) 162{ 163 bool was_next_timer = (queue.list.first() == &timer); 164 queue.list.remove(timer); 165 auto now = timer.now(false); 166 if (timer.m_expires > now) 167 timer.m_remaining = timer.m_expires - now; 168 169 if (was_next_timer) 170 update_next_timer_due(queue); 171 // Whenever we remove a timer that was still queued (but hasn't been 172 // fired) we added a reference to it. So, when removing it from the 173 // queue we need to drop that reference. 174 timer.unref(); 175} 176 177void TimerQueue::fire() 178{ 179 SpinlockLocker lock(g_timerqueue_lock); 180 181 auto fire_timers = [&](Queue& queue) { 182 auto* timer = queue.list.first(); 183 VERIFY(timer); 184 VERIFY(queue.next_timer_due == timer->m_expires); 185 186 while (timer && timer->now(true) > timer->m_expires) { 187 queue.list.remove(*timer); 188 189 m_timers_executing.append(*timer); 190 191 update_next_timer_due(queue); 192 193 lock.unlock(); 194 195 // Defer executing the timer outside of the irq handler 196 Processor::deferred_call_queue([this, timer]() { 197 // Check if we were cancelled in between being triggered 198 // by the timer irq handler and now. If so, just drop 199 // our reference and don't execute the callback. 200 if (!timer->set_cancelled()) { 201 timer->m_callback(); 202 SpinlockLocker lock(g_timerqueue_lock); 203 m_timers_executing.remove(*timer); 204 } 205 timer->clear_in_use(); 206 timer->set_callback_finished(); 207 // Drop the reference we added when queueing the timer 208 timer->unref(); 209 }); 210 211 lock.lock(); 212 timer = queue.list.first(); 213 } 214 }; 215 216 if (!m_timer_queue_monotonic.list.is_empty()) 217 fire_timers(m_timer_queue_monotonic); 218 if (!m_timer_queue_realtime.list.is_empty()) 219 fire_timers(m_timer_queue_realtime); 220} 221 222void TimerQueue::update_next_timer_due(Queue& queue) 223{ 224 VERIFY(g_timerqueue_lock.is_locked()); 225 226 if (auto* next_timer = queue.list.first()) 227 queue.next_timer_due = next_timer->m_expires; 228 else 229 queue.next_timer_due = {}; 230} 231 232}