mirror of
https://github.com/RGBCube/serenity
synced 2025-07-24 22:17:42 +00:00
Kernel: Add CLOCK_REALTIME support to the TimerQueue
This allows us to use blocking timeouts with either monotonic or real time for all blockers. Which means that clock_nanosleep() now also supports CLOCK_REALTIME. Also, switch alarm() to use CLOCK_REALTIME as per specification.
This commit is contained in:
parent
4c1e27ec65
commit
12cf6f8650
9 changed files with 182 additions and 97 deletions
|
@ -46,9 +46,9 @@ unsigned Process::sys$alarm(unsigned seconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (seconds > 0) {
|
if (seconds > 0) {
|
||||||
auto deadline = TimeManagement::the().monotonic_time(); // TODO: should be using CLOCK_REALTIME
|
auto deadline = TimeManagement::the().current_time(CLOCK_REALTIME).value();
|
||||||
timespec_add(deadline, { seconds, 0 }, deadline);
|
timespec_add(deadline, { seconds, 0 }, deadline);
|
||||||
m_alarm_timer = TimerQueue::the().add_timer_without_id(deadline, [this]() {
|
m_alarm_timer = TimerQueue::the().add_timer_without_id(CLOCK_REALTIME, deadline, [this]() {
|
||||||
(void)send_signal(SIGALRM, nullptr);
|
(void)send_signal(SIGALRM, nullptr);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,20 +34,11 @@ int Process::sys$clock_gettime(clockid_t clock_id, Userspace<timespec*> user_ts)
|
||||||
{
|
{
|
||||||
REQUIRE_PROMISE(stdio);
|
REQUIRE_PROMISE(stdio);
|
||||||
|
|
||||||
timespec ts = {};
|
auto ts = TimeManagement::the().current_time(clock_id);
|
||||||
|
if (ts.is_error())
|
||||||
|
return ts.error();
|
||||||
|
|
||||||
switch (clock_id) {
|
if (!copy_to_user(user_ts, &ts.value()))
|
||||||
case CLOCK_MONOTONIC:
|
|
||||||
ts = TimeManagement::the().monotonic_time();
|
|
||||||
break;
|
|
||||||
case CLOCK_REALTIME:
|
|
||||||
ts = TimeManagement::the().epoch_time();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!copy_to_user(user_ts, &ts))
|
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -88,13 +79,14 @@ int Process::sys$clock_nanosleep(Userspace<const Syscall::SC_clock_nanosleep_par
|
||||||
bool is_absolute = params.flags & TIMER_ABSTIME;
|
bool is_absolute = params.flags & TIMER_ABSTIME;
|
||||||
|
|
||||||
switch (params.clock_id) {
|
switch (params.clock_id) {
|
||||||
case CLOCK_MONOTONIC: {
|
case CLOCK_MONOTONIC:
|
||||||
|
case CLOCK_REALTIME: {
|
||||||
bool was_interrupted;
|
bool was_interrupted;
|
||||||
if (is_absolute) {
|
if (is_absolute) {
|
||||||
was_interrupted = Thread::current()->sleep_until(requested_sleep).was_interrupted();
|
was_interrupted = Thread::current()->sleep_until(params.clock_id, requested_sleep).was_interrupted();
|
||||||
} else {
|
} else {
|
||||||
timespec remaining_sleep;
|
timespec remaining_sleep;
|
||||||
was_interrupted = Thread::current()->sleep(requested_sleep, &remaining_sleep).was_interrupted();
|
was_interrupted = Thread::current()->sleep(params.clock_id, requested_sleep, &remaining_sleep).was_interrupted();
|
||||||
if (was_interrupted && params.remaining_sleep && !copy_to_user(params.remaining_sleep, &remaining_sleep))
|
if (was_interrupted && params.remaining_sleep && !copy_to_user(params.remaining_sleep, &remaining_sleep))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
}
|
}
|
||||||
|
|
|
@ -271,16 +271,16 @@ void Thread::relock_process(bool did_unlock)
|
||||||
Processor::current().restore_critical(prev_crit, prev_flags);
|
Processor::current().restore_critical(prev_crit, prev_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Thread::sleep(const timespec& duration, timespec* remaining_time) -> BlockResult
|
auto Thread::sleep(clockid_t clock_id, const timespec& duration, timespec* remaining_time) -> BlockResult
|
||||||
{
|
{
|
||||||
ASSERT(state() == Thread::Running);
|
ASSERT(state() == Thread::Running);
|
||||||
return Thread::current()->block<Thread::SleepBlocker>(nullptr, Thread::BlockTimeout(false, &duration), remaining_time);
|
return Thread::current()->block<Thread::SleepBlocker>(nullptr, Thread::BlockTimeout(false, &duration, nullptr, clock_id), remaining_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Thread::sleep_until(const timespec& deadline) -> BlockResult
|
auto Thread::sleep_until(clockid_t clock_id, const timespec& deadline) -> BlockResult
|
||||||
{
|
{
|
||||||
ASSERT(state() == Thread::Running);
|
ASSERT(state() == Thread::Running);
|
||||||
return Thread::current()->block<Thread::SleepBlocker>(nullptr, Thread::BlockTimeout(true, &deadline));
|
return Thread::current()->block<Thread::SleepBlocker>(nullptr, Thread::BlockTimeout(true, &deadline, nullptr, clock_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* Thread::state_string() const
|
const char* Thread::state_string() const
|
||||||
|
@ -1081,7 +1081,7 @@ Thread::BlockResult Thread::wait_on(WaitQueue& queue, const char* reason, const
|
||||||
{
|
{
|
||||||
ScopedSpinLock sched_lock(g_scheduler_lock);
|
ScopedSpinLock sched_lock(g_scheduler_lock);
|
||||||
if (!timeout.is_infinite()) {
|
if (!timeout.is_infinite()) {
|
||||||
timer = TimerQueue::the().add_timer_without_id(timeout.absolute_time(), [&]() {
|
timer = TimerQueue::the().add_timer_without_id(timeout.clock_id(), timeout.absolute_time(), [&]() {
|
||||||
// NOTE: this may execute on the same or any other processor!
|
// NOTE: this may execute on the same or any other processor!
|
||||||
ScopedSpinLock lock(g_scheduler_lock);
|
ScopedSpinLock lock(g_scheduler_lock);
|
||||||
if (!block_finished) {
|
if (!block_finished) {
|
||||||
|
|
|
@ -211,28 +211,30 @@ public:
|
||||||
: m_infinite(true)
|
: m_infinite(true)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
explicit BlockTimeout(bool is_absolute, const timeval* time, const timespec* start_time = nullptr)
|
explicit BlockTimeout(bool is_absolute, const timeval* time, const timespec* start_time = nullptr, clockid_t clock_id = CLOCK_MONOTONIC)
|
||||||
: m_infinite(!time)
|
: m_clock_id(clock_id)
|
||||||
|
, m_infinite(!time)
|
||||||
{
|
{
|
||||||
if (!m_infinite) {
|
if (!m_infinite) {
|
||||||
if (time->tv_sec > 0 || time->tv_usec > 0) {
|
if (time->tv_sec > 0 || time->tv_usec > 0) {
|
||||||
timeval_to_timespec(*time, m_time);
|
timeval_to_timespec(*time, m_time);
|
||||||
m_should_block = true;
|
m_should_block = true;
|
||||||
}
|
}
|
||||||
m_start_time = start_time ? *start_time : TimeManagement::the().monotonic_time();
|
m_start_time = start_time ? *start_time : TimeManagement::the().current_time(clock_id).value();
|
||||||
if (!is_absolute)
|
if (!is_absolute)
|
||||||
timespec_add(m_time, m_start_time, m_time);
|
timespec_add(m_time, m_start_time, m_time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
explicit BlockTimeout(bool is_absolute, const timespec* time, const timespec* start_time = nullptr)
|
explicit BlockTimeout(bool is_absolute, const timespec* time, const timespec* start_time = nullptr, clockid_t clock_id = CLOCK_MONOTONIC)
|
||||||
: m_infinite(!time)
|
: m_clock_id(clock_id)
|
||||||
|
, m_infinite(!time)
|
||||||
{
|
{
|
||||||
if (!m_infinite) {
|
if (!m_infinite) {
|
||||||
if (time->tv_sec > 0 || time->tv_nsec > 0) {
|
if (time->tv_sec > 0 || time->tv_nsec > 0) {
|
||||||
m_time = *time;
|
m_time = *time;
|
||||||
m_should_block = true;
|
m_should_block = true;
|
||||||
}
|
}
|
||||||
m_start_time = start_time ? *start_time : TimeManagement::the().monotonic_time();
|
m_start_time = start_time ? *start_time : TimeManagement::the().current_time(clock_id).value();
|
||||||
if (!is_absolute)
|
if (!is_absolute)
|
||||||
timespec_add(m_time, m_start_time, m_time);
|
timespec_add(m_time, m_start_time, m_time);
|
||||||
}
|
}
|
||||||
|
@ -240,12 +242,14 @@ public:
|
||||||
|
|
||||||
const timespec& absolute_time() const { return m_time; }
|
const timespec& absolute_time() const { return m_time; }
|
||||||
const timespec* start_time() const { return !m_infinite ? &m_start_time : nullptr; }
|
const timespec* start_time() const { return !m_infinite ? &m_start_time : nullptr; }
|
||||||
|
clockid_t clock_id() const { return m_clock_id; }
|
||||||
bool is_infinite() const { return m_infinite; }
|
bool is_infinite() const { return m_infinite; }
|
||||||
bool should_block() const { return m_infinite || m_should_block; };
|
bool should_block() const { return m_infinite || m_should_block; };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
timespec m_time { 0, 0 };
|
timespec m_time { 0, 0 };
|
||||||
timespec m_start_time { 0, 0 };
|
timespec m_start_time { 0, 0 };
|
||||||
|
clockid_t m_clock_id { CLOCK_MONOTONIC };
|
||||||
bool m_infinite { false };
|
bool m_infinite { false };
|
||||||
bool m_should_block { false };
|
bool m_should_block { false };
|
||||||
};
|
};
|
||||||
|
@ -733,7 +737,7 @@ public:
|
||||||
|
|
||||||
auto& block_timeout = t.override_timeout(timeout);
|
auto& block_timeout = t.override_timeout(timeout);
|
||||||
if (!block_timeout.is_infinite()) {
|
if (!block_timeout.is_infinite()) {
|
||||||
m_blocker_timeout = timer = TimerQueue::the().add_timer_without_id(block_timeout.absolute_time(), [&]() {
|
m_blocker_timeout = timer = TimerQueue::the().add_timer_without_id(block_timeout.clock_id(), block_timeout.absolute_time(), [&]() {
|
||||||
// NOTE: this may execute on the same or any other processor!
|
// NOTE: this may execute on the same or any other processor!
|
||||||
ScopedSpinLock scheduler_lock(g_scheduler_lock);
|
ScopedSpinLock scheduler_lock(g_scheduler_lock);
|
||||||
ScopedSpinLock lock(m_lock);
|
ScopedSpinLock lock(m_lock);
|
||||||
|
@ -816,8 +820,16 @@ public:
|
||||||
void unblock_from_blocker(Blocker&);
|
void unblock_from_blocker(Blocker&);
|
||||||
void unblock(u8 signal = 0);
|
void unblock(u8 signal = 0);
|
||||||
|
|
||||||
BlockResult sleep(const timespec&, timespec* = nullptr);
|
BlockResult sleep(clockid_t, const timespec&, timespec* = nullptr);
|
||||||
BlockResult sleep_until(const timespec&);
|
BlockResult sleep(const timespec& duration, timespec* remaining_time = nullptr)
|
||||||
|
{
|
||||||
|
return sleep(CLOCK_MONOTONIC, duration, remaining_time);
|
||||||
|
}
|
||||||
|
BlockResult sleep_until(clockid_t, const timespec&);
|
||||||
|
BlockResult sleep_until(const timespec& duration)
|
||||||
|
{
|
||||||
|
return sleep_until(CLOCK_MONOTONIC, duration);
|
||||||
|
}
|
||||||
|
|
||||||
// Tell this thread to unblock if needed,
|
// Tell this thread to unblock if needed,
|
||||||
// gracefully unwind the stack and die.
|
// gracefully unwind the stack and die.
|
||||||
|
|
|
@ -197,7 +197,7 @@ auto Thread::WriteBlocker::override_timeout(const BlockTimeout& timeout) -> cons
|
||||||
if (description.is_socket()) {
|
if (description.is_socket()) {
|
||||||
auto& socket = *description.socket();
|
auto& socket = *description.socket();
|
||||||
if (socket.has_send_timeout()) {
|
if (socket.has_send_timeout()) {
|
||||||
m_timeout = BlockTimeout(false, &socket.send_timeout(), timeout.start_time());
|
m_timeout = BlockTimeout(false, &socket.send_timeout(), timeout.start_time(), timeout.clock_id());
|
||||||
if (timeout.is_infinite() || (!m_timeout.is_infinite() && m_timeout.absolute_time() < timeout.absolute_time()))
|
if (timeout.is_infinite() || (!m_timeout.is_infinite() && m_timeout.absolute_time() < timeout.absolute_time()))
|
||||||
return m_timeout;
|
return m_timeout;
|
||||||
}
|
}
|
||||||
|
@ -216,7 +216,7 @@ auto Thread::ReadBlocker::override_timeout(const BlockTimeout& timeout) -> const
|
||||||
if (description.is_socket()) {
|
if (description.is_socket()) {
|
||||||
auto& socket = *description.socket();
|
auto& socket = *description.socket();
|
||||||
if (socket.has_receive_timeout()) {
|
if (socket.has_receive_timeout()) {
|
||||||
m_timeout = BlockTimeout(false, &socket.receive_timeout(), timeout.start_time());
|
m_timeout = BlockTimeout(false, &socket.receive_timeout(), timeout.start_time(), timeout.clock_id());
|
||||||
if (timeout.is_infinite() || (!m_timeout.is_infinite() && m_timeout.absolute_time() < timeout.absolute_time()))
|
if (timeout.is_infinite() || (!m_timeout.is_infinite() && m_timeout.absolute_time() < timeout.absolute_time()))
|
||||||
return m_timeout;
|
return m_timeout;
|
||||||
}
|
}
|
||||||
|
@ -256,7 +256,7 @@ void Thread::SleepBlocker::calculate_remaining()
|
||||||
{
|
{
|
||||||
if (!m_remaining)
|
if (!m_remaining)
|
||||||
return;
|
return;
|
||||||
auto time_now = TimeManagement::the().monotonic_time();
|
auto time_now = TimeManagement::the().current_time(m_deadline.clock_id()).value();
|
||||||
if (time_now < m_deadline.absolute_time())
|
if (time_now < m_deadline.absolute_time())
|
||||||
timespec_sub(m_deadline.absolute_time(), time_now, *m_remaining);
|
timespec_sub(m_deadline.absolute_time(), time_now, *m_remaining);
|
||||||
else
|
else
|
||||||
|
|
|
@ -52,6 +52,18 @@ TimeManagement& TimeManagement::the()
|
||||||
return *s_the;
|
return *s_the;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KResultOr<timespec> TimeManagement::current_time(clockid_t clock_id) const
|
||||||
|
{
|
||||||
|
switch (clock_id) {
|
||||||
|
case CLOCK_MONOTONIC:
|
||||||
|
return monotonic_time();
|
||||||
|
case CLOCK_REALTIME:
|
||||||
|
return epoch_time();
|
||||||
|
default:
|
||||||
|
return KResult(EINVAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool TimeManagement::is_system_timer(const HardwareTimerBase& timer) const
|
bool TimeManagement::is_system_timer(const HardwareTimerBase& timer) const
|
||||||
{
|
{
|
||||||
return &timer == m_system_timer.ptr();
|
return &timer == m_system_timer.ptr();
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include <AK/NonnullRefPtrVector.h>
|
#include <AK/NonnullRefPtrVector.h>
|
||||||
#include <AK/RefPtr.h>
|
#include <AK/RefPtr.h>
|
||||||
#include <AK/Types.h>
|
#include <AK/Types.h>
|
||||||
|
#include <Kernel/KResult.h>
|
||||||
#include <Kernel/UnixTypes.h>
|
#include <Kernel/UnixTypes.h>
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
@ -49,6 +50,7 @@ public:
|
||||||
static timespec ticks_to_time(u64 ticks, time_t ticks_per_second);
|
static timespec ticks_to_time(u64 ticks, time_t ticks_per_second);
|
||||||
static u64 time_to_ticks(const timespec& tspec, time_t ticks_per_second);
|
static u64 time_to_ticks(const timespec& tspec, time_t ticks_per_second);
|
||||||
|
|
||||||
|
KResultOr<timespec> current_time(clockid_t clock_id) const;
|
||||||
timespec monotonic_time() const;
|
timespec monotonic_time() const;
|
||||||
timespec epoch_time() const;
|
timespec epoch_time() const;
|
||||||
void set_epoch_time(timespec);
|
void set_epoch_time(timespec);
|
||||||
|
|
|
@ -42,7 +42,12 @@ timespec Timer::remaining() const
|
||||||
{
|
{
|
||||||
if (m_remaining == 0)
|
if (m_remaining == 0)
|
||||||
return {};
|
return {};
|
||||||
return TimerQueue::the().ticks_to_time(m_remaining);
|
return TimerQueue::the().ticks_to_time(m_clock_id, m_remaining);
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 Timer::now() const
|
||||||
|
{
|
||||||
|
return TimerQueue::the().time_to_ticks(m_clock_id, TimeManagement::the().current_time(m_clock_id).value());
|
||||||
}
|
}
|
||||||
|
|
||||||
TimerQueue& TimerQueue::the()
|
TimerQueue& TimerQueue::the()
|
||||||
|
@ -55,9 +60,9 @@ TimerQueue::TimerQueue()
|
||||||
m_ticks_per_second = TimeManagement::the().ticks_per_second();
|
m_ticks_per_second = TimeManagement::the().ticks_per_second();
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<Timer> TimerQueue::add_timer_without_id(const timespec& deadline, Function<void()>&& callback)
|
RefPtr<Timer> TimerQueue::add_timer_without_id(clockid_t clock_id, const timespec& deadline, Function<void()>&& callback)
|
||||||
{
|
{
|
||||||
if (deadline <= TimeManagement::the().monotonic_time())
|
if (deadline <= TimeManagement::the().current_time(clock_id).value())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
// Because timer handlers can execute on any processor and there is
|
// Because timer handlers can execute on any processor and there is
|
||||||
|
@ -65,7 +70,7 @@ RefPtr<Timer> TimerQueue::add_timer_without_id(const timespec& deadline, Functio
|
||||||
// *must* be a RefPtr<Timer>. Otherwise calling cancel_timer() could
|
// *must* be a RefPtr<Timer>. Otherwise calling cancel_timer() could
|
||||||
// inadvertently cancel another timer that has been created between
|
// inadvertently cancel another timer that has been created between
|
||||||
// returning from the timer handler and a call to cancel_timer().
|
// returning from the timer handler and a call to cancel_timer().
|
||||||
auto timer = adopt(*new Timer(time_to_ticks(deadline), move(callback)));
|
auto timer = adopt(*new Timer(clock_id, time_to_ticks(clock_id, deadline), move(callback)));
|
||||||
|
|
||||||
ScopedSpinLock lock(g_timerqueue_lock);
|
ScopedSpinLock lock(g_timerqueue_lock);
|
||||||
timer->m_id = 0; // Don't generate a timer id
|
timer->m_id = 0; // Don't generate a timer id
|
||||||
|
@ -86,16 +91,17 @@ TimerId TimerQueue::add_timer(NonnullRefPtr<Timer>&& timer)
|
||||||
void TimerQueue::add_timer_locked(NonnullRefPtr<Timer> timer)
|
void TimerQueue::add_timer_locked(NonnullRefPtr<Timer> timer)
|
||||||
{
|
{
|
||||||
u64 timer_expiration = timer->m_expires;
|
u64 timer_expiration = timer->m_expires;
|
||||||
ASSERT(timer_expiration >= time_to_ticks(TimeManagement::the().monotonic_time()));
|
ASSERT(timer_expiration >= time_to_ticks(timer->m_clock_id, TimeManagement::the().current_time(timer->m_clock_id).value()));
|
||||||
|
|
||||||
ASSERT(!timer->is_queued());
|
ASSERT(!timer->is_queued());
|
||||||
|
|
||||||
if (m_timer_queue.is_empty()) {
|
auto& queue = queue_for_timer(*timer);
|
||||||
m_timer_queue.append(&timer.leak_ref());
|
if (queue.list.is_empty()) {
|
||||||
m_next_timer_due = timer_expiration;
|
queue.list.append(&timer.leak_ref());
|
||||||
|
queue.next_timer_due = timer_expiration;
|
||||||
} else {
|
} else {
|
||||||
Timer* following_timer = nullptr;
|
Timer* following_timer = nullptr;
|
||||||
m_timer_queue.for_each([&](Timer& t) {
|
queue.list.for_each([&](Timer& t) {
|
||||||
if (t.m_expires > timer_expiration) {
|
if (t.m_expires > timer_expiration) {
|
||||||
following_timer = &t;
|
following_timer = &t;
|
||||||
return IterationDecision::Break;
|
return IterationDecision::Break;
|
||||||
|
@ -103,51 +109,85 @@ void TimerQueue::add_timer_locked(NonnullRefPtr<Timer> timer)
|
||||||
return IterationDecision::Continue;
|
return IterationDecision::Continue;
|
||||||
});
|
});
|
||||||
if (following_timer) {
|
if (following_timer) {
|
||||||
bool next_timer_needs_update = m_timer_queue.head() == following_timer;
|
bool next_timer_needs_update = queue.list.head() == following_timer;
|
||||||
m_timer_queue.insert_before(following_timer, &timer.leak_ref());
|
queue.list.insert_before(following_timer, &timer.leak_ref());
|
||||||
if (next_timer_needs_update)
|
if (next_timer_needs_update)
|
||||||
m_next_timer_due = timer_expiration;
|
queue.next_timer_due = timer_expiration;
|
||||||
} else {
|
} else {
|
||||||
m_timer_queue.append(&timer.leak_ref());
|
queue.list.append(&timer.leak_ref());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TimerId TimerQueue::add_timer(timeval& deadline, Function<void()>&& callback)
|
TimerId TimerQueue::add_timer(clockid_t clock_id, timeval& deadline, Function<void()>&& callback)
|
||||||
{
|
{
|
||||||
auto expires = TimeManagement::the().monotonic_time();
|
auto expires = TimeManagement::the().current_time(clock_id).value();
|
||||||
timespec_add_timeval(expires, deadline, expires);
|
timespec_add_timeval(expires, deadline, expires);
|
||||||
return add_timer(adopt(*new Timer(time_to_ticks(expires), move(callback))));
|
return add_timer(adopt(*new Timer(clock_id, time_to_ticks(clock_id, expires), move(callback))));
|
||||||
}
|
}
|
||||||
|
|
||||||
timespec TimerQueue::ticks_to_time(u64 ticks) const
|
timespec TimerQueue::ticks_to_time(clockid_t clock_id, u64 ticks) const
|
||||||
{
|
{
|
||||||
timespec tspec;
|
timespec tspec;
|
||||||
tspec.tv_sec = ticks / m_ticks_per_second;
|
switch (clock_id) {
|
||||||
tspec.tv_nsec = (ticks % m_ticks_per_second) * (1'000'000'000 / m_ticks_per_second);
|
case CLOCK_MONOTONIC:
|
||||||
|
tspec.tv_sec = ticks / m_ticks_per_second;
|
||||||
|
tspec.tv_nsec = (ticks % m_ticks_per_second) * (1'000'000'000 / m_ticks_per_second);
|
||||||
|
break;
|
||||||
|
case CLOCK_REALTIME:
|
||||||
|
tspec.tv_sec = ticks / 1'000'000'000;
|
||||||
|
tspec.tv_nsec = ticks % 1'000'000'000;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
ASSERT(tspec.tv_nsec <= 1'000'000'000);
|
ASSERT(tspec.tv_nsec <= 1'000'000'000);
|
||||||
return tspec;
|
return tspec;
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 TimerQueue::time_to_ticks(const timespec& tspec) const
|
u64 TimerQueue::time_to_ticks(clockid_t clock_id, const timespec& tspec) const
|
||||||
{
|
{
|
||||||
u64 ticks = (u64)tspec.tv_sec * m_ticks_per_second;
|
u64 ticks;
|
||||||
ticks += ((u64)tspec.tv_nsec * m_ticks_per_second) / 1'000'000'000;
|
switch (clock_id) {
|
||||||
|
case CLOCK_MONOTONIC:
|
||||||
|
ticks = (u64)tspec.tv_sec * m_ticks_per_second;
|
||||||
|
ticks += ((u64)tspec.tv_nsec * m_ticks_per_second) / 1'000'000'000;
|
||||||
|
break;
|
||||||
|
case CLOCK_REALTIME:
|
||||||
|
ticks = (u64)tspec.tv_sec * 1'000'000'000 + tspec.tv_nsec;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
return ticks;
|
return ticks;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TimerQueue::cancel_timer(TimerId id)
|
bool TimerQueue::cancel_timer(TimerId id)
|
||||||
{
|
{
|
||||||
ScopedSpinLock lock(g_timerqueue_lock);
|
|
||||||
Timer* found_timer = nullptr;
|
Timer* found_timer = nullptr;
|
||||||
if (m_timer_queue.for_each([&](Timer& timer) {
|
Queue* timer_queue = nullptr;
|
||||||
|
|
||||||
|
ScopedSpinLock lock(g_timerqueue_lock);
|
||||||
|
if (m_timer_queue_monotonic.list.for_each([&](Timer& timer) {
|
||||||
if (timer.m_id == id) {
|
if (timer.m_id == id) {
|
||||||
found_timer = &timer;
|
found_timer = &timer;
|
||||||
|
timer_queue = &m_timer_queue_monotonic;
|
||||||
return IterationDecision::Break;
|
return IterationDecision::Break;
|
||||||
}
|
}
|
||||||
return IterationDecision::Continue;
|
return IterationDecision::Continue;
|
||||||
})
|
})
|
||||||
!= IterationDecision::Break) {
|
!= IterationDecision::Break) {
|
||||||
|
m_timer_queue_realtime.list.for_each([&](Timer& timer) {
|
||||||
|
if (timer.m_id == id) {
|
||||||
|
found_timer = &timer;
|
||||||
|
timer_queue = &m_timer_queue_realtime;
|
||||||
|
return IterationDecision::Break;
|
||||||
|
}
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found_timer) {
|
||||||
// The timer may be executing right now, if it is then it should
|
// 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
|
// be in m_timers_executing. If it is then release the lock
|
||||||
// briefly to allow it to finish by removing itself
|
// briefly to allow it to finish by removing itself
|
||||||
|
@ -171,7 +211,8 @@ bool TimerQueue::cancel_timer(TimerId id)
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(found_timer);
|
ASSERT(found_timer);
|
||||||
remove_timer_locked(*found_timer);
|
ASSERT(timer_queue);
|
||||||
|
remove_timer_locked(*timer_queue, *found_timer);
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
found_timer->unref();
|
found_timer->unref();
|
||||||
return true;
|
return true;
|
||||||
|
@ -179,8 +220,9 @@ bool TimerQueue::cancel_timer(TimerId id)
|
||||||
|
|
||||||
bool TimerQueue::cancel_timer(Timer& timer)
|
bool TimerQueue::cancel_timer(Timer& timer)
|
||||||
{
|
{
|
||||||
|
auto& timer_queue = queue_for_timer(timer);
|
||||||
ScopedSpinLock lock(g_timerqueue_lock);
|
ScopedSpinLock lock(g_timerqueue_lock);
|
||||||
if (!m_timer_queue.contains_slow(&timer)) {
|
if (!timer_queue.list.contains_slow(&timer)) {
|
||||||
// The timer may be executing right now, if it is then it should
|
// 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
|
// be in m_timers_executing. If it is then release the lock
|
||||||
// briefly to allow it to finish by removing itself
|
// briefly to allow it to finish by removing itself
|
||||||
|
@ -199,58 +241,64 @@ bool TimerQueue::cancel_timer(Timer& timer)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
remove_timer_locked(timer);
|
remove_timer_locked(timer_queue, timer);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TimerQueue::remove_timer_locked(Timer& timer)
|
void TimerQueue::remove_timer_locked(Queue& queue, Timer& timer)
|
||||||
{
|
{
|
||||||
bool was_next_timer = (m_timer_queue.head() == &timer);
|
bool was_next_timer = (queue.list.head() == &timer);
|
||||||
m_timer_queue.remove(&timer);
|
queue.list.remove(&timer);
|
||||||
timer.set_queued(false);
|
timer.set_queued(false);
|
||||||
auto now = TimeManagement::the().monotonic_ticks();
|
auto now = timer.now();
|
||||||
if (timer.m_expires > now)
|
if (timer.m_expires > now)
|
||||||
timer.m_remaining = timer.m_expires - now;
|
timer.m_remaining = timer.m_expires - now;
|
||||||
|
|
||||||
if (was_next_timer)
|
if (was_next_timer)
|
||||||
update_next_timer_due();
|
update_next_timer_due(queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TimerQueue::fire()
|
void TimerQueue::fire()
|
||||||
{
|
{
|
||||||
ScopedSpinLock lock(g_timerqueue_lock);
|
ScopedSpinLock lock(g_timerqueue_lock);
|
||||||
auto* timer = m_timer_queue.head();
|
|
||||||
if (!timer)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ASSERT(m_next_timer_due == timer->m_expires);
|
auto fire_timers = [&](Queue& queue) {
|
||||||
|
auto* timer = queue.list.head();
|
||||||
|
ASSERT(timer);
|
||||||
|
ASSERT(queue.next_timer_due == timer->m_expires);
|
||||||
|
|
||||||
while (timer && TimeManagement::the().monotonic_ticks() > timer->m_expires) {
|
while (timer && timer->now() > timer->m_expires) {
|
||||||
m_timer_queue.remove(timer);
|
queue.list.remove(timer);
|
||||||
m_timers_executing.append(timer);
|
m_timers_executing.append(timer);
|
||||||
|
|
||||||
update_next_timer_due();
|
update_next_timer_due(queue);
|
||||||
|
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
timer->m_callback();
|
timer->m_callback();
|
||||||
lock.lock();
|
lock.lock();
|
||||||
|
|
||||||
m_timers_executing.remove(timer);
|
m_timers_executing.remove(timer);
|
||||||
timer->set_queued(false);
|
timer->set_queued(false);
|
||||||
timer->unref();
|
timer->unref();
|
||||||
|
|
||||||
timer = m_timer_queue.head();
|
timer = queue.list.head();
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!m_timer_queue_monotonic.list.is_empty())
|
||||||
|
fire_timers(m_timer_queue_monotonic);
|
||||||
|
if (!m_timer_queue_realtime.list.is_empty())
|
||||||
|
fire_timers(m_timer_queue_realtime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TimerQueue::update_next_timer_due()
|
void TimerQueue::update_next_timer_due(Queue& queue)
|
||||||
{
|
{
|
||||||
ASSERT(g_timerqueue_lock.is_locked());
|
ASSERT(g_timerqueue_lock.is_locked());
|
||||||
|
|
||||||
if (auto* next_timer = m_timer_queue.head())
|
if (auto* next_timer = queue.list.head())
|
||||||
m_next_timer_due = next_timer->m_expires;
|
queue.next_timer_due = next_timer->m_expires;
|
||||||
else
|
else
|
||||||
m_next_timer_due = 0;
|
queue.next_timer_due = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,8 +43,9 @@ class Timer : public RefCounted<Timer>
|
||||||
friend class InlineLinkedListNode<Timer>;
|
friend class InlineLinkedListNode<Timer>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Timer(u64 expires, Function<void()>&& callback)
|
Timer(clockid_t clock_id, u64 expires, Function<void()>&& callback)
|
||||||
: m_expires(expires)
|
: m_clock_id(clock_id)
|
||||||
|
, m_expires(expires)
|
||||||
, m_callback(move(callback))
|
, m_callback(move(callback))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -57,6 +58,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TimerId m_id;
|
TimerId m_id;
|
||||||
|
clockid_t m_clock_id;
|
||||||
u64 m_expires;
|
u64 m_expires;
|
||||||
u64 m_remaining { 0 };
|
u64 m_remaining { 0 };
|
||||||
Function<void()> m_callback;
|
Function<void()> m_callback;
|
||||||
|
@ -78,6 +80,7 @@ private:
|
||||||
}
|
}
|
||||||
bool is_queued() const { return m_queued.load(AK::MemoryOrder::memory_order_relaxed); }
|
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); }
|
void set_queued(bool queued) { m_queued.store(queued, AK::MemoryOrder::memory_order_relaxed); }
|
||||||
|
u64 now() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class TimerQueue {
|
class TimerQueue {
|
||||||
|
@ -88,8 +91,8 @@ public:
|
||||||
static TimerQueue& the();
|
static TimerQueue& the();
|
||||||
|
|
||||||
TimerId add_timer(NonnullRefPtr<Timer>&&);
|
TimerId add_timer(NonnullRefPtr<Timer>&&);
|
||||||
RefPtr<Timer> add_timer_without_id(const timespec& timeout, Function<void()>&& callback);
|
RefPtr<Timer> add_timer_without_id(clockid_t, const timespec&, Function<void()>&&);
|
||||||
TimerId add_timer(timeval& timeout, Function<void()>&& callback);
|
TimerId add_timer(clockid_t, timeval& timeout, Function<void()>&& callback);
|
||||||
bool cancel_timer(TimerId id);
|
bool cancel_timer(TimerId id);
|
||||||
bool cancel_timer(Timer&);
|
bool cancel_timer(Timer&);
|
||||||
bool cancel_timer(NonnullRefPtr<Timer>&& timer)
|
bool cancel_timer(NonnullRefPtr<Timer>&& timer)
|
||||||
|
@ -99,17 +102,33 @@ public:
|
||||||
void fire();
|
void fire();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void remove_timer_locked(Timer&);
|
struct Queue {
|
||||||
void update_next_timer_due();
|
InlineLinkedList<Timer> list;
|
||||||
|
u64 next_timer_due { 0 };
|
||||||
|
};
|
||||||
|
void remove_timer_locked(Queue&, Timer&);
|
||||||
|
void update_next_timer_due(Queue&);
|
||||||
void add_timer_locked(NonnullRefPtr<Timer>);
|
void add_timer_locked(NonnullRefPtr<Timer>);
|
||||||
|
|
||||||
timespec ticks_to_time(u64 ticks) const;
|
Queue& queue_for_timer(Timer& timer)
|
||||||
u64 time_to_ticks(const timespec&) const;
|
{
|
||||||
|
switch (timer.m_clock_id) {
|
||||||
|
case CLOCK_MONOTONIC:
|
||||||
|
return m_timer_queue_monotonic;
|
||||||
|
case CLOCK_REALTIME:
|
||||||
|
return m_timer_queue_realtime;
|
||||||
|
default:
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
timespec ticks_to_time(clockid_t, u64 ticks) const;
|
||||||
|
u64 time_to_ticks(clockid_t, const timespec&) const;
|
||||||
|
|
||||||
u64 m_next_timer_due { 0 };
|
|
||||||
u64 m_timer_id_count { 0 };
|
u64 m_timer_id_count { 0 };
|
||||||
u64 m_ticks_per_second { 0 };
|
u64 m_ticks_per_second { 0 };
|
||||||
InlineLinkedList<Timer> m_timer_queue;
|
Queue m_timer_queue_monotonic;
|
||||||
|
Queue m_timer_queue_realtime;
|
||||||
InlineLinkedList<Timer> m_timers_executing;
|
InlineLinkedList<Timer> m_timers_executing;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue