|
@@ -61,7 +61,7 @@ RefPtr<Timer> TimerQueue::add_timer_without_id(const timespec& deadline, Functio
|
|
|
auto timer = adopt(*new Timer(time_to_ticks(deadline), move(callback)));
|
|
|
|
|
|
ScopedSpinLock lock(g_timerqueue_lock);
|
|
|
- timer->id = 0; // Don't generate a timer id
|
|
|
+ timer->m_id = 0; // Don't generate a timer id
|
|
|
add_timer_locked(timer);
|
|
|
return timer;
|
|
|
}
|
|
@@ -70,31 +70,38 @@ TimerId TimerQueue::add_timer(NonnullRefPtr<Timer>&& timer)
|
|
|
{
|
|
|
ScopedSpinLock lock(g_timerqueue_lock);
|
|
|
|
|
|
- timer->id = ++m_timer_id_count;
|
|
|
- ASSERT(timer->id != 0); // wrapped
|
|
|
+ timer->m_id = ++m_timer_id_count;
|
|
|
+ ASSERT(timer->m_id != 0); // wrapped
|
|
|
add_timer_locked(move(timer));
|
|
|
return m_timer_id_count;
|
|
|
}
|
|
|
|
|
|
void TimerQueue::add_timer_locked(NonnullRefPtr<Timer> timer)
|
|
|
{
|
|
|
- u64 timer_expiration = timer->expires;
|
|
|
+ u64 timer_expiration = timer->m_expires;
|
|
|
ASSERT(timer_expiration >= time_to_ticks(TimeManagement::the().monotonic_time()));
|
|
|
|
|
|
+ ASSERT(!timer->is_queued());
|
|
|
+
|
|
|
if (m_timer_queue.is_empty()) {
|
|
|
- m_timer_queue.append(move(timer));
|
|
|
+ m_timer_queue.append(&timer.leak_ref());
|
|
|
m_next_timer_due = timer_expiration;
|
|
|
} else {
|
|
|
- auto following_timer = m_timer_queue.find([&timer_expiration](auto& other) { return other->expires > timer_expiration; });
|
|
|
-
|
|
|
- if (following_timer.is_end()) {
|
|
|
- m_timer_queue.append(move(timer));
|
|
|
- } else {
|
|
|
- auto next_timer_needs_update = following_timer.is_begin();
|
|
|
- m_timer_queue.insert_before(following_timer, move(timer));
|
|
|
-
|
|
|
+ Timer* following_timer = nullptr;
|
|
|
+ m_timer_queue.for_each([&](Timer& t) {
|
|
|
+ if (t.m_expires > timer_expiration) {
|
|
|
+ following_timer = &t;
|
|
|
+ return IterationDecision::Break;
|
|
|
+ }
|
|
|
+ return IterationDecision::Continue;
|
|
|
+ });
|
|
|
+ if (following_timer) {
|
|
|
+ bool next_timer_needs_update = m_timer_queue.head() == following_timer;
|
|
|
+ m_timer_queue.insert_before(following_timer, &timer.leak_ref());
|
|
|
if (next_timer_needs_update)
|
|
|
m_next_timer_due = timer_expiration;
|
|
|
+ } else {
|
|
|
+ m_timer_queue.append(&timer.leak_ref());
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -125,51 +132,106 @@ u64 TimerQueue::time_to_ticks(const timespec& tspec) const
|
|
|
bool TimerQueue::cancel_timer(TimerId id)
|
|
|
{
|
|
|
ScopedSpinLock lock(g_timerqueue_lock);
|
|
|
- auto it = m_timer_queue.find([id](auto& timer) { return timer->id == id; });
|
|
|
- if (it.is_end())
|
|
|
+ Timer* found_timer = nullptr;
|
|
|
+ if (m_timer_queue.for_each([&](Timer& timer) {
|
|
|
+ if (timer.m_id == id) {
|
|
|
+ found_timer = &timer;
|
|
|
+ return IterationDecision::Break;
|
|
|
+ }
|
|
|
+ return IterationDecision::Continue;
|
|
|
+ })
|
|
|
+ != IterationDecision::Break) {
|
|
|
+ // The timer may be executing right now, if it is then it should
|
|
|
+ // be in m_timers_executing. If it is then release the lock
|
|
|
+ // briefly to allow it to finish by removing itself
|
|
|
+ // NOTE: This can only happen with multiple processors!
|
|
|
+ while (m_timers_executing.for_each([&](Timer& timer) {
|
|
|
+ if (timer.m_id == id)
|
|
|
+ return IterationDecision::Break;
|
|
|
+ return IterationDecision::Continue;
|
|
|
+ }) == IterationDecision::Break) {
|
|
|
+ // NOTE: This isn't the most efficient way to wait, but
|
|
|
+ // it should only happen when multiple processors are used.
|
|
|
+ // Also, the timers should execute pretty quickly, so it
|
|
|
+ // should not loop here for very long. But we can't yield.
|
|
|
+ lock.unlock();
|
|
|
+ Processor::wait_check();
|
|
|
+ lock.lock();
|
|
|
+ }
|
|
|
+ // We were not able to cancel the timer, but at this point
|
|
|
+ // the handler should have completed if it was running!
|
|
|
return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ ASSERT(found_timer);
|
|
|
+ bool was_next_timer = (m_timer_queue.head() == found_timer);
|
|
|
|
|
|
- auto was_next_timer = it.is_begin();
|
|
|
- m_timer_queue.remove(it);
|
|
|
+ m_timer_queue.remove(found_timer);
|
|
|
+ found_timer->set_queued(false);
|
|
|
|
|
|
if (was_next_timer)
|
|
|
update_next_timer_due();
|
|
|
|
|
|
+ lock.unlock();
|
|
|
+ found_timer->unref();
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
-bool TimerQueue::cancel_timer(const NonnullRefPtr<Timer>& timer)
|
|
|
+bool TimerQueue::cancel_timer(Timer& timer)
|
|
|
{
|
|
|
ScopedSpinLock lock(g_timerqueue_lock);
|
|
|
- auto it = m_timer_queue.find([timer](auto& t) { return t.ptr() == timer.ptr(); });
|
|
|
- if (it.is_end())
|
|
|
+ if (!m_timer_queue.contains_slow(&timer)) {
|
|
|
+ // The timer may be executing right now, if it is then it should
|
|
|
+ // be in m_timers_executing. If it is then release the lock
|
|
|
+ // briefly to allow it to finish by removing itself
|
|
|
+ // NOTE: This can only happen with multiple processors!
|
|
|
+ while (m_timers_executing.contains_slow(&timer)) {
|
|
|
+ // NOTE: This isn't the most efficient way to wait, but
|
|
|
+ // it should only happen when multiple processors are used.
|
|
|
+ // Also, the timers should execute pretty quickly, so it
|
|
|
+ // should not loop here for very long. But we can't yield.
|
|
|
+ lock.unlock();
|
|
|
+ Processor::wait_check();
|
|
|
+ lock.lock();
|
|
|
+ }
|
|
|
+ // We were not able to cancel the timer, but at this point
|
|
|
+ // the handler should have completed if it was running!
|
|
|
return false;
|
|
|
+ }
|
|
|
|
|
|
- auto was_next_timer = it.is_begin();
|
|
|
- m_timer_queue.remove(it);
|
|
|
+ bool was_next_timer = (m_timer_queue.head() == &timer);
|
|
|
+ m_timer_queue.remove(&timer);
|
|
|
+ timer.set_queued(false);
|
|
|
|
|
|
if (was_next_timer)
|
|
|
update_next_timer_due();
|
|
|
-
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
void TimerQueue::fire()
|
|
|
{
|
|
|
ScopedSpinLock lock(g_timerqueue_lock);
|
|
|
- if (m_timer_queue.is_empty())
|
|
|
+ auto* timer = m_timer_queue.head();
|
|
|
+ if (!timer)
|
|
|
return;
|
|
|
|
|
|
- ASSERT(m_next_timer_due == m_timer_queue.first()->expires);
|
|
|
+ ASSERT(m_next_timer_due == timer->m_expires);
|
|
|
|
|
|
- while (!m_timer_queue.is_empty() && TimeManagement::the().monotonic_ticks() > m_timer_queue.first()->expires) {
|
|
|
- auto timer = m_timer_queue.take_first();
|
|
|
+ while (timer && TimeManagement::the().monotonic_ticks() > timer->m_expires) {
|
|
|
+ m_timer_queue.remove(timer);
|
|
|
+ m_timers_executing.append(timer);
|
|
|
|
|
|
update_next_timer_due();
|
|
|
|
|
|
lock.unlock();
|
|
|
- timer->callback();
|
|
|
+ timer->m_callback();
|
|
|
lock.lock();
|
|
|
+
|
|
|
+ m_timers_executing.remove(timer);
|
|
|
+ timer->set_queued(false);
|
|
|
+ timer->unref();
|
|
|
+
|
|
|
+ timer = m_timer_queue.head();
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -177,10 +239,10 @@ void TimerQueue::update_next_timer_due()
|
|
|
{
|
|
|
ASSERT(g_timerqueue_lock.is_locked());
|
|
|
|
|
|
- if (m_timer_queue.is_empty())
|
|
|
- m_next_timer_due = 0;
|
|
|
+ if (auto* next_timer = m_timer_queue.head())
|
|
|
+ m_next_timer_due = next_timer->m_expires;
|
|
|
else
|
|
|
- m_next_timer_due = m_timer_queue.first()->expires;
|
|
|
+ m_next_timer_due = 0;
|
|
|
}
|
|
|
|
|
|
}
|