diff --git a/Kernel/Syscalls/alarm.cpp b/Kernel/Syscalls/alarm.cpp index 2636c41b4f..79413eca44 100644 --- a/Kernel/Syscalls/alarm.cpp +++ b/Kernel/Syscalls/alarm.cpp @@ -46,9 +46,9 @@ unsigned Process::sys$alarm(unsigned seconds) } 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); - 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); }); } diff --git a/Kernel/Syscalls/clock.cpp b/Kernel/Syscalls/clock.cpp index 8546bf7b60..a48c2df98a 100644 --- a/Kernel/Syscalls/clock.cpp +++ b/Kernel/Syscalls/clock.cpp @@ -34,20 +34,11 @@ int Process::sys$clock_gettime(clockid_t clock_id, Userspace user_ts) { REQUIRE_PROMISE(stdio); - timespec ts = {}; + auto ts = TimeManagement::the().current_time(clock_id); + if (ts.is_error()) + return ts.error(); - switch (clock_id) { - 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)) + if (!copy_to_user(user_ts, &ts.value())) return -EFAULT; return 0; } @@ -88,13 +79,14 @@ int Process::sys$clock_nanosleep(Userspacesleep_until(requested_sleep).was_interrupted(); + was_interrupted = Thread::current()->sleep_until(params.clock_id, requested_sleep).was_interrupted(); } else { 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)) return -EFAULT; } diff --git a/Kernel/Thread.cpp b/Kernel/Thread.cpp index 400ad8e4bf..c6a713069b 100644 --- a/Kernel/Thread.cpp +++ b/Kernel/Thread.cpp @@ -271,16 +271,16 @@ void Thread::relock_process(bool did_unlock) 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); - return Thread::current()->block(nullptr, Thread::BlockTimeout(false, &duration), remaining_time); + return Thread::current()->block(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); - return Thread::current()->block(nullptr, Thread::BlockTimeout(true, &deadline)); + return Thread::current()->block(nullptr, Thread::BlockTimeout(true, &deadline, nullptr, clock_id)); } 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); 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! ScopedSpinLock lock(g_scheduler_lock); if (!block_finished) { diff --git a/Kernel/Thread.h b/Kernel/Thread.h index af2cbcb576..ceffcecc8b 100644 --- a/Kernel/Thread.h +++ b/Kernel/Thread.h @@ -211,28 +211,30 @@ public: : m_infinite(true) { } - explicit BlockTimeout(bool is_absolute, const timeval* time, const timespec* start_time = nullptr) - : m_infinite(!time) + explicit BlockTimeout(bool is_absolute, const timeval* time, const timespec* start_time = nullptr, clockid_t clock_id = CLOCK_MONOTONIC) + : m_clock_id(clock_id) + , m_infinite(!time) { if (!m_infinite) { if (time->tv_sec > 0 || time->tv_usec > 0) { timeval_to_timespec(*time, m_time); 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) timespec_add(m_time, m_start_time, m_time); } } - explicit BlockTimeout(bool is_absolute, const timespec* time, const timespec* start_time = nullptr) - : m_infinite(!time) + explicit BlockTimeout(bool is_absolute, const timespec* time, const timespec* start_time = nullptr, clockid_t clock_id = CLOCK_MONOTONIC) + : m_clock_id(clock_id) + , m_infinite(!time) { if (!m_infinite) { if (time->tv_sec > 0 || time->tv_nsec > 0) { m_time = *time; 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) timespec_add(m_time, m_start_time, m_time); } @@ -240,12 +242,14 @@ public: const timespec& absolute_time() const { return m_time; } 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 should_block() const { return m_infinite || m_should_block; }; private: timespec m_time { 0, 0 }; timespec m_start_time { 0, 0 }; + clockid_t m_clock_id { CLOCK_MONOTONIC }; bool m_infinite { false }; bool m_should_block { false }; }; @@ -733,7 +737,7 @@ public: auto& block_timeout = t.override_timeout(timeout); 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! ScopedSpinLock scheduler_lock(g_scheduler_lock); ScopedSpinLock lock(m_lock); @@ -816,8 +820,16 @@ public: void unblock_from_blocker(Blocker&); void unblock(u8 signal = 0); - BlockResult sleep(const timespec&, timespec* = nullptr); - BlockResult sleep_until(const timespec&); + BlockResult sleep(clockid_t, const timespec&, timespec* = nullptr); + 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, // gracefully unwind the stack and die. diff --git a/Kernel/ThreadBlockers.cpp b/Kernel/ThreadBlockers.cpp index b04eccb68c..1bd025a643 100644 --- a/Kernel/ThreadBlockers.cpp +++ b/Kernel/ThreadBlockers.cpp @@ -197,7 +197,7 @@ auto Thread::WriteBlocker::override_timeout(const BlockTimeout& timeout) -> cons if (description.is_socket()) { auto& socket = *description.socket(); 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())) return m_timeout; } @@ -216,7 +216,7 @@ auto Thread::ReadBlocker::override_timeout(const BlockTimeout& timeout) -> const if (description.is_socket()) { auto& socket = *description.socket(); 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())) return m_timeout; } @@ -256,7 +256,7 @@ void Thread::SleepBlocker::calculate_remaining() { if (!m_remaining) 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()) timespec_sub(m_deadline.absolute_time(), time_now, *m_remaining); else diff --git a/Kernel/Time/TimeManagement.cpp b/Kernel/Time/TimeManagement.cpp index 639cf94f42..bcf0d8f8ea 100644 --- a/Kernel/Time/TimeManagement.cpp +++ b/Kernel/Time/TimeManagement.cpp @@ -52,6 +52,18 @@ TimeManagement& TimeManagement::the() return *s_the; } +KResultOr 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 { return &timer == m_system_timer.ptr(); diff --git a/Kernel/Time/TimeManagement.h b/Kernel/Time/TimeManagement.h index b70b162fc7..009f345742 100644 --- a/Kernel/Time/TimeManagement.h +++ b/Kernel/Time/TimeManagement.h @@ -29,6 +29,7 @@ #include #include #include +#include #include namespace Kernel { @@ -49,6 +50,7 @@ public: 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); + KResultOr current_time(clockid_t clock_id) const; timespec monotonic_time() const; timespec epoch_time() const; void set_epoch_time(timespec); diff --git a/Kernel/TimerQueue.cpp b/Kernel/TimerQueue.cpp index 6c1647b550..0a5a67b22e 100644 --- a/Kernel/TimerQueue.cpp +++ b/Kernel/TimerQueue.cpp @@ -42,7 +42,12 @@ timespec Timer::remaining() const { if (m_remaining == 0) 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() @@ -55,9 +60,9 @@ TimerQueue::TimerQueue() m_ticks_per_second = TimeManagement::the().ticks_per_second(); } -RefPtr TimerQueue::add_timer_without_id(const timespec& deadline, Function&& callback) +RefPtr TimerQueue::add_timer_without_id(clockid_t clock_id, const timespec& deadline, Function&& callback) { - if (deadline <= TimeManagement::the().monotonic_time()) + if (deadline <= TimeManagement::the().current_time(clock_id).value()) return {}; // Because timer handlers can execute on any processor and there is @@ -65,7 +70,7 @@ RefPtr TimerQueue::add_timer_without_id(const timespec& deadline, Functio // *must* be a RefPtr. Otherwise calling cancel_timer() could // inadvertently cancel another timer that has been created between // 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); timer->m_id = 0; // Don't generate a timer id @@ -86,16 +91,17 @@ TimerId TimerQueue::add_timer(NonnullRefPtr&& timer) void TimerQueue::add_timer_locked(NonnullRefPtr timer) { 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()); - if (m_timer_queue.is_empty()) { - m_timer_queue.append(&timer.leak_ref()); - m_next_timer_due = timer_expiration; + auto& queue = queue_for_timer(*timer); + if (queue.list.is_empty()) { + queue.list.append(&timer.leak_ref()); + queue.next_timer_due = timer_expiration; } else { Timer* following_timer = nullptr; - m_timer_queue.for_each([&](Timer& t) { + queue.list.for_each([&](Timer& t) { if (t.m_expires > timer_expiration) { following_timer = &t; return IterationDecision::Break; @@ -103,51 +109,85 @@ void TimerQueue::add_timer_locked(NonnullRefPtr timer) 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()); + bool next_timer_needs_update = queue.list.head() == following_timer; + queue.list.insert_before(following_timer, &timer.leak_ref()); if (next_timer_needs_update) - m_next_timer_due = timer_expiration; + queue.next_timer_due = timer_expiration; } else { - m_timer_queue.append(&timer.leak_ref()); + queue.list.append(&timer.leak_ref()); } } } -TimerId TimerQueue::add_timer(timeval& deadline, Function&& callback) +TimerId TimerQueue::add_timer(clockid_t clock_id, timeval& deadline, Function&& callback) { - auto expires = TimeManagement::the().monotonic_time(); + auto expires = TimeManagement::the().current_time(clock_id).value(); 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; - tspec.tv_sec = ticks / m_ticks_per_second; - tspec.tv_nsec = (ticks % m_ticks_per_second) * (1'000'000'000 / m_ticks_per_second); + switch (clock_id) { + 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); 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; - ticks += ((u64)tspec.tv_nsec * m_ticks_per_second) / 1'000'000'000; + u64 ticks; + 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; } bool TimerQueue::cancel_timer(TimerId id) { - ScopedSpinLock lock(g_timerqueue_lock); 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) { found_timer = &timer; + timer_queue = &m_timer_queue_monotonic; return IterationDecision::Break; } return IterationDecision::Continue; }) != 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 // be in m_timers_executing. If it is then release the lock // briefly to allow it to finish by removing itself @@ -171,7 +211,8 @@ bool TimerQueue::cancel_timer(TimerId id) } ASSERT(found_timer); - remove_timer_locked(*found_timer); + ASSERT(timer_queue); + remove_timer_locked(*timer_queue, *found_timer); lock.unlock(); found_timer->unref(); return true; @@ -179,8 +220,9 @@ bool TimerQueue::cancel_timer(TimerId id) bool TimerQueue::cancel_timer(Timer& timer) { + auto& timer_queue = queue_for_timer(timer); 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 // be in m_timers_executing. If it is then release the lock // briefly to allow it to finish by removing itself @@ -199,58 +241,64 @@ bool TimerQueue::cancel_timer(Timer& timer) return false; } - remove_timer_locked(timer); + remove_timer_locked(timer_queue, timer); 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); - m_timer_queue.remove(&timer); + bool was_next_timer = (queue.list.head() == &timer); + queue.list.remove(&timer); timer.set_queued(false); - auto now = TimeManagement::the().monotonic_ticks(); + auto now = timer.now(); if (timer.m_expires > now) timer.m_remaining = timer.m_expires - now; if (was_next_timer) - update_next_timer_due(); + update_next_timer_due(queue); } void TimerQueue::fire() { 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) { - m_timer_queue.remove(timer); - m_timers_executing.append(timer); + while (timer && timer->now() > timer->m_expires) { + queue.list.remove(timer); + m_timers_executing.append(timer); - update_next_timer_due(); + update_next_timer_due(queue); - lock.unlock(); - timer->m_callback(); - lock.lock(); + lock.unlock(); + timer->m_callback(); + lock.lock(); - m_timers_executing.remove(timer); - timer->set_queued(false); - timer->unref(); + m_timers_executing.remove(timer); + timer->set_queued(false); + 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()); - if (auto* next_timer = m_timer_queue.head()) - m_next_timer_due = next_timer->m_expires; + if (auto* next_timer = queue.list.head()) + queue.next_timer_due = next_timer->m_expires; else - m_next_timer_due = 0; + queue.next_timer_due = 0; } } diff --git a/Kernel/TimerQueue.h b/Kernel/TimerQueue.h index ffeda2fedc..33e741b388 100644 --- a/Kernel/TimerQueue.h +++ b/Kernel/TimerQueue.h @@ -43,8 +43,9 @@ class Timer : public RefCounted friend class InlineLinkedListNode; public: - Timer(u64 expires, Function&& callback) - : m_expires(expires) + Timer(clockid_t clock_id, u64 expires, Function&& callback) + : m_clock_id(clock_id) + , m_expires(expires) , m_callback(move(callback)) { } @@ -57,6 +58,7 @@ public: private: TimerId m_id; + clockid_t m_clock_id; u64 m_expires; u64 m_remaining { 0 }; Function m_callback; @@ -78,6 +80,7 @@ private: } 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); } + u64 now() const; }; class TimerQueue { @@ -88,8 +91,8 @@ public: static TimerQueue& the(); TimerId add_timer(NonnullRefPtr&&); - RefPtr add_timer_without_id(const timespec& timeout, Function&& callback); - TimerId add_timer(timeval& timeout, Function&& callback); + RefPtr add_timer_without_id(clockid_t, const timespec&, Function&&); + TimerId add_timer(clockid_t, timeval& timeout, Function&& callback); bool cancel_timer(TimerId id); bool cancel_timer(Timer&); bool cancel_timer(NonnullRefPtr&& timer) @@ -99,17 +102,33 @@ public: void fire(); private: - void remove_timer_locked(Timer&); - void update_next_timer_due(); + struct Queue { + InlineLinkedList list; + u64 next_timer_due { 0 }; + }; + void remove_timer_locked(Queue&, Timer&); + void update_next_timer_due(Queue&); void add_timer_locked(NonnullRefPtr); - timespec ticks_to_time(u64 ticks) const; - u64 time_to_ticks(const timespec&) const; + Queue& queue_for_timer(Timer& timer) + { + 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_ticks_per_second { 0 }; - InlineLinkedList m_timer_queue; + Queue m_timer_queue_monotonic; + Queue m_timer_queue_realtime; InlineLinkedList m_timers_executing; };