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:
parent
c6230b746d
commit
601a688b6f
3 changed files with 143 additions and 53 deletions
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue