diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 4f643a06..380279f7 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -25,6 +25,7 @@ set(SOURCES src/thread/ThreadImage.cpp src/thread/Scheduler.cpp src/thread/Clock.cpp + src/thread/Timer.cpp src/sys/Syscall.cpp src/sys/exit.cpp src/sys/clock_gettime.cpp diff --git a/kernel/src/api/Syscall.h b/kernel/src/api/Syscall.h index 995af42a..273930e3 100644 --- a/kernel/src/api/Syscall.h +++ b/kernel/src/api/Syscall.h @@ -9,7 +9,8 @@ _e(pivot_root) _e(sigreturn) _e(sigaction) _e(kill) _e(sigprocmask) _e(setpgid) _e(isatty) \ _e(getpgid) _e(socket) _e(bind) _e(connect) _e(listen) _e(accept) _e(poll) _e(msync) \ _e(truncate) _e(ftruncate) _e(utimensat) _e(setitimer) _e(pledge) _e(memstat) \ - _e(setsid) _e(getsid) _e(getgroups) _e(setgroups) _e(pause) _e(sigsuspend) + _e(setsid) _e(getsid) _e(getgroups) _e(setgroups) _e(pause) _e(sigsuspend) \ + _e(timer_create) _e(timer_settime) _e(timer_gettime) _e(timer_delete) enum Syscalls { diff --git a/kernel/src/sys/exec.cpp b/kernel/src/sys/exec.cpp index fa81d67c..1da8b799 100644 --- a/kernel/src/sys/exec.cpp +++ b/kernel/src/sys/exec.cpp @@ -108,15 +108,16 @@ Result sys_execve(Registers* regs, SyscallArgs args) guard.deactivate(); - // Reset all timers. - if (current->real_timer.clock) { current->real_timer.clock->remove_from_timer_queue(¤t->real_timer); } - if (current->virtual_timer.clock) + current->real_timer.disarm(); + current->virtual_timer.disarm(); + current->profiling_timer.disarm(); + for (int i = 0; i < MAX_POSIX_TIMERS; i++) { - current->virtual_timer.clock->remove_from_timer_queue(¤t->virtual_timer); - } - if (current->profiling_timer.clock) - { - current->profiling_timer.clock->remove_from_timer_queue(¤t->profiling_timer); + if (current->posix_timers[i].has_value()) + { + current->posix_timers[i]->disarm(); + current->posix_timers[i] = {}; + } } for (int i = 0; i < FD_MAX; i++) diff --git a/kernel/src/sys/setitimer.cpp b/kernel/src/sys/setitimer.cpp index 422407c2..199eacef 100644 --- a/kernel/src/sys/setitimer.cpp +++ b/kernel/src/sys/setitimer.cpp @@ -4,8 +4,11 @@ #include "sys/Syscall.h" #include "thread/Scheduler.h" #include "thread/Timer.h" +#include #include +#include #include +#include Result sys_setitimer(Registers*, SyscallArgs args) { @@ -38,11 +41,11 @@ Result sys_setitimer(Registers*, SyscallArgs args) if (old_timer) { - if (timer->clock) + if (timer->active_clock) { struct itimerval result; - auto total = timer->clock->from_ticks(timer->total_ticks); - auto left = timer->clock->from_ticks(timer->clock->ticks_left(timer)); + auto total = timer->active_clock->from_ticks(timer->interval_ticks); + auto left = timer->active_clock->from_ticks(timer->active_clock->ticks_left(timer)); result.it_interval = TIMESPEC_TO_TIMEVAL(total); result.it_value = TIMESPEC_TO_TIMEVAL(left); if (!MemoryManager::copy_to_user_typed(old_timer, &result)) return err(EFAULT); @@ -57,7 +60,7 @@ Result sys_setitimer(Registers*, SyscallArgs args) if (new_timer) { - if (timer->clock) timer->clock->remove_from_timer_queue(timer); + timer->disarm(); struct itimerval itimer; if (!MemoryManager::copy_from_user_typed(new_timer, &itimer)) return err(EFAULT); @@ -68,7 +71,7 @@ Result sys_setitimer(Registers*, SyscallArgs args) if (itimer.it_interval.tv_sec != 0 || itimer.it_interval.tv_usec != 0) { timer->restart = true; - timer->total_ticks = + timer->interval_ticks = (itimer.it_interval.tv_sec * 1'000'000'000 + itimer.it_interval.tv_usec * 1000) / clock->resolution(); } else @@ -76,11 +79,162 @@ Result sys_setitimer(Registers*, SyscallArgs args) if (itimer.it_value.tv_sec != 0 || itimer.it_value.tv_usec != 0) { - timer->delta_ticks = - (itimer.it_value.tv_sec * 1'000'000'000 + itimer.it_value.tv_usec * 1000) / clock->resolution(); - clock->add_to_timer_queue(timer); + u64 ticks = (itimer.it_value.tv_sec * 1'000'000'000 + itimer.it_value.tv_usec * 1000) / clock->resolution(); + timer->arm(clock, ticks); } } return 0; } + +Result sys_timer_create(Registers*, SyscallArgs args) +{ + clockid_t clockid = (clockid_t)args[0]; + struct sigevent* sevp = (struct sigevent*)args[1]; + timer_t* timerid = (timer_t*)args[2]; + + auto* current = Scheduler::current(); + + TRY(check_pledge(current, Promise::p_stdio)); + + struct sigevent ksevp; + if (!sevp) + { + ksevp.sigev_notify = SIGEV_SIGNAL; + ksevp.sigev_signo = SIGALRM; + } + else if (!MemoryManager::copy_from_user_typed(sevp, &ksevp)) + return err(EFAULT); + + Clock* clock; + switch (clockid) + { + case CLOCK_REALTIME: clock = &g_realtime_clock; break; + case CLOCK_MONOTONIC: clock = &g_monotonic_clock; break; + default: return err(EINVAL); + } + + if (ksevp.sigev_notify != SIGEV_SIGNAL) return err(ENOTSUP); + if (ksevp.sigev_signo <= 0 || ksevp.sigev_signo > NSIG) return err(EINVAL); + + int id = TRY(current->allocate_timerid()); + current->posix_timers[id] = Timer {}; + + Timer* timer = current->posix_timers[id].value_ptr(); + timer->signo = ksevp.sigev_signo; + timer->designated_clock = clock; + + if (!MemoryManager::copy_to_user_typed(timerid, &id)) return err(EFAULT); + + return 0; +} + +Result sys_timer_settime(Registers*, SyscallArgs args) +{ + timer_t timerid = (timer_t)args[0]; + int flags = (int)args[1]; + const struct itimerspec* new_value = (const struct itimerspec*)args[2]; + struct itimerspec* old_value = (struct itimerspec*)args[3]; + if (timerid < 0 || timerid >= MAX_POSIX_TIMERS) return err(EINVAL); + if (flags > 0) return err(ENOTSUP); + + auto* current = Scheduler::current(); + + TRY(check_pledge(current, Promise::p_stdio)); + + if (!current->posix_timers[timerid].has_value()) return err(EINVAL); + Timer* timer = current->posix_timers[timerid].value_ptr(); + + if (old_value) + { + if (timer->active_clock) + { + struct itimerspec result; + result.it_interval = timer->active_clock->from_ticks(timer->interval_ticks); + result.it_value = timer->active_clock->from_ticks(timer->active_clock->ticks_left(timer)); + if (!MemoryManager::copy_to_user_typed(old_value, &result)) return err(EFAULT); + } + else + { + struct itimerspec result; + memset(&result, 0, sizeof(result)); + if (!MemoryManager::copy_to_user_typed(old_value, &result)) return err(EFAULT); + } + } + + struct itimerspec itimer; + if (!MemoryManager::copy_from_user_typed(new_value, &itimer)) return err(EFAULT); + + timer->disarm(); + + Clock* clock = timer->designated_clock; + check(clock); + + timer->thread = current; + + if (itimer.it_interval.tv_sec != 0 || itimer.it_interval.tv_nsec != 0) + { + timer->restart = true; + timer->interval_ticks = + (itimer.it_interval.tv_sec * 1'000'000'000 + itimer.it_interval.tv_nsec) / clock->resolution(); + } + else + timer->restart = false; + + if (itimer.it_value.tv_sec != 0 || itimer.it_value.tv_nsec != 0) + { + u64 ticks = (itimer.it_value.tv_sec * 1'000'000'000 + itimer.it_value.tv_nsec) / clock->resolution(); + timer->arm(clock, ticks); + } + + return 0; +} + +Result sys_timer_gettime(Registers*, SyscallArgs args) +{ + timer_t timerid = (timer_t)args[0]; + struct itimerspec* value = (struct itimerspec*)args[1]; + if (timerid < 0 || timerid >= MAX_POSIX_TIMERS) return err(EINVAL); + + auto* current = Scheduler::current(); + + TRY(check_pledge(current, Promise::p_stdio)); + + if (!current->posix_timers[timerid].has_value()) return err(EINVAL); + Timer* timer = current->posix_timers[timerid].value_ptr(); + + if (timer->active_clock) + { + struct itimerspec result; + result.it_interval = timer->active_clock->from_ticks(timer->interval_ticks); + result.it_value = timer->active_clock->from_ticks(timer->active_clock->ticks_left(timer)); + if (!MemoryManager::copy_to_user_typed(value, &result)) return err(EFAULT); + } + else + { + struct itimerspec result; + memset(&result, 0, sizeof(result)); + if (!MemoryManager::copy_to_user_typed(value, &result)) return err(EFAULT); + } + + return 0; +} + +Result sys_timer_delete(Registers*, SyscallArgs args) +{ + timer_t timerid = (timer_t)args[0]; + if (timerid < 0 || timerid >= MAX_POSIX_TIMERS) return err(EINVAL); + + auto* current = Scheduler::current(); + + TRY(check_pledge(current, Promise::p_stdio)); + + if (!current->posix_timers[timerid].has_value()) return err(EINVAL); + + Timer* timer = current->posix_timers[timerid].value_ptr(); + timer->disarm(); + + current->posix_timers[timerid] = {}; + + return 0; +} diff --git a/kernel/src/thread/Clock.cpp b/kernel/src/thread/Clock.cpp index ce8bce87..1fb6c8bb 100644 --- a/kernel/src/thread/Clock.cpp +++ b/kernel/src/thread/Clock.cpp @@ -71,7 +71,7 @@ void Clock::update(const struct timespec& time) void Clock::add_to_timer_queue(Timer* timer) { - check(timer->clock == nullptr); + check(timer->active_clock == nullptr); for (auto* t : m_timer_queue) { @@ -86,12 +86,12 @@ void Clock::add_to_timer_queue(Timer* timer) m_timer_queue.append(timer); - timer->clock = this; + timer->active_clock = this; } void Clock::remove_from_timer_queue(Timer* timer) { - check(timer->clock == this); + check(timer->active_clock == this); auto maybe_next = m_timer_queue.next(timer); if (maybe_next.has_value()) @@ -101,7 +101,7 @@ void Clock::remove_from_timer_queue(Timer* timer) } m_timer_queue.remove(timer); - timer->clock = nullptr; + timer->active_clock = nullptr; } void Clock::tick() @@ -122,7 +122,7 @@ void Clock::tick() if (t->delta_ticks == 0) { this->m_timer_queue.remove(t); - t->clock = nullptr; + t->active_clock = nullptr; t->thread->send_signal(t->signo); if (t->restart) timers_to_be_restarted.append(t); return true; @@ -131,7 +131,10 @@ void Clock::tick() return false; }); - timers_to_be_restarted.consume([this](Timer* t) { add_to_timer_queue(t); }); + timers_to_be_restarted.consume([this](Timer* t) { + t->delta_ticks = t->interval_ticks; + add_to_timer_queue(t); + }); } void Clock::get_time(struct timespec& out) @@ -146,7 +149,7 @@ long Clock::resolution() u64 Clock::ticks_left(Timer* timer) { - check(timer->clock == this); + check(timer->active_clock == this); u64 total = 0; if (timer->delta_ticks == 0) return 0; diff --git a/kernel/src/thread/Scheduler.cpp b/kernel/src/thread/Scheduler.cpp index 9ac0aeca..4b869a01 100644 --- a/kernel/src/thread/Scheduler.cpp +++ b/kernel/src/thread/Scheduler.cpp @@ -221,7 +221,14 @@ namespace Scheduler MemoryManager::unmap_owned_and_free_vm(stack.bottom(), stack.bytes() / ARCH_PAGE_SIZE).release_value(); } - if (thread->real_timer.clock) { thread->real_timer.clock->remove_from_timer_queue(&thread->real_timer); } + // FIXME: Shouldn't all this be done when the timers' destructors are called? + thread->real_timer.disarm(); + thread->virtual_timer.disarm(); + thread->profiling_timer.disarm(); + for (int i = 0; i < MAX_POSIX_TIMERS; i++) + { + if (thread->posix_timers[i].has_value()) thread->posix_timers[i]->disarm(); + } delete thread; diff --git a/kernel/src/thread/Thread.cpp b/kernel/src/thread/Thread.cpp index be9f88b8..61956fdf 100644 --- a/kernel/src/thread/Thread.cpp +++ b/kernel/src/thread/Thread.cpp @@ -58,6 +58,28 @@ Result Thread::resolve_fd(int fd) return maybe_descriptor.value_ptr(); } +Result Thread::allocate_timerid() +{ + for (int i = 0; i < MAX_POSIX_TIMERS; i++) + { + // FIXME: Possible race condition, this should be used alongside a mutex. + if (!posix_timers[i].has_value()) { return i; } + } + + return err(EMFILE); +} + +Result Thread::resolve_timerid(int tid) +{ + if (tid < 0 || tid >= MAX_POSIX_TIMERS) return err(EBADF); + + Option& maybe_timer = posix_timers[tid]; + + if (!maybe_timer.has_value()) return err(EINVAL); + + return maybe_timer.value_ptr(); +} + Result> Thread::resolve_atfile(int dirfd, const String& path, bool allow_empty_path, bool follow_last_symlink, SharedPtr* parent_inode) { diff --git a/kernel/src/thread/Thread.h b/kernel/src/thread/Thread.h index ada8ad90..9b189c01 100644 --- a/kernel/src/thread/Thread.h +++ b/kernel/src/thread/Thread.h @@ -19,6 +19,8 @@ #error "Unknown architecture." #endif +constexpr int MAX_POSIX_TIMERS = 64; + class Timer; enum class ThreadState @@ -106,6 +108,11 @@ struct Thread : public LinkedListNode Clock virtual_clock; Clock profiling_clock; + Option posix_timers[MAX_POSIX_TIMERS]; + + Result allocate_timerid(); + Result resolve_timerid(int id); + StaticString<128> cmdline; String current_directory_path = {}; diff --git a/kernel/src/thread/Timer.cpp b/kernel/src/thread/Timer.cpp new file mode 100644 index 00000000..de24d8f6 --- /dev/null +++ b/kernel/src/thread/Timer.cpp @@ -0,0 +1,16 @@ +#include "thread/Timer.h" +#include "thread/Clock.h" + +void Timer::disarm() +{ + if (active_clock) active_clock->remove_from_timer_queue(this); +} + +void Timer::arm(Clock* c, u64 ticks) +{ + disarm(); + + delta_ticks = ticks; + + c->add_to_timer_queue(this); +} diff --git a/kernel/src/thread/Timer.h b/kernel/src/thread/Timer.h index bd1c1bf4..f7b7e99c 100644 --- a/kernel/src/thread/Timer.h +++ b/kernel/src/thread/Timer.h @@ -9,10 +9,14 @@ class Timer : public LinkedListNode { public: u64 delta_ticks { 0 }; - u64 total_ticks { 0 }; + u64 interval_ticks { 0 }; Thread* thread; int signo { SIGALRM }; bool restart { false }; - Clock* clock { nullptr }; + void disarm(); + void arm(Clock* clock, u64 ticks); + + Clock* active_clock { nullptr }; + Clock* designated_clock { nullptr }; }; diff --git a/libc/include/bits/itimer.h b/libc/include/bits/itimer.h index 2708a330..dbf73941 100644 --- a/libc/include/bits/itimer.h +++ b/libc/include/bits/itimer.h @@ -1,4 +1,4 @@ -/* bits/itimer.h: Constants for setitimer() and getitimer(). */ +/* bits/itimer.h: Constants for POSIX timers, setitimer() and getitimer(). */ #ifndef _BITS_ITIMER_H #define _BITS_ITIMER_H @@ -15,4 +15,10 @@ struct itimerval struct timeval it_value; }; +struct itimerspec +{ + struct timespec it_interval; + struct timespec it_value; +}; + #endif diff --git a/libc/include/bits/sigevent.h b/libc/include/bits/sigevent.h new file mode 100644 index 00000000..7f1f9f31 --- /dev/null +++ b/libc/include/bits/sigevent.h @@ -0,0 +1,24 @@ +/* bits/sigevent.h: The sigevent structure. */ + +#ifndef _BITS_SIGEVENT_H +#define _BITS_SIGEVENT_H + +#define SIGEV_NONE 0 +#define SIGEV_SIGNAL 1 +#define SIGEV_THREAD 2 + +union sigval { + int sival_int; + void* sival_ptr; +}; + +struct sigevent +{ + int sigev_notify; + int sigev_signo; + union sigval sigev_value; + void (*sigev_notify_function)(union sigval); + void* sigev_notify_attributes; +}; + +#endif diff --git a/libc/include/sys/types.h b/libc/include/sys/types.h index 0ee7f598..72339c83 100644 --- a/libc/include/sys/types.h +++ b/libc/include/sys/types.h @@ -20,6 +20,7 @@ typedef __u32_t uid_t; typedef __u32_t gid_t; typedef __u64_t nlink_t; typedef __i64_t suseconds_t; +typedef __i32_t timer_t; typedef off_t fpos_t;