1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 15:47:44 +00:00

Kernel: TimerQueue::cancel_timer needs to wait if timer is executing

We need to be able to guarantee that a timer won't be executing after
TimerQueue::cancel_timer returns. In the case of multiple processors
this means that we may need to wait while the timer handler finishes
execution on another core.

This also fixes a problem in Thread::block and Thread::wait_on where
theoretically the timer could execute after the function returned
and the Thread disappeared.
This commit is contained in:
Tom 2020-12-01 13:02:54 -07:00 committed by Andreas Kling
parent c6230b746d
commit 601a688b6f
3 changed files with 143 additions and 53 deletions

View file

@ -27,41 +27,59 @@
#pragma once
#include <AK/Function.h>
#include <AK/InlineLinkedList.h>
#include <AK/NonnullRefPtr.h>
#include <AK/OwnPtr.h>
#include <AK/RefCounted.h>
#include <AK/SinglyLinkedList.h>
#include <Kernel/Time/TimeManagement.h>
namespace Kernel {
typedef u64 TimerId;
struct Timer : public RefCounted<Timer> {
TimerId id;
u64 expires;
Function<void()> callback;
class Timer : public RefCounted<Timer>
, public InlineLinkedListNode<Timer> {
friend class TimerQueue;
friend class InlineLinkedListNode<Timer>;
public:
Timer(u64 expires, Function<void()>&& callback)
: m_expires(expires)
, m_callback(move(callback))
{
}
~Timer()
{
ASSERT(!is_queued());
}
private:
TimerId m_id;
u64 m_expires;
Function<void()> m_callback;
Timer* m_next { nullptr };
Timer* m_prev { nullptr };
Atomic<bool> m_queued { false };
bool operator<(const Timer& rhs) const
{
return expires < rhs.expires;
return m_expires < rhs.m_expires;
}
bool operator>(const Timer& rhs) const
{
return expires > rhs.expires;
return m_expires > rhs.m_expires;
}
bool operator==(const Timer& rhs) const
{
return id == rhs.id;
}
Timer(u64 expires, Function<void()>&& callback)
: expires(expires)
, callback(move(callback))
{
return m_id == rhs.m_id;
}
bool is_queued() const { return m_queued.load(AK::MemoryOrder::memory_order_relaxed); }
void set_queued(bool queued) { m_queued.store(queued, AK::MemoryOrder::memory_order_relaxed); }
};
class TimerQueue {
friend class Timer;
public:
TimerQueue();
static TimerQueue& the();
@ -70,10 +88,14 @@ public:
RefPtr<Timer> add_timer_without_id(const timespec& timeout, Function<void()>&& callback);
TimerId add_timer(timeval& timeout, Function<void()>&& callback);
bool cancel_timer(TimerId id);
bool cancel_timer(const NonnullRefPtr<Timer>&);
bool cancel_timer(NonnullRefPtr<Timer>&& timer)
{
return cancel_timer(timer.leak_ref());
}
void fire();
private:
bool cancel_timer(Timer&);
void update_next_timer_due();
void add_timer_locked(NonnullRefPtr<Timer>);
@ -83,7 +105,8 @@ private:
u64 m_next_timer_due { 0 };
u64 m_timer_id_count { 0 };
u64 m_ticks_per_second { 0 };
SinglyLinkedList<NonnullRefPtr<Timer>> m_timer_queue;
InlineLinkedList<Timer> m_timer_queue;
InlineLinkedList<Timer> m_timers_executing;
};
}