Kernel/LibC: Implement alarm() and setitimer()

This makes vim able to start!
This commit is contained in:
Bananymous 2024-08-01 21:09:31 +03:00
parent da3b30cd94
commit a33b63d066
6 changed files with 139 additions and 0 deletions

View File

@ -19,6 +19,7 @@
#include <sys/mman.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <termios.h>
namespace LibELF { class LoadableELF; }
@ -77,6 +78,7 @@ namespace Kernel
BAN::ErrorOr<long> sys_wait(pid_t pid, int* stat_loc, int options);
BAN::ErrorOr<long> sys_sleep(int seconds);
BAN::ErrorOr<long> sys_nanosleep(const timespec* rqtp, timespec* rmtp);
BAN::ErrorOr<long> sys_setitimer(int which, const itimerval* value, itimerval* ovalue);
BAN::ErrorOr<long> sys_setpwd(const char* path);
BAN::ErrorOr<long> sys_getpwd(char* buffer, size_t size);
@ -207,6 +209,9 @@ namespace Kernel
BAN::ErrorOr<BAN::String> absolute_path_of(BAN::StringView) const;
// ONLY CALLED BY TIMER INTERRUPT
static void update_alarm_queue();
private:
Process(const Credentials&, pid_t pid, pid_t parent, pid_t sid, pid_t pgrp);
static Process* create_process(const Credentials&, pid_t parent, pid_t sid = 0, pid_t pgrp = 0);
@ -275,6 +280,9 @@ namespace Kernel
BAN::String m_working_directory;
BAN::Vector<Thread*> m_threads;
uint64_t m_alarm_interval_ns { 0 };
uint64_t m_alarm_wake_time_ns { 0 };
mutable SpinLock m_signal_lock;
struct sigaction m_signal_handlers[_SIGMAX + 1] { };
uint64_t m_signal_pending_mask { 0 };

View File

@ -337,6 +337,10 @@ done:
{
ASSERT(InterruptController::get().is_in_service(IRQ_TIMER));
InterruptController::get().eoi(IRQ_TIMER);
if (Processor::current_is_bsb())
Process::update_alarm_queue();
Processor::scheduler().timer_interrupt();
auto& current_thread = Thread::current();

View File

@ -27,6 +27,7 @@
namespace Kernel
{
static BAN::LinkedList<Process*> s_alarm_processes;
static BAN::Vector<Process*> s_processes;
static RecursiveSpinLock s_process_lock;
@ -640,6 +641,112 @@ namespace Kernel
return 0;
}
BAN::ErrorOr<long> Process::sys_setitimer(int which, const itimerval* value, itimerval* ovalue)
{
switch (which)
{
case ITIMER_PROF:
case ITIMER_REAL:
case ITIMER_VIRTUAL:
break;
default:
return BAN::Error::from_errno(EINVAL);
}
LockGuard _(m_process_lock);
if (value)
TRY(validate_pointer_access(value, sizeof(itimerval)));
if (ovalue)
TRY(validate_pointer_access(ovalue, sizeof(itimerval)));
{
SpinLockGuard _(s_process_lock);
const uint64_t current_ns = SystemTimer::get().ns_since_boot();
if (m_alarm_wake_time_ns)
{
for (auto it = s_alarm_processes.begin(); it != s_alarm_processes.end(); it++)
{
if (*it != this)
continue;
s_alarm_processes.remove(it);
break;
}
}
if (m_alarm_wake_time_ns && ovalue)
{
const uint64_t interval_us = m_alarm_interval_ns / 1000;
ovalue->it_interval = {
.tv_sec = static_cast<time_t>(interval_us / 1'000'000),
.tv_usec = static_cast<suseconds_t>(interval_us % 1'000'000),
};
const uint64_t remaining_us = current_ns < m_alarm_wake_time_ns ? (current_ns - m_alarm_wake_time_ns) / 1000 : 1;
ovalue->it_value = {
.tv_sec = static_cast<time_t>(remaining_us / 1'000'000),
.tv_usec = static_cast<suseconds_t>(remaining_us % 1'000'000),
};
m_alarm_interval_ns = 0;
m_alarm_wake_time_ns = 0;
}
if (value)
{
const uint64_t value_us = value->it_value.tv_sec * 1'000'000 + value->it_value.tv_usec;
const uint64_t interval_us = value->it_interval.tv_sec * 1'000'000 + value->it_interval.tv_usec;
if (value_us)
{
const uint64_t wake_time_ns = current_ns + value_us * 1000;
auto it = s_alarm_processes.begin();
while (it != s_alarm_processes.end() && (*it)->m_alarm_wake_time_ns < wake_time_ns)
it++;
TRY(s_alarm_processes.insert(it, this));
m_alarm_wake_time_ns = wake_time_ns;
m_alarm_interval_ns = interval_us * 1000;
}
}
}
return 0;
}
void Process::update_alarm_queue()
{
ASSERT(Processor::current_is_bsb());
SpinLockGuard _(s_process_lock);
const uint64_t current_ns = SystemTimer::get().ns_since_boot();
while (!s_alarm_processes.empty())
{
auto* process = s_alarm_processes.front();
if (current_ns < process->m_alarm_wake_time_ns)
break;
process->add_pending_signal(SIGALRM);
Processor::scheduler().unblock_thread(process->m_threads.front());
s_alarm_processes.remove(s_alarm_processes.begin());
if (process->m_alarm_interval_ns == 0)
continue;
process->m_alarm_wake_time_ns = current_ns + process->m_alarm_interval_ns;
auto it = s_alarm_processes.begin();
while (it != s_alarm_processes.end() && (*it)->m_alarm_wake_time_ns < process->m_alarm_wake_time_ns)
it++;
MUST(s_alarm_processes.insert(it, process));
}
}
BAN::ErrorOr<void> Process::create_file_or_dir(BAN::StringView path, mode_t mode)
{
switch (mode & Inode::Mode::TYPE_MASK)

View File

@ -86,6 +86,7 @@ __BEGIN_DECLS
O(SYS_SIGACTION, sigaction) \
O(SYS_SIGPENDING, sigpending) \
O(SYS_SIGPROCMASK, sigprocmask) \
O(SYS_SETITIMER, setitimer) \
enum Syscall
{

View File

@ -1,5 +1,7 @@
#include <sys/syscall.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
int gettimeofday(struct timeval* __restrict tp, void* __restrict tzp)
{
@ -13,3 +15,8 @@ int gettimeofday(struct timeval* __restrict tp, void* __restrict tzp)
tp->tv_usec = ts.tv_nsec / 1000;
return 0;
}
int setitimer(int which, const struct itimerval* __restrict value, struct itimerval* __restrict ovalue)
{
return syscall(SYS_SETITIMER, which, value, ovalue);
}

View File

@ -8,6 +8,7 @@
#include <string.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <unistd.h>
char** environ;
@ -514,3 +515,14 @@ int access(const char* path, int amode)
{
return syscall(SYS_ACCESS, path, amode);
}
unsigned alarm(unsigned seconds)
{
itimerval value, ovalue;
value.it_value.tv_sec = seconds;
value.it_value.tv_usec = 0;
value.it_interval.tv_sec = 0;
value.it_interval.tv_usec = 0;
setitimer(ITIMER_REAL, &value, &ovalue);
return ovalue.it_value.tv_sec;
}