Compare commits
5 Commits
f8cc093e17
...
7f6863c093
Author | SHA1 | Date | |
---|---|---|---|
7f6863c093 | |||
a9c339939a | |||
3231a1296d | |||
17b44a8ce6 | |||
41c90aa436 |
@ -1,3 +1,4 @@
|
|||||||
|
#include <os/Timer.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <ui/App.h>
|
#include <ui/App.h>
|
||||||
#include <ui/Label.h>
|
#include <ui/Label.h>
|
||||||
@ -14,8 +15,6 @@ void update_time()
|
|||||||
|
|
||||||
g_label->set_text(StringView { buf });
|
g_label->set_text(StringView { buf });
|
||||||
|
|
||||||
os::EventLoop::the().register_timer(1000, update_time);
|
|
||||||
|
|
||||||
ui::App::the().main_window()->draw();
|
ui::App::the().main_window()->draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,5 +37,7 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
|
|
||||||
update_time();
|
update_time();
|
||||||
|
|
||||||
|
auto timer = TRY(os::Timer::create_repeating(1000, update_time));
|
||||||
|
|
||||||
return app.run();
|
return app.run();
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <luna/Heap.h>
|
#include <luna/Heap.h>
|
||||||
#include <os/ArgumentParser.h>
|
#include <os/ArgumentParser.h>
|
||||||
|
#include <os/Timer.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
@ -105,7 +106,6 @@ static void update()
|
|||||||
{
|
{
|
||||||
next_generation();
|
next_generation();
|
||||||
draw_cells();
|
draw_cells();
|
||||||
os::EventLoop::the().register_timer(100, update);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<int> luna_main(int argc, char** argv)
|
Result<int> luna_main(int argc, char** argv)
|
||||||
@ -119,7 +119,9 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
|
|
||||||
TRY(fill_cells());
|
TRY(fill_cells());
|
||||||
|
|
||||||
os::EventLoop::the().register_timer(100, update);
|
update();
|
||||||
|
|
||||||
|
auto timer = TRY(os::Timer::create_repeating(100, update));
|
||||||
|
|
||||||
return app.run();
|
return app.run();
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ set(SOURCES
|
|||||||
src/thread/ThreadImage.cpp
|
src/thread/ThreadImage.cpp
|
||||||
src/thread/Scheduler.cpp
|
src/thread/Scheduler.cpp
|
||||||
src/thread/Clock.cpp
|
src/thread/Clock.cpp
|
||||||
|
src/thread/Timer.cpp
|
||||||
src/sys/Syscall.cpp
|
src/sys/Syscall.cpp
|
||||||
src/sys/exit.cpp
|
src/sys/exit.cpp
|
||||||
src/sys/clock_gettime.cpp
|
src/sys/clock_gettime.cpp
|
||||||
|
@ -9,7 +9,8 @@
|
|||||||
_e(pivot_root) _e(sigreturn) _e(sigaction) _e(kill) _e(sigprocmask) _e(setpgid) _e(isatty) \
|
_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(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(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
|
enum Syscalls
|
||||||
{
|
{
|
||||||
|
@ -105,6 +105,8 @@ void handle_cpu_exception(int signo, const char* err, Registers* regs)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CPU::print_stack_trace_at(regs);
|
||||||
|
|
||||||
CPU::efficient_halt();
|
CPU::efficient_halt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,15 +108,16 @@ Result<u64> sys_execve(Registers* regs, SyscallArgs args)
|
|||||||
|
|
||||||
guard.deactivate();
|
guard.deactivate();
|
||||||
|
|
||||||
// Reset all timers.
|
current->real_timer.disarm();
|
||||||
if (current->real_timer.clock) { current->real_timer.clock->remove_from_timer_queue(¤t->real_timer); }
|
current->virtual_timer.disarm();
|
||||||
if (current->virtual_timer.clock)
|
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->posix_timers[i].has_value())
|
||||||
|
{
|
||||||
|
current->posix_timers[i]->disarm();
|
||||||
|
current->posix_timers[i] = {};
|
||||||
}
|
}
|
||||||
if (current->profiling_timer.clock)
|
|
||||||
{
|
|
||||||
current->profiling_timer.clock->remove_from_timer_queue(¤t->profiling_timer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < FD_MAX; i++)
|
for (int i = 0; i < FD_MAX; i++)
|
||||||
|
@ -4,8 +4,11 @@
|
|||||||
#include "sys/Syscall.h"
|
#include "sys/Syscall.h"
|
||||||
#include "thread/Scheduler.h"
|
#include "thread/Scheduler.h"
|
||||||
#include "thread/Timer.h"
|
#include "thread/Timer.h"
|
||||||
|
#include <bits/clockid.h>
|
||||||
#include <bits/itimer.h>
|
#include <bits/itimer.h>
|
||||||
|
#include <bits/sigevent.h>
|
||||||
#include <luna/Common.h>
|
#include <luna/Common.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
Result<u64> sys_setitimer(Registers*, SyscallArgs args)
|
Result<u64> sys_setitimer(Registers*, SyscallArgs args)
|
||||||
{
|
{
|
||||||
@ -38,11 +41,11 @@ Result<u64> sys_setitimer(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
if (old_timer)
|
if (old_timer)
|
||||||
{
|
{
|
||||||
if (timer->clock)
|
if (timer->active_clock)
|
||||||
{
|
{
|
||||||
struct itimerval result;
|
struct itimerval result;
|
||||||
auto total = timer->clock->from_ticks(timer->total_ticks);
|
auto total = timer->active_clock->from_ticks(timer->interval_ticks);
|
||||||
auto left = timer->clock->from_ticks(timer->clock->ticks_left(timer));
|
auto left = timer->active_clock->from_ticks(timer->active_clock->ticks_left(timer));
|
||||||
result.it_interval = TIMESPEC_TO_TIMEVAL(total);
|
result.it_interval = TIMESPEC_TO_TIMEVAL(total);
|
||||||
result.it_value = TIMESPEC_TO_TIMEVAL(left);
|
result.it_value = TIMESPEC_TO_TIMEVAL(left);
|
||||||
if (!MemoryManager::copy_to_user_typed(old_timer, &result)) return err(EFAULT);
|
if (!MemoryManager::copy_to_user_typed(old_timer, &result)) return err(EFAULT);
|
||||||
@ -57,7 +60,7 @@ Result<u64> sys_setitimer(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
if (new_timer)
|
if (new_timer)
|
||||||
{
|
{
|
||||||
if (timer->clock) timer->clock->remove_from_timer_queue(timer);
|
timer->disarm();
|
||||||
|
|
||||||
struct itimerval itimer;
|
struct itimerval itimer;
|
||||||
if (!MemoryManager::copy_from_user_typed(new_timer, &itimer)) return err(EFAULT);
|
if (!MemoryManager::copy_from_user_typed(new_timer, &itimer)) return err(EFAULT);
|
||||||
@ -68,7 +71,7 @@ Result<u64> sys_setitimer(Registers*, SyscallArgs args)
|
|||||||
if (itimer.it_interval.tv_sec != 0 || itimer.it_interval.tv_usec != 0)
|
if (itimer.it_interval.tv_sec != 0 || itimer.it_interval.tv_usec != 0)
|
||||||
{
|
{
|
||||||
timer->restart = true;
|
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();
|
(itimer.it_interval.tv_sec * 1'000'000'000 + itimer.it_interval.tv_usec * 1000) / clock->resolution();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -76,11 +79,162 @@ Result<u64> sys_setitimer(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
if (itimer.it_value.tv_sec != 0 || itimer.it_value.tv_usec != 0)
|
if (itimer.it_value.tv_sec != 0 || itimer.it_value.tv_usec != 0)
|
||||||
{
|
{
|
||||||
timer->delta_ticks =
|
u64 ticks = (itimer.it_value.tv_sec * 1'000'000'000 + itimer.it_value.tv_usec * 1000) / clock->resolution();
|
||||||
(itimer.it_value.tv_sec * 1'000'000'000 + itimer.it_value.tv_usec * 1000) / clock->resolution();
|
timer->arm(clock, ticks);
|
||||||
clock->add_to_timer_queue(timer);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result<u64> 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<u64> 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<u64> 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<u64> 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;
|
||||||
|
}
|
||||||
|
@ -71,7 +71,7 @@ void Clock::update(const struct timespec& time)
|
|||||||
|
|
||||||
void Clock::add_to_timer_queue(Timer* timer)
|
void Clock::add_to_timer_queue(Timer* timer)
|
||||||
{
|
{
|
||||||
check(timer->clock == nullptr);
|
check(timer->active_clock == nullptr);
|
||||||
|
|
||||||
for (auto* t : m_timer_queue)
|
for (auto* t : m_timer_queue)
|
||||||
{
|
{
|
||||||
@ -86,12 +86,12 @@ void Clock::add_to_timer_queue(Timer* timer)
|
|||||||
|
|
||||||
m_timer_queue.append(timer);
|
m_timer_queue.append(timer);
|
||||||
|
|
||||||
timer->clock = this;
|
timer->active_clock = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Clock::remove_from_timer_queue(Timer* timer)
|
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);
|
auto maybe_next = m_timer_queue.next(timer);
|
||||||
if (maybe_next.has_value())
|
if (maybe_next.has_value())
|
||||||
@ -101,7 +101,7 @@ void Clock::remove_from_timer_queue(Timer* timer)
|
|||||||
}
|
}
|
||||||
m_timer_queue.remove(timer);
|
m_timer_queue.remove(timer);
|
||||||
|
|
||||||
timer->clock = nullptr;
|
timer->active_clock = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Clock::tick()
|
void Clock::tick()
|
||||||
@ -122,7 +122,7 @@ void Clock::tick()
|
|||||||
if (t->delta_ticks == 0)
|
if (t->delta_ticks == 0)
|
||||||
{
|
{
|
||||||
this->m_timer_queue.remove(t);
|
this->m_timer_queue.remove(t);
|
||||||
t->clock = nullptr;
|
t->active_clock = nullptr;
|
||||||
t->thread->send_signal(t->signo);
|
t->thread->send_signal(t->signo);
|
||||||
if (t->restart) timers_to_be_restarted.append(t);
|
if (t->restart) timers_to_be_restarted.append(t);
|
||||||
return true;
|
return true;
|
||||||
@ -131,7 +131,10 @@ void Clock::tick()
|
|||||||
return false;
|
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)
|
void Clock::get_time(struct timespec& out)
|
||||||
@ -146,7 +149,7 @@ long Clock::resolution()
|
|||||||
|
|
||||||
u64 Clock::ticks_left(Timer* timer)
|
u64 Clock::ticks_left(Timer* timer)
|
||||||
{
|
{
|
||||||
check(timer->clock == this);
|
check(timer->active_clock == this);
|
||||||
|
|
||||||
u64 total = 0;
|
u64 total = 0;
|
||||||
if (timer->delta_ticks == 0) return 0;
|
if (timer->delta_ticks == 0) return 0;
|
||||||
|
@ -221,7 +221,14 @@ namespace Scheduler
|
|||||||
MemoryManager::unmap_owned_and_free_vm(stack.bottom(), stack.bytes() / ARCH_PAGE_SIZE).release_value();
|
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;
|
delete thread;
|
||||||
|
|
||||||
|
@ -58,6 +58,28 @@ Result<FileDescriptor*> Thread::resolve_fd(int fd)
|
|||||||
return maybe_descriptor.value_ptr();
|
return maybe_descriptor.value_ptr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result<int> 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<Timer*> Thread::resolve_timerid(int tid)
|
||||||
|
{
|
||||||
|
if (tid < 0 || tid >= MAX_POSIX_TIMERS) return err(EBADF);
|
||||||
|
|
||||||
|
Option<Timer>& maybe_timer = posix_timers[tid];
|
||||||
|
|
||||||
|
if (!maybe_timer.has_value()) return err(EINVAL);
|
||||||
|
|
||||||
|
return maybe_timer.value_ptr();
|
||||||
|
}
|
||||||
|
|
||||||
Result<SharedPtr<VFS::Inode>> Thread::resolve_atfile(int dirfd, const String& path, bool allow_empty_path,
|
Result<SharedPtr<VFS::Inode>> Thread::resolve_atfile(int dirfd, const String& path, bool allow_empty_path,
|
||||||
bool follow_last_symlink, SharedPtr<VFS::Inode>* parent_inode)
|
bool follow_last_symlink, SharedPtr<VFS::Inode>* parent_inode)
|
||||||
{
|
{
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
#error "Unknown architecture."
|
#error "Unknown architecture."
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
constexpr int MAX_POSIX_TIMERS = 64;
|
||||||
|
|
||||||
class Timer;
|
class Timer;
|
||||||
|
|
||||||
enum class ThreadState
|
enum class ThreadState
|
||||||
@ -106,6 +108,11 @@ struct Thread : public LinkedListNode<Thread>
|
|||||||
Clock virtual_clock;
|
Clock virtual_clock;
|
||||||
Clock profiling_clock;
|
Clock profiling_clock;
|
||||||
|
|
||||||
|
Option<Timer> posix_timers[MAX_POSIX_TIMERS];
|
||||||
|
|
||||||
|
Result<int> allocate_timerid();
|
||||||
|
Result<Timer*> resolve_timerid(int id);
|
||||||
|
|
||||||
StaticString<128> cmdline;
|
StaticString<128> cmdline;
|
||||||
|
|
||||||
String current_directory_path = {};
|
String current_directory_path = {};
|
||||||
|
16
kernel/src/thread/Timer.cpp
Normal file
16
kernel/src/thread/Timer.cpp
Normal file
@ -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);
|
||||||
|
}
|
@ -9,10 +9,14 @@ class Timer : public LinkedListNode<Timer>
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
u64 delta_ticks { 0 };
|
u64 delta_ticks { 0 };
|
||||||
u64 total_ticks { 0 };
|
u64 interval_ticks { 0 };
|
||||||
Thread* thread;
|
Thread* thread;
|
||||||
int signo { SIGALRM };
|
int signo { SIGALRM };
|
||||||
bool restart { false };
|
bool restart { false };
|
||||||
|
|
||||||
Clock* clock { nullptr };
|
void disarm();
|
||||||
|
void arm(Clock* clock, u64 ticks);
|
||||||
|
|
||||||
|
Clock* active_clock { nullptr };
|
||||||
|
Clock* designated_clock { nullptr };
|
||||||
};
|
};
|
||||||
|
@ -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
|
#ifndef _BITS_ITIMER_H
|
||||||
#define _BITS_ITIMER_H
|
#define _BITS_ITIMER_H
|
||||||
@ -15,4 +15,10 @@ struct itimerval
|
|||||||
struct timeval it_value;
|
struct timeval it_value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct itimerspec
|
||||||
|
{
|
||||||
|
struct timespec it_interval;
|
||||||
|
struct timespec it_value;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
24
libc/include/bits/sigevent.h
Normal file
24
libc/include/bits/sigevent.h
Normal file
@ -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
|
@ -20,6 +20,7 @@ typedef __u32_t uid_t;
|
|||||||
typedef __u32_t gid_t;
|
typedef __u32_t gid_t;
|
||||||
typedef __u64_t nlink_t;
|
typedef __u64_t nlink_t;
|
||||||
typedef __i64_t suseconds_t;
|
typedef __i64_t suseconds_t;
|
||||||
|
typedef __i32_t timer_t;
|
||||||
|
|
||||||
typedef off_t fpos_t;
|
typedef off_t fpos_t;
|
||||||
|
|
||||||
|
@ -4,8 +4,11 @@
|
|||||||
#define _TIME_H
|
#define _TIME_H
|
||||||
|
|
||||||
#include <bits/clockid.h>
|
#include <bits/clockid.h>
|
||||||
|
#include <bits/itimer.h>
|
||||||
|
#include <bits/sigevent.h>
|
||||||
#include <bits/struct_tm.h>
|
#include <bits/struct_tm.h>
|
||||||
#include <bits/timespec.h>
|
#include <bits/timespec.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
typedef long int clock_t;
|
typedef long int clock_t;
|
||||||
|
|
||||||
@ -58,6 +61,18 @@ extern "C"
|
|||||||
/* Estimate the CPU time used by a process. */
|
/* Estimate the CPU time used by a process. */
|
||||||
clock_t clock(void);
|
clock_t clock(void);
|
||||||
|
|
||||||
|
/* Create a new POSIX timer. */
|
||||||
|
int timer_create(clockid_t clockid, struct sigevent* sevp, timer_t* timerid);
|
||||||
|
|
||||||
|
/* Get the current time remaining for a POSIX timer. */
|
||||||
|
int timer_gettime(timer_t timerid, struct itimerspec* value);
|
||||||
|
|
||||||
|
/* Set the time remaining and interval for a POSIX timer. */
|
||||||
|
int timer_settime(timer_t timerid, int flags, const struct itimerspec* new_value, struct itimerspec* old_value);
|
||||||
|
|
||||||
|
/* Delete an existing POSIX timer. */
|
||||||
|
int timer_delete(timer_t timerid);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -206,4 +206,28 @@ extern "C"
|
|||||||
long rc = syscall(SYS_setitimer, which, new_timer, old_timer);
|
long rc = syscall(SYS_setitimer, which, new_timer, old_timer);
|
||||||
__errno_return(rc, int);
|
__errno_return(rc, int);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int timer_create(clockid_t clockid, struct sigevent* sevp, timer_t* timerid)
|
||||||
|
{
|
||||||
|
long rc = syscall(SYS_timer_create, clockid, sevp, timerid);
|
||||||
|
__errno_return(rc, int);
|
||||||
|
}
|
||||||
|
|
||||||
|
int timer_gettime(timer_t timerid, struct itimerspec* value)
|
||||||
|
{
|
||||||
|
long rc = syscall(SYS_timer_gettime, timerid, value);
|
||||||
|
__errno_return(rc, int);
|
||||||
|
}
|
||||||
|
|
||||||
|
int timer_settime(timer_t timerid, int flags, const struct itimerspec* new_value, struct itimerspec* old_value)
|
||||||
|
{
|
||||||
|
long rc = syscall(SYS_timer_settime, timerid, flags, new_value, old_value);
|
||||||
|
__errno_return(rc, int);
|
||||||
|
}
|
||||||
|
|
||||||
|
int timer_delete(timer_t timerid)
|
||||||
|
{
|
||||||
|
long rc = syscall(SYS_timer_delete, timerid);
|
||||||
|
__errno_return(rc, int);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ set(SOURCES
|
|||||||
src/LocalClient.cpp
|
src/LocalClient.cpp
|
||||||
src/IPC.cpp
|
src/IPC.cpp
|
||||||
src/SharedMemory.cpp
|
src/SharedMemory.cpp
|
||||||
|
src/Timer.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(os ${SOURCES})
|
add_library(os ${SOURCES})
|
||||||
|
@ -3,15 +3,15 @@
|
|||||||
* @author apio (cloudapio.eu)
|
* @author apio (cloudapio.eu)
|
||||||
* @brief Base class for event-driven applications.
|
* @brief Base class for event-driven applications.
|
||||||
*
|
*
|
||||||
* @copyright Copyright (c) 2023, the Luna authors.
|
* @copyright Copyright (c) 2023-2024, the Luna authors.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <luna/HashMap.h>
|
#include <luna/HashMap.h>
|
||||||
#include <luna/LinkedList.h>
|
|
||||||
#include <luna/Vector.h>
|
#include <luna/Vector.h>
|
||||||
#include <os/Action.h>
|
#include <os/Action.h>
|
||||||
|
#include <os/Timer.h>
|
||||||
#include <sys/poll.h>
|
#include <sys/poll.h>
|
||||||
|
|
||||||
namespace os
|
namespace os
|
||||||
@ -61,15 +61,6 @@ namespace os
|
|||||||
*/
|
*/
|
||||||
Result<void> register_signal_handler(int sig, void (*handler)(int));
|
Result<void> register_signal_handler(int sig, void (*handler)(int));
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Register a function to be called in a certain amount of time.
|
|
||||||
*
|
|
||||||
* @param milliseconds The number of milliseconds to wait before invoking the function.
|
|
||||||
* @param callback The function to call when the time is up.
|
|
||||||
* @return Result<void> Whether the operation succeeded.
|
|
||||||
*/
|
|
||||||
Result<void> register_timer(long milliseconds, Action&& callback);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Run the event loop until it is asked to quit.
|
* @brief Run the event loop until it is asked to quit.
|
||||||
*
|
*
|
||||||
@ -98,12 +89,8 @@ namespace os
|
|||||||
|
|
||||||
int m_signal_pipe[2];
|
int m_signal_pipe[2];
|
||||||
|
|
||||||
struct Timer final : public LinkedListNode<Timer>
|
LinkedList<Timer> m_timer_list;
|
||||||
{
|
|
||||||
long target;
|
|
||||||
Action action;
|
|
||||||
};
|
|
||||||
|
|
||||||
LinkedList<Timer> m_timer_queue;
|
friend class Timer;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
39
libos/include/os/Timer.h
Normal file
39
libos/include/os/Timer.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* @file Timer.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Millisecond-precision timer.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <luna/LinkedList.h>
|
||||||
|
#include <luna/OwnedPtr.h>
|
||||||
|
#include <os/Action.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
namespace os
|
||||||
|
{
|
||||||
|
class EventLoop;
|
||||||
|
class Timer : LinkedListNode<Timer>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static Result<OwnedPtr<Timer>> create_singleshot(unsigned int milliseconds, Action&& action);
|
||||||
|
static Result<OwnedPtr<Timer>> create_repeating(unsigned int milliseconds, Action&& action);
|
||||||
|
|
||||||
|
~Timer();
|
||||||
|
|
||||||
|
private:
|
||||||
|
timer_t m_timerid { -1 };
|
||||||
|
bool m_repeating;
|
||||||
|
struct timespec m_interval;
|
||||||
|
Action m_action;
|
||||||
|
|
||||||
|
void check_if_should_invoke_action();
|
||||||
|
|
||||||
|
Timer() = default;
|
||||||
|
|
||||||
|
friend class EventLoop;
|
||||||
|
};
|
||||||
|
}
|
@ -3,7 +3,7 @@
|
|||||||
* @author apio (cloudapio.eu)
|
* @author apio (cloudapio.eu)
|
||||||
* @brief Base class for event-driven applications.
|
* @brief Base class for event-driven applications.
|
||||||
*
|
*
|
||||||
* @copyright Copyright (c) 2023, the Luna authors.
|
* @copyright Copyright (c) 2023-2024, the Luna authors.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -18,21 +18,6 @@
|
|||||||
|
|
||||||
static os::EventLoop* s_the = nullptr;
|
static os::EventLoop* s_the = nullptr;
|
||||||
|
|
||||||
static void alarm_milliseconds(long target)
|
|
||||||
{
|
|
||||||
struct itimerval itimer;
|
|
||||||
memset(&itimer.it_interval, 0, sizeof(itimer.it_interval));
|
|
||||||
itimer.it_value = { .tv_sec = target / 1000, .tv_usec = (target % 1000) * 1000 };
|
|
||||||
setitimer(ITIMER_REAL, &itimer, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static long get_monotonic_time_in_milliseconds()
|
|
||||||
{
|
|
||||||
struct timespec time;
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &time);
|
|
||||||
return time.tv_sec * 1000 + time.tv_nsec / 1'000'000;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace os
|
namespace os
|
||||||
{
|
{
|
||||||
EventLoop::EventLoop()
|
EventLoop::EventLoop()
|
||||||
@ -78,40 +63,6 @@ namespace os
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<void> EventLoop::register_timer(long milliseconds, Action&& callback)
|
|
||||||
{
|
|
||||||
long target = get_monotonic_time_in_milliseconds() + milliseconds;
|
|
||||||
|
|
||||||
auto* timer = TRY(make<Timer>());
|
|
||||||
timer->target = target;
|
|
||||||
timer->action = move(callback);
|
|
||||||
|
|
||||||
Timer* next = nullptr;
|
|
||||||
|
|
||||||
for (auto* t : m_timer_queue)
|
|
||||||
{
|
|
||||||
if (target < t->target)
|
|
||||||
{
|
|
||||||
next = t;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_timer_queue.first().value_or(nullptr) == next)
|
|
||||||
{
|
|
||||||
alarm_milliseconds(milliseconds);
|
|
||||||
m_timer_queue.prepend(timer);
|
|
||||||
}
|
|
||||||
else if (next == nullptr) { m_timer_queue.append(timer); }
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto previous = m_timer_queue.previous(next).value();
|
|
||||||
m_timer_queue.add_after(previous, timer);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
int EventLoop::run()
|
int EventLoop::run()
|
||||||
{
|
{
|
||||||
while (!m_should_quit)
|
while (!m_should_quit)
|
||||||
@ -168,19 +119,7 @@ namespace os
|
|||||||
|
|
||||||
void EventLoop::handle_timer_signal(int)
|
void EventLoop::handle_timer_signal(int)
|
||||||
{
|
{
|
||||||
auto& queue = the().m_timer_queue;
|
auto& queue = the().m_timer_list;
|
||||||
if (queue.count())
|
queue.delayed_for_each([](Timer* t) { t->check_if_should_invoke_action(); });
|
||||||
{
|
|
||||||
auto timer = queue.remove(queue.expect_first());
|
|
||||||
|
|
||||||
auto first = queue.first();
|
|
||||||
if (first.has_value())
|
|
||||||
{
|
|
||||||
alarm((unsigned int)(first.value()->target - get_monotonic_time_in_milliseconds()));
|
|
||||||
}
|
|
||||||
|
|
||||||
timer->action();
|
|
||||||
delete timer;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
86
libos/src/Timer.cpp
Normal file
86
libos/src/Timer.cpp
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/**
|
||||||
|
* @file Timer.cpp
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Millisecond-precision timer.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <os/EventLoop.h>
|
||||||
|
#include <os/Timer.h>
|
||||||
|
|
||||||
|
namespace os
|
||||||
|
{
|
||||||
|
Result<OwnedPtr<Timer>> Timer::create_singleshot(unsigned int milliseconds, Action&& action)
|
||||||
|
{
|
||||||
|
auto timer = TRY(adopt_owned_if_nonnull(new (std::nothrow) Timer()));
|
||||||
|
|
||||||
|
int status = timer_create(CLOCK_REALTIME, nullptr, &timer->m_timerid);
|
||||||
|
if (status < 0) return err(errno);
|
||||||
|
|
||||||
|
struct timespec time = { .tv_sec = milliseconds / 1000, .tv_nsec = (milliseconds % 1000) * 1'000'000 };
|
||||||
|
struct itimerspec itimer = { .it_interval = { .tv_sec = 0, .tv_nsec = 0 }, .it_value = time };
|
||||||
|
|
||||||
|
timer_settime(timer->m_timerid, 0, &itimer, nullptr);
|
||||||
|
|
||||||
|
timer->m_repeating = false;
|
||||||
|
timer->m_action = move(action);
|
||||||
|
|
||||||
|
EventLoop::the().m_timer_list.append(timer.ptr());
|
||||||
|
|
||||||
|
return timer;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<OwnedPtr<Timer>> Timer::create_repeating(unsigned int milliseconds, Action&& action)
|
||||||
|
{
|
||||||
|
auto timer = TRY(adopt_owned_if_nonnull(new (std::nothrow) Timer()));
|
||||||
|
|
||||||
|
int status = timer_create(CLOCK_REALTIME, nullptr, &timer->m_timerid);
|
||||||
|
if (status < 0) return err(errno);
|
||||||
|
|
||||||
|
struct timespec time = { .tv_sec = milliseconds / 1000, .tv_nsec = (milliseconds % 1000) * 1'000'000 };
|
||||||
|
struct itimerspec itimer = { .it_interval = { .tv_sec = 0, .tv_nsec = 0 }, .it_value = time };
|
||||||
|
|
||||||
|
timer_settime(timer->m_timerid, 0, &itimer, nullptr);
|
||||||
|
|
||||||
|
timer->m_repeating = true;
|
||||||
|
timer->m_interval = time;
|
||||||
|
timer->m_action = move(action);
|
||||||
|
|
||||||
|
EventLoop::the().m_timer_list.append(timer.ptr());
|
||||||
|
|
||||||
|
return timer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Timer::check_if_should_invoke_action()
|
||||||
|
{
|
||||||
|
struct itimerspec remaining;
|
||||||
|
if (timer_gettime(m_timerid, &remaining) < 0) return;
|
||||||
|
|
||||||
|
if (!remaining.it_value.tv_sec && !remaining.it_value.tv_nsec)
|
||||||
|
{
|
||||||
|
// Timer expired!
|
||||||
|
m_action();
|
||||||
|
|
||||||
|
if (!m_repeating)
|
||||||
|
{
|
||||||
|
timer_delete(m_timerid);
|
||||||
|
EventLoop::the().m_timer_list.remove(this);
|
||||||
|
m_timerid = -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
struct itimerspec itimer = { .it_interval = { .tv_sec = 0, .tv_nsec = 0 }, .it_value = m_interval };
|
||||||
|
|
||||||
|
timer_settime(m_timerid, 0, &itimer, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer::~Timer()
|
||||||
|
{
|
||||||
|
if (m_timerid >= 0) timer_delete(m_timerid);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user