Skip to content

Commit

Permalink
Kernel: Add CLOCK_REALTIME support to the TimerQueue
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
tomuta authored and awesomekling committed Dec 2, 2020
1 parent 4c1e27e commit 12cf6f8
Show file tree
Hide file tree
Showing 9 changed files with 182 additions and 97 deletions.
4 changes: 2 additions & 2 deletions Kernel/Syscalls/alarm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
}
Expand Down
24 changes: 8 additions & 16 deletions Kernel/Syscalls/clock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,11 @@ int Process::sys$clock_gettime(clockid_t clock_id, Userspace<timespec*> 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;
}
Expand Down Expand Up @@ -88,13 +79,14 @@ int Process::sys$clock_nanosleep(Userspace<const Syscall::SC_clock_nanosleep_par
bool is_absolute = params.flags & TIMER_ABSTIME;

switch (params.clock_id) {
case CLOCK_MONOTONIC: {
case CLOCK_MONOTONIC:
case CLOCK_REALTIME: {
bool was_interrupted;
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 {
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;
}
Expand Down
10 changes: 5 additions & 5 deletions Kernel/Thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<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);
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
Expand Down Expand Up @@ -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) {
Expand Down
30 changes: 21 additions & 9 deletions Kernel/Thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,41 +211,45 @@ class Thread
: 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);
}
}

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 };
};
Expand Down Expand Up @@ -733,7 +737,7 @@ class Thread

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);
Expand Down Expand Up @@ -816,8 +820,16 @@ class Thread
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.
Expand Down
6 changes: 3 additions & 3 deletions Kernel/ThreadBlockers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand Down Expand Up @@ -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
Expand Down
12 changes: 12 additions & 0 deletions Kernel/Time/TimeManagement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,18 @@ TimeManagement& TimeManagement::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
{
return &timer == m_system_timer.ptr();
Expand Down
2 changes: 2 additions & 0 deletions Kernel/Time/TimeManagement.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <AK/NonnullRefPtrVector.h>
#include <AK/RefPtr.h>
#include <AK/Types.h>
#include <Kernel/KResult.h>
#include <Kernel/UnixTypes.h>

namespace Kernel {
Expand All @@ -49,6 +50,7 @@ class TimeManagement {
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<timespec> current_time(clockid_t clock_id) const;
timespec monotonic_time() const;
timespec epoch_time() const;
void set_epoch_time(timespec);
Expand Down
Loading

0 comments on commit 12cf6f8

Please sign in to comment.