mirror of
https://gitlab.com/sortix/sortix.git
synced 2023-02-13 20:55:38 -05:00
Add support for per-process timers.
This commit is contained in:
parent
12a8bb91aa
commit
df5deac29b
27 changed files with 1473 additions and 127 deletions
|
@ -350,6 +350,11 @@ sys/socket/socketpair.o \
|
|||
system.o \
|
||||
tfork.o \
|
||||
time.o \
|
||||
time/timer_create.o \
|
||||
time/timer_delete.o \
|
||||
time/timer_getoverrun.o \
|
||||
time/timer_gettime.o \
|
||||
time/timer_settime.o \
|
||||
tmpfile.o \
|
||||
tmpnam.o \
|
||||
truncateat.o \
|
||||
|
|
|
@ -51,7 +51,7 @@ __BEGIN_DECLS
|
|||
@include(ssize_t.h)
|
||||
@include(suseconds_t.h)
|
||||
@include(time_t.h)
|
||||
/* TODO: timer_t */
|
||||
@include(timer_t.h)
|
||||
/* TODO: trace*_t */
|
||||
@include(uid_t.h)
|
||||
@include(useconds_t.h)
|
||||
|
|
|
@ -54,25 +54,18 @@ struct tm
|
|||
|
||||
__END_DECLS
|
||||
#include <sortix/timespec.h>
|
||||
#include <sortix/itimerspec.h>
|
||||
__BEGIN_DECLS
|
||||
|
||||
/* TODO: This itimer stuff is replaced with another interface IIRC. */
|
||||
struct itimerspec
|
||||
{
|
||||
struct timespec it_interval;
|
||||
struct timespec it_value;
|
||||
};
|
||||
|
||||
@include(NULL.h)
|
||||
|
||||
#define CLOCKS_PER_SEC ((clock_t) 1000000)
|
||||
|
||||
__END_DECLS
|
||||
#include <sortix/clock.h>
|
||||
#include <sortix/time.h>
|
||||
__BEGIN_DECLS
|
||||
|
||||
#define TIMER_ABSTIME (1<<0)
|
||||
|
||||
/* getdate_err is omitted, use strptime */
|
||||
|
||||
char* asctime(const struct tm*);
|
||||
|
@ -101,7 +94,7 @@ size_t strftime_l(char* __restrict, size_t, const char* __restrict,
|
|||
char* strptime(const char* __restrict, const char* __restrict,
|
||||
struct tm* __restrict);
|
||||
time_t time(time_t*);
|
||||
int timer_create(clockid_t, struct sigevent* __restrict, time_t* __restrict);
|
||||
int timer_create(clockid_t, struct sigevent* __restrict, timer_t* __restrict);
|
||||
int timer_delete(timer_t);
|
||||
int timer_getoverrun(timer_t);
|
||||
int timer_gettime(timer_t, struct itimerspec*);
|
||||
|
|
37
libc/time/timer_create.cpp
Normal file
37
libc/time/timer_create.cpp
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||
|
||||
This file is part of the Sortix C Library.
|
||||
|
||||
The Sortix C Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
The Sortix C Library is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with the Sortix C Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
time/timer_create.cpp
|
||||
Creates a per-process timer.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
|
||||
DEFN_SYSCALL3(int, sys_timer_create, SYSCALL_TIMER_CREATE, clockid_t,
|
||||
struct sigevent*, timer_t*);
|
||||
|
||||
extern "C" int timer_create(clockid_t clockid, struct sigevent* restrict evp,
|
||||
timer_t* restrict timerid)
|
||||
{
|
||||
return sys_timer_create(clockid, evp, timerid);
|
||||
}
|
34
libc/time/timer_delete.cpp
Normal file
34
libc/time/timer_delete.cpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||
|
||||
This file is part of the Sortix C Library.
|
||||
|
||||
The Sortix C Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
The Sortix C Library is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with the Sortix C Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
time/timer_delete.cpp
|
||||
Delete a per-process timer.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
DEFN_SYSCALL1(int, sys_timer_delete, SYSCALL_TIMER_DELETE, timer_t);
|
||||
|
||||
extern "C" int timer_delete(timer_t timerid)
|
||||
{
|
||||
return sys_timer_delete(timerid);
|
||||
}
|
34
libc/time/timer_getoverrun.cpp
Normal file
34
libc/time/timer_getoverrun.cpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||
|
||||
This file is part of the Sortix C Library.
|
||||
|
||||
The Sortix C Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
The Sortix C Library is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with the Sortix C Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
time/timer_getoverrun.cpp
|
||||
Retrieve number of missed events on a timer.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
DEFN_SYSCALL1(int, sys_timer_getoverrun, SYSCALL_TIMER_GETOVERRUN, timer_t);
|
||||
|
||||
extern "C" int timer_getoverrun(timer_t timerid)
|
||||
{
|
||||
return sys_timer_getoverrun(timerid);
|
||||
}
|
35
libc/time/timer_gettime.cpp
Normal file
35
libc/time/timer_gettime.cpp
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||
|
||||
This file is part of the Sortix C Library.
|
||||
|
||||
The Sortix C Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
The Sortix C Library is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with the Sortix C Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
time/timer_gettime.cpp
|
||||
Get time remaining on a timer.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
DEFN_SYSCALL2(int, sys_timer_gettime, SYSCALL_TIMER_GETTIME, timer_t,
|
||||
struct itimerspec*);
|
||||
|
||||
extern "C" int timer_gettime(timer_t timerid, struct itimerspec* value)
|
||||
{
|
||||
return sys_timer_gettime(timerid, value);
|
||||
}
|
37
libc/time/timer_settime.cpp
Normal file
37
libc/time/timer_settime.cpp
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||
|
||||
This file is part of the Sortix C Library.
|
||||
|
||||
The Sortix C Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
The Sortix C Library is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with the Sortix C Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
time/timer_settime.cpp
|
||||
Set time remaining on a timer.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
DEFN_SYSCALL4(int, sys_timer_settime, SYSCALL_TIMER_SETTIME, timer_t, int,
|
||||
const struct itimerspec*, struct itimerspec*);
|
||||
|
||||
extern "C" int timer_settime(timer_t timerid, int flags,
|
||||
const struct itimerspec* restrict value,
|
||||
struct itimerspec* restrict ovalue)
|
||||
{
|
||||
return sys_timer_settime(timerid, flags, value, ovalue);
|
||||
}
|
|
@ -54,6 +54,7 @@ ifdef X86FAMILY
|
|||
$(CPU)/thread.o \
|
||||
$(CPU)/scheduler.o \
|
||||
$(CPU)/process.o \
|
||||
x86-family/time.o \
|
||||
x86-family/msr.o \
|
||||
x86-family/float.o \
|
||||
x86-family/x86-family.o
|
||||
|
@ -74,6 +75,7 @@ addralloc.o \
|
|||
ata.o \
|
||||
bga.o \
|
||||
calltrace.o \
|
||||
clock.o \
|
||||
com.o \
|
||||
copy.o \
|
||||
$(CPU)/calltrace.o \
|
||||
|
@ -123,7 +125,9 @@ textbuffer.o \
|
|||
textterminal.o \
|
||||
thread.o \
|
||||
time.o \
|
||||
timer.o \
|
||||
uart.o \
|
||||
user-timer.o \
|
||||
utf8.o \
|
||||
vga.o \
|
||||
vgatextbuffer.o \
|
||||
|
|
374
sortix/clock.cpp
Normal file
374
sortix/clock.cpp
Normal file
|
@ -0,0 +1,374 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
Sortix is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
clock.cpp
|
||||
Clock and timer facility.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include <assert.h>
|
||||
#include <timespec.h>
|
||||
|
||||
#include <sortix/kernel/platform.h>
|
||||
#include <sortix/kernel/clock.h>
|
||||
#include <sortix/kernel/interrupt.h>
|
||||
#include <sortix/kernel/kthread.h>
|
||||
#include <sortix/kernel/timer.h>
|
||||
#include <sortix/kernel/worker.h>
|
||||
|
||||
namespace Sortix {
|
||||
|
||||
Clock::Clock()
|
||||
{
|
||||
delay_timer = NULL;
|
||||
absolute_timer = NULL;
|
||||
current_time = timespec_nul();
|
||||
resolution = timespec_nul();
|
||||
clock_mutex = KTHREAD_MUTEX_INITIALIZER;
|
||||
clock_callable_from_interrupt = false;
|
||||
we_disabled_interrupts = false;
|
||||
}
|
||||
|
||||
Clock::~Clock()
|
||||
{
|
||||
// TODO: The best solution would probably be to cancel everything that is
|
||||
// waiting on us, but that is a bit dangerous since things have to be
|
||||
// notified carefully that they should not use stale pointers to this
|
||||
// clock. This is a bunch of work and since the clock is being
|
||||
// destroyed, you could argue that you shouldn't be using a clock
|
||||
// whose lifetime you don't control. Therefore assume that all users
|
||||
// of the clock has stopped using it.
|
||||
assert(!absolute_timer && !delay_timer);
|
||||
}
|
||||
|
||||
// This clock and timer facility is designed to work even from interrupt
|
||||
// handlers. For instance, this is needed by the uptime clock that is
|
||||
// incremented every timer interrupt. If we don't need interrupt handler safety,
|
||||
// we simply fall back on regular mutual exclusion.
|
||||
|
||||
void Clock::SetCallableFromInterrupts(bool callable_from_interrupts)
|
||||
{
|
||||
clock_callable_from_interrupt = callable_from_interrupts;
|
||||
}
|
||||
|
||||
void Clock::LockClock()
|
||||
{
|
||||
if ( clock_callable_from_interrupt )
|
||||
{
|
||||
if ( (we_disabled_interrupts = Interrupt::IsEnabled()) )
|
||||
Interrupt::Disable();
|
||||
}
|
||||
else
|
||||
kthread_mutex_lock(&clock_mutex);
|
||||
}
|
||||
|
||||
void Clock::UnlockClock()
|
||||
{
|
||||
if ( clock_callable_from_interrupt )
|
||||
{
|
||||
if ( we_disabled_interrupts )
|
||||
Interrupt::Enable();
|
||||
}
|
||||
else
|
||||
kthread_mutex_unlock(&clock_mutex);
|
||||
}
|
||||
|
||||
void Clock::Set(struct timespec* now, struct timespec* res)
|
||||
{
|
||||
LockClock();
|
||||
|
||||
if ( now )
|
||||
current_time = *now;
|
||||
if ( res )
|
||||
resolution = *res;
|
||||
|
||||
TriggerAbsolute();
|
||||
|
||||
UnlockClock();
|
||||
}
|
||||
|
||||
void Clock::Get(struct timespec* now, struct timespec* res)
|
||||
{
|
||||
LockClock();
|
||||
|
||||
if ( now )
|
||||
*now = current_time;
|
||||
if ( res )
|
||||
*res = resolution;
|
||||
|
||||
UnlockClock();
|
||||
}
|
||||
|
||||
// We maintain two queues of timers; one for timers that sleep for a duration
|
||||
// and one that that sleeps until a certain point in time. This lets us deal
|
||||
// nicely with non-monotonic clocks and simplifies the code. The absolute timers
|
||||
// queue is simply sorted after their wake-up time, while the delay timers queue
|
||||
// is sorted after their delays, where each node stores the delay between it and
|
||||
// its previous node (if any, otherwise just the actual time left of the timer).
|
||||
// This data structure allows constant time detection of whether a timer should
|
||||
// be fired and the double-linked queue allow constant-time cancellation - this
|
||||
// is at the expense of linear time insertion, but it is kinda okay since timers
|
||||
// that are soon will always be at the start (and hence quick to insert), while
|
||||
// timers in the far future will be last and the calling thread probably
|
||||
// wouldn't mind a little delay.
|
||||
|
||||
// TODO: If locking the clock means disabling interrupts, and a large numbers of
|
||||
// timers are attached to this clock, then inserting a timer becomes
|
||||
// expensive as the CPU locks up for a moment. Perhaps this is not as bad
|
||||
// as it theoretically could be?
|
||||
|
||||
void Clock::RegisterAbsolute(Timer* timer) // Lock acquired.
|
||||
{
|
||||
assert(!(timer->flags & TIMER_ACTIVE));
|
||||
timer->flags |= TIMER_ACTIVE;
|
||||
|
||||
Timer* before = NULL;
|
||||
for ( Timer* iter = absolute_timer; iter; iter = before->next_timer )
|
||||
if ( timespec_lt(timer->value.it_value, iter->value.it_value) )
|
||||
break;
|
||||
else
|
||||
before = iter;
|
||||
|
||||
timer->prev_timer = before;
|
||||
timer->next_timer = before ? before->next_timer : NULL;
|
||||
if ( timer->next_timer ) timer->next_timer->prev_timer = timer;
|
||||
(before ? before->next_timer : absolute_timer) = timer;
|
||||
}
|
||||
|
||||
void Clock::RegisterDelay(Timer* timer) // Lock acquired.
|
||||
{
|
||||
assert(!(timer->flags & TIMER_ACTIVE));
|
||||
timer->flags |= TIMER_ACTIVE;
|
||||
|
||||
Timer* before = NULL;
|
||||
struct timespec before_time = timespec_nul();
|
||||
for ( Timer* iter = delay_timer; iter; iter = before->next_timer )
|
||||
if ( timespec_lt(timer->value.it_value, iter->value.it_value) )
|
||||
break;
|
||||
else
|
||||
before = iter,
|
||||
before_time = timespec_add(before_time, iter->value.it_value);
|
||||
|
||||
timer->value.it_value = timespec_sub(timer->value.it_value, before_time);
|
||||
timer->prev_timer = before;
|
||||
timer->next_timer = before ? before->next_timer : NULL;
|
||||
if ( timer->next_timer ) timer->next_timer->prev_timer = timer;
|
||||
(before ? before->next_timer : delay_timer) = timer;
|
||||
|
||||
if ( timer->next_timer )
|
||||
timer->next_timer->value.it_value =
|
||||
timespec_sub(timer->next_timer->value.it_value, timer->value.it_value);
|
||||
}
|
||||
|
||||
void Clock::Register(Timer* timer)
|
||||
{
|
||||
if ( timer->flags & TIMER_ABSOLUTE )
|
||||
RegisterAbsolute(timer);
|
||||
else
|
||||
RegisterDelay(timer);
|
||||
}
|
||||
|
||||
void Clock::UnlinkAbsolute(Timer* timer) // Lock acquired.
|
||||
{
|
||||
(timer->prev_timer ? timer->prev_timer->next_timer : absolute_timer) = timer->next_timer;
|
||||
if ( timer->next_timer ) timer->next_timer->prev_timer = timer->prev_timer;
|
||||
timer->prev_timer = timer->next_timer = NULL;
|
||||
timer->flags &= ~TIMER_ACTIVE;
|
||||
}
|
||||
|
||||
void Clock::UnlinkDelay(Timer* timer) // Lock acquired.
|
||||
{
|
||||
(timer->prev_timer ? timer->prev_timer->next_timer : delay_timer) = timer->next_timer;
|
||||
if ( timer->next_timer ) timer->next_timer->prev_timer = timer->prev_timer;
|
||||
if ( timer->next_timer ) timer->next_timer->value.it_value = timespec_add(timer->next_timer->value.it_value, timer->value.it_value);
|
||||
timer->prev_timer = timer->next_timer = NULL;
|
||||
timer->flags &= ~TIMER_ACTIVE;
|
||||
}
|
||||
|
||||
void Clock::Unlink(Timer* timer) // Lock acquired.
|
||||
{
|
||||
if ( timer->flags & TIMER_ACTIVE )
|
||||
{
|
||||
if ( timer->flags & TIMER_ABSOLUTE )
|
||||
UnlinkAbsolute(timer);
|
||||
else
|
||||
UnlinkDelay(timer);
|
||||
}
|
||||
}
|
||||
|
||||
void Clock::Cancel(Timer* timer)
|
||||
{
|
||||
LockClock();
|
||||
|
||||
Unlink(timer);
|
||||
|
||||
while ( timer->flags & TIMER_FIRING )
|
||||
{
|
||||
UnlockClock();
|
||||
|
||||
// TODO: This busy-loop is rather inefficient. We could set up some
|
||||
// condition variable and wait on it. However, if the lock is
|
||||
// turning interrupts off, then there is no mutex we can use.
|
||||
kthread_yield();
|
||||
|
||||
LockClock();
|
||||
}
|
||||
|
||||
UnlockClock();
|
||||
}
|
||||
|
||||
void Clock::Advance(struct timespec duration)
|
||||
{
|
||||
LockClock();
|
||||
|
||||
current_time = timespec_add(current_time, duration);
|
||||
TriggerDelay(duration);
|
||||
TriggerAbsolute();
|
||||
|
||||
UnlockClock();
|
||||
}
|
||||
|
||||
// Fire timers that wait for a certain amount of time.
|
||||
void Clock::TriggerDelay(struct timespec unaccounted) // Lock acquired.
|
||||
{
|
||||
while ( Timer* timer = delay_timer )
|
||||
{
|
||||
if ( timespec_lt(unaccounted, timer->value.it_value) )
|
||||
{
|
||||
timer->value.it_value = timespec_sub(timer->value.it_value, unaccounted);
|
||||
break;
|
||||
}
|
||||
unaccounted = timespec_sub(unaccounted, timer->value.it_value);
|
||||
timer->value.it_value = timespec_nul();
|
||||
if ( (delay_timer = delay_timer->next_timer) )
|
||||
delay_timer->prev_timer = NULL;
|
||||
FireTimer(timer);
|
||||
}
|
||||
}
|
||||
|
||||
// Fire timers that wait until a certain point in time.
|
||||
void Clock::TriggerAbsolute() // Lock acquired.
|
||||
{
|
||||
while ( Timer* timer = absolute_timer )
|
||||
{
|
||||
if ( timespec_lt(timer->value.it_value, current_time) )
|
||||
break;
|
||||
if ( (absolute_timer = absolute_timer->next_timer) )
|
||||
absolute_timer->prev_timer = NULL;
|
||||
FireTimer(timer);
|
||||
}
|
||||
}
|
||||
|
||||
static void Clock__DoFireTimer(Timer* timer)
|
||||
{
|
||||
timer->callback(timer->clock, timer, timer->user);
|
||||
}
|
||||
|
||||
static void Clock__FireTimer(void* timer_ptr)
|
||||
{
|
||||
Timer* timer = (Timer*) timer_ptr;
|
||||
assert(timer->clock);
|
||||
|
||||
// Combine all the additionally pending events into a single one and notify
|
||||
// the caller of all the events that he missed because we couldn't call him
|
||||
// fast enough.
|
||||
timer->clock->LockClock();
|
||||
timer->num_overrun_events = timer->num_firings_scheduled;
|
||||
timer->num_firings_scheduled = 0;
|
||||
timer->clock->UnlockClock();
|
||||
|
||||
Clock__DoFireTimer(timer);
|
||||
|
||||
// If additional events happened during the time of the event handler, we'll
|
||||
// have to handle them because the firing bit is set. We'll schedule another
|
||||
// worker thread job and resume there, so this worker thread can continue to
|
||||
// do other important stuff.
|
||||
timer->clock->LockClock();
|
||||
if ( timer->num_firings_scheduled )
|
||||
Worker::Schedule(Clock__FireTimer, timer_ptr);
|
||||
// If this was the last event, we'll clear the firing bit and the advance
|
||||
// thread now has the responsibility of creating worker thread jobs.
|
||||
else
|
||||
timer->flags &= ~TIMER_FIRING;
|
||||
timer->clock->UnlockClock();
|
||||
}
|
||||
|
||||
static void Clock__FireTimer_InterruptWorker(void* timer_ptr_ptr, size_t)
|
||||
{
|
||||
void* timer_ptr = *((void**) timer_ptr_ptr);
|
||||
Clock__FireTimer(timer_ptr);
|
||||
}
|
||||
|
||||
void Clock::FireTimer(Timer* timer)
|
||||
{
|
||||
timer->flags &= ~TIMER_ACTIVE;
|
||||
|
||||
// If the CPU is currently interrupted, we call the timer callback directly
|
||||
// only if it is known to work when the interrupts are disabled on this CPU.
|
||||
// Otherwise, we forward the timer pointer to a special interrupt-safe
|
||||
// worker thread that'll run the callback normally.
|
||||
if ( !Interrupt::IsEnabled() )
|
||||
{
|
||||
if ( timer->flags & TIMER_FUNC_INTERRUPT_HANDLER )
|
||||
Clock__DoFireTimer(timer);
|
||||
else if ( timer->flags & TIMER_FIRING )
|
||||
timer->num_firings_scheduled++;
|
||||
else
|
||||
{
|
||||
timer->flags |= TIMER_FIRING;
|
||||
Interrupt::ScheduleWork(Clock__FireTimer_InterruptWorker, &timer,
|
||||
sizeof(timer));
|
||||
}
|
||||
}
|
||||
|
||||
// Normally, we will run the timer callback in a worker thread, but as an
|
||||
// optimization, if the callback is known to be short and simple and safely
|
||||
// handles this situation, we'll simply call it from the current thread.
|
||||
else
|
||||
{
|
||||
if ( timer->flags & TIMER_FUNC_ADVANCE_THREAD )
|
||||
Clock__DoFireTimer(timer);
|
||||
else if ( timer->flags & TIMER_FIRING )
|
||||
timer->num_firings_scheduled++;
|
||||
else
|
||||
{
|
||||
timer->flags |= TIMER_FIRING;
|
||||
Worker::Schedule(Clock__FireTimer, timer);
|
||||
}
|
||||
}
|
||||
|
||||
// Rearm the timer only if it is periodic.
|
||||
if ( timespec_le(timer->value.it_interval, timespec_nul()) )
|
||||
return;
|
||||
|
||||
// TODO: If the period is too short (such a single nanosecond) on a delay
|
||||
// timer, then it will try to spend each nanosecond avanced carefully
|
||||
// and reliably schedule a shitload of firings. Not only that, but it
|
||||
// will also loop this function many million timers per tick!
|
||||
|
||||
// TODO: Throtte the timer if firing while the callback is still running!
|
||||
// TODO: Doesn't reload properly for absolute timers!
|
||||
if ( timer->flags & TIMER_ABSOLUTE )
|
||||
timer->value.it_value = timespec_add(timer->value.it_value, timer->value.it_interval);
|
||||
else
|
||||
timer->value.it_value = timer->value.it_interval;
|
||||
Register(timer);
|
||||
}
|
||||
|
||||
} // namespace Sortix
|
|
@ -54,7 +54,7 @@ typedef long int __ssize_t;
|
|||
#else
|
||||
typedef int __ssize_t;
|
||||
#endif
|
||||
typedef void* __timer_t;
|
||||
typedef __uintptr_t __timer_t;
|
||||
typedef __SIZE_TYPE__ __socklen_t;
|
||||
|
||||
#if defined(SORTIX_KERNEL) || defined(LIBC_LIBRARY)
|
||||
|
|
40
sortix/include/sortix/itimerspec.h
Normal file
40
sortix/include/sortix/itimerspec.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
Sortix is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
sortix/itimerspec.h
|
||||
Declaration of the itimerspec structure.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef INCLUDE_SORTIX_ITIMERSPEC_H
|
||||
#define INCLUDE_SORTIX_ITIMERSPEC_H
|
||||
|
||||
#include <features.h>
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
struct itimerspec
|
||||
{
|
||||
struct timespec it_interval;
|
||||
struct timespec it_value;
|
||||
};
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif
|
80
sortix/include/sortix/kernel/clock.h
Normal file
80
sortix/include/sortix/kernel/clock.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
Sortix is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
sortix/kernel/clock.h
|
||||
A virtual clock that can be measured and waited upon.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef INCLUDE_SORTIX_KERNEL_CLOCK_H
|
||||
#define INCLUDE_SORTIX_KERNEL_CLOCK_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <sortix/timespec.h>
|
||||
|
||||
#include <sortix/kernel/kthread.h>
|
||||
|
||||
namespace Sortix {
|
||||
|
||||
class Clock;
|
||||
class Timer;
|
||||
|
||||
class Clock
|
||||
{
|
||||
public:
|
||||
Clock();
|
||||
~Clock();
|
||||
|
||||
public:
|
||||
Timer* delay_timer;
|
||||
Timer* absolute_timer;
|
||||
struct timespec current_time;
|
||||
struct timespec resolution;
|
||||
kthread_mutex_t clock_mutex;
|
||||
bool clock_callable_from_interrupt;
|
||||
bool we_disabled_interrupts;
|
||||
|
||||
public:
|
||||
void SetCallableFromInterrupts(bool callable_from_interrupts);
|
||||
void Set(struct timespec* now, struct timespec* res);
|
||||
void Get(struct timespec* now, struct timespec* res);
|
||||
void Advance(struct timespec duration);
|
||||
void Register(Timer* timer);
|
||||
void Unlink(Timer* timer);
|
||||
void Cancel(Timer* timer);
|
||||
void LockClock();
|
||||
void UnlockClock();
|
||||
|
||||
public: // These should only be called if the clock is locked.
|
||||
void RegisterAbsolute(Timer* timer);
|
||||
void RegisterDelay(Timer* timer);
|
||||
void UnlinkAbsolute(Timer* timer);
|
||||
void UnlinkDelay(Timer* timer);
|
||||
|
||||
private: // These should only be called if the clock is locked.
|
||||
void FireTimer(Timer* timer);
|
||||
void TriggerDelay(struct timespec unaccounted);
|
||||
void TriggerAbsolute();
|
||||
|
||||
};
|
||||
|
||||
} // namespace Sortix
|
||||
|
||||
#endif
|
|
@ -33,13 +33,18 @@
|
|||
|
||||
#include <sortix/timespec.h>
|
||||
|
||||
namespace Sortix {
|
||||
class Clock;
|
||||
} // namespace Sortix
|
||||
|
||||
namespace Sortix {
|
||||
namespace Time {
|
||||
|
||||
void Init();
|
||||
struct timespec Get(clockid_t clock, struct timespec* res = NULL);
|
||||
bool TryGet(clockid_t clock, struct timespec* time, struct timespec* res = NULL);
|
||||
|
||||
void Start();
|
||||
void OnTick(struct timespec tick_period);
|
||||
struct timespec Get(clockid_t clock);
|
||||
Clock* GetClock(clockid_t clock);
|
||||
|
||||
} // namespace Time
|
||||
} // namespace Sortix
|
||||
|
|
79
sortix/include/sortix/kernel/timer.h
Normal file
79
sortix/include/sortix/kernel/timer.h
Normal file
|
@ -0,0 +1,79 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
Sortix is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
sortix/kernel/timer.h
|
||||
A virtual timer that triggers an action in a worker thread when triggered.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef INCLUDE_SORTIX_KERNEL_TIMER_H
|
||||
#define INCLUDE_SORTIX_KERNEL_TIMER_H
|
||||
|
||||
#include <sortix/timespec.h>
|
||||
#include <sortix/itimerspec.h>
|
||||
|
||||
#include <sortix/kernel/kthread.h>
|
||||
|
||||
namespace Sortix {
|
||||
|
||||
class Clock;
|
||||
class Timer;
|
||||
|
||||
const int TIMER_ABSOLUTE = 1 << 0;
|
||||
const int TIMER_ACTIVE = 1 << 1;
|
||||
const int TIMER_FIRING = 1 << 2;
|
||||
const int TIMER_FUNC_INTERRUPT_HANDLER = 1 << 3;
|
||||
const int TIMER_FUNC_ADVANCE_THREAD = 1 << 4;
|
||||
|
||||
class Timer
|
||||
{
|
||||
public:
|
||||
Timer();
|
||||
~Timer();
|
||||
|
||||
public:
|
||||
struct itimerspec value;
|
||||
Clock* clock;
|
||||
Timer* prev_timer;
|
||||
Timer* next_timer;
|
||||
void (*callback)(Clock* clock, Timer* timer, void* user);
|
||||
void* user;
|
||||
size_t num_firings_scheduled;
|
||||
size_t num_overrun_events;
|
||||
int flags;
|
||||
|
||||
private:
|
||||
void Fire();
|
||||
void GetInternal(struct itimerspec* current);
|
||||
|
||||
public:
|
||||
void Attach(Clock* the_clock);
|
||||
void Detach();
|
||||
bool IsAttached() const { return clock; }
|
||||
void Cancel();
|
||||
Clock* GetClock() const { return clock; }
|
||||
void Get(struct itimerspec* current);
|
||||
void Set(struct itimerspec* value, struct itimerspec* ovalue, int flags,
|
||||
void (*callback)(Clock*, Timer*, void*), void* user);
|
||||
|
||||
};
|
||||
|
||||
} // namespace Sortix
|
||||
|
||||
#endif
|
50
sortix/include/sortix/kernel/user-timer.h
Normal file
50
sortix/include/sortix/kernel/user-timer.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
Sortix is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
sortix/kernel/user-timer.h
|
||||
Timer facility provided to user-space.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef INCLUDE_SORTIX_KERNEL_USER_TIMER_H
|
||||
#define INCLUDE_SORTIX_KERNEL_USER_TIMER_H
|
||||
|
||||
#include <sortix/sigevent.h>
|
||||
|
||||
#include <sortix/kernel/timer.h>
|
||||
|
||||
namespace Sortix {
|
||||
|
||||
class Process;
|
||||
|
||||
struct UserTimer
|
||||
{
|
||||
Timer timer;
|
||||
struct sigevent event;
|
||||
Process* process;
|
||||
timer_t timerid;
|
||||
|
||||
public:
|
||||
static void Init();
|
||||
|
||||
};
|
||||
|
||||
} // namespace Sortix
|
||||
|
||||
#endif
|
54
sortix/include/sortix/sigevent.h
Normal file
54
sortix/include/sortix/sigevent.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
Sortix is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
sortix/sigevent.h
|
||||
Declares the sigevent structure.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef INCLUDE_SORTIX_SIGEVENT_H
|
||||
#define INCLUDE_SORTIX_SIGEVENT_H
|
||||
|
||||
#include <features.h>
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
#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);
|
||||
/*pthread_attr_t* sigev_notify_attributes;*/
|
||||
void* sigev_notify_attributes;
|
||||
};
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif
|
|
@ -118,6 +118,11 @@
|
|||
#define SYSCALL_WRITEV 94
|
||||
#define SYSCALL_PREADV 95
|
||||
#define SYSCALL_PWRITEV 96
|
||||
#define SYSCALL_MAX_NUM 97 /* index of highest constant + 1 */
|
||||
#define SYSCALL_TIMER_CREATE 97
|
||||
#define SYSCALL_TIMER_DELETE 98
|
||||
#define SYSCALL_TIMER_GETOVERRUN 99
|
||||
#define SYSCALL_TIMER_GETTIME 100
|
||||
#define SYSCALL_TIMER_SETTIME 101
|
||||
#define SYSCALL_MAX_NUM 102 /* index of highest constant + 1 */
|
||||
|
||||
#endif
|
||||
|
|
36
sortix/include/sortix/time.h
Normal file
36
sortix/include/sortix/time.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
Sortix is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
sortix/time.h
|
||||
Declarations for the kernel time interfaces.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef INCLUDE_SORTIX_TIME_H
|
||||
#define INCLUDE_SORTIX_TIME_H
|
||||
|
||||
#include <features.h>
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
#define TIMER_ABSTIME (1<<0)
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif
|
|
@ -47,6 +47,7 @@
|
|||
#include <sortix/kernel/scheduler.h>
|
||||
#include <sortix/kernel/fcache.h>
|
||||
#include <sortix/kernel/string.h>
|
||||
#include <sortix/kernel/user-timer.h>
|
||||
|
||||
#include <sortix/fcntl.h>
|
||||
#include <sortix/stat.h>
|
||||
|
@ -247,6 +248,9 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo)
|
|||
// Stage 2. Transition to Multithreaded Environment
|
||||
//
|
||||
|
||||
// Initialize the clocks.
|
||||
Time::Init();
|
||||
|
||||
// Initialize the process system.
|
||||
Process::Init();
|
||||
|
||||
|
@ -296,7 +300,7 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo)
|
|||
Float::Init();
|
||||
|
||||
// The time driver will run the scheduler on the next timer interrupt.
|
||||
Time::Init();
|
||||
Time::Start();
|
||||
|
||||
// Become the system idle thread.
|
||||
SystemIdleThread(NULL);
|
||||
|
@ -422,6 +426,9 @@ static void BootThread(void* /*user*/)
|
|||
// Initialize poll system call.
|
||||
Poll::Init();
|
||||
|
||||
// Initialize per-process timers.
|
||||
UserTimer::Init();
|
||||
|
||||
// Initialize the kernel information query syscall.
|
||||
Info::Init();
|
||||
|
||||
|
|
|
@ -123,6 +123,7 @@ namespace Sortix
|
|||
threadlock = KTHREAD_MUTEX_INITIALIZER;
|
||||
ptrlock = KTHREAD_MUTEX_INITIALIZER;
|
||||
idlock = KTHREAD_MUTEX_INITIALIZER;
|
||||
user_timers_lock = KTHREAD_MUTEX_INITIALIZER;
|
||||
mmapfrom = 0x80000000UL;
|
||||
exitstatus = -1;
|
||||
pid = AllocatePID();
|
||||
|
@ -234,6 +235,16 @@ namespace Sortix
|
|||
// This can't be called if the process is still alive.
|
||||
assert(!firstthread);
|
||||
|
||||
// Disarm and detach all the timers in the process.
|
||||
for ( timer_t i = 0; i < PROCESS_TIMER_NUM_MAX; i++ )
|
||||
{
|
||||
if ( user_timers[i].timer.IsAttached() )
|
||||
{
|
||||
user_timers[i].timer.Cancel();
|
||||
user_timers[i].timer.Detach();
|
||||
}
|
||||
}
|
||||
|
||||
// We need to temporarily reload the correct addrese space of the dying
|
||||
// process such that we can unmap and free its memory.
|
||||
addr_t prevaddrspace = curthread->SwitchAddressSpace(addrspace);
|
||||
|
|
|
@ -25,10 +25,18 @@
|
|||
#ifndef SORTIX_PROCESS_H
|
||||
#define SORTIX_PROCESS_H
|
||||
|
||||
#include "cpu.h"
|
||||
#include <sortix/fork.h>
|
||||
|
||||
#include <sortix/kernel/clock.h>
|
||||
#include <sortix/kernel/kthread.h>
|
||||
#include <sortix/kernel/refcount.h>
|
||||
#include <sortix/fork.h>
|
||||
#include <sortix/kernel/time.h>
|
||||
#include <sortix/kernel/timer.h>
|
||||
#include <sortix/kernel/user-timer.h>
|
||||
|
||||
#include "cpu.h"
|
||||
|
||||
#define PROCESS_TIMER_NUM_MAX 32
|
||||
|
||||
namespace Sortix
|
||||
{
|
||||
|
@ -38,6 +46,7 @@ namespace Sortix
|
|||
class DescriptorTable;
|
||||
class MountTable;
|
||||
struct ProcessSegment;
|
||||
struct ProcessTimer;
|
||||
struct ioctx_struct;
|
||||
typedef struct ioctx_struct ioctx_t;
|
||||
|
||||
|
@ -133,6 +142,10 @@ namespace Sortix
|
|||
public:
|
||||
ProcessSegment* segments;
|
||||
|
||||
public:
|
||||
kthread_mutex_t user_timers_lock;
|
||||
UserTimer user_timers[PROCESS_TIMER_NUM_MAX];
|
||||
|
||||
public:
|
||||
int Execute(const char* programname, const uint8_t* program,
|
||||
size_t programsize, int argc, const char* const* argv,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
|
|
134
sortix/time.cpp
134
sortix/time.cpp
|
@ -29,13 +29,12 @@
|
|||
#include <timespec.h>
|
||||
|
||||
#include <sortix/clock.h>
|
||||
#include <sortix/timespec.h>
|
||||
|
||||
#include <sortix/kernel/platform.h>
|
||||
#include <sortix/kernel/syscall.h>
|
||||
#include <sortix/kernel/copy.h>
|
||||
#include <sortix/kernel/interrupt.h>
|
||||
#include <sortix/kernel/syscall.h>
|
||||
#include <sortix/kernel/clock.h>
|
||||
#include <sortix/kernel/time.h>
|
||||
#include <sortix/kernel/scheduler.h>
|
||||
|
||||
|
@ -46,130 +45,51 @@
|
|||
#include "serialterminal.h"
|
||||
#endif
|
||||
|
||||
#include "cpu.h"
|
||||
|
||||
#if !defined(PLATFORM_X86_FAMILY)
|
||||
#error Provide a time implementation for this CPU.
|
||||
#endif
|
||||
|
||||
namespace Sortix {
|
||||
namespace Time {
|
||||
|
||||
struct timespec since_boot;
|
||||
struct timespec since_boot_period;
|
||||
uint16_t since_boot_divisor;
|
||||
void CPUInit();
|
||||
|
||||
static uint16_t DivisorOfFrequency(long frequency)
|
||||
{
|
||||
// The value we send to the PIT is the value to divide it's input clock
|
||||
// (1193180 Hz) by, to get our required frequency. Note that the divisor
|
||||
// must be small enough to fit into 16 bits.
|
||||
return 1193180 / frequency;
|
||||
}
|
||||
Clock* realtime_clock;
|
||||
Clock* uptime_clock;
|
||||
|
||||
static long FrequencyOfDivisor(uint16_t divisor)
|
||||
{
|
||||
return 1193180 / divisor;
|
||||
}
|
||||
|
||||
static long RealFrequencyOfFrequency(long frequency)
|
||||
{
|
||||
return FrequencyOfDivisor(DivisorOfFrequency(frequency));
|
||||
}
|
||||
|
||||
static struct timespec PeriodOfFrequency(long frequency)
|
||||
{
|
||||
long period_ns = 1000000000L / frequency;
|
||||
return timespec_make(0, period_ns);
|
||||
}
|
||||
|
||||
static void RequestIRQ0(uint16_t divisor)
|
||||
{
|
||||
CPU::OutPortB(0x43, 0x36);
|
||||
CPU::OutPortB(0x40, divisor >> 0 & 0xFF);
|
||||
CPU::OutPortB(0x40, divisor >> 8 & 0xFF);
|
||||
}
|
||||
|
||||
static void InitializeTimer(long frequency)
|
||||
{
|
||||
long real_frequence = RealFrequencyOfFrequency(frequency);
|
||||
since_boot_divisor = DivisorOfFrequency(frequency);
|
||||
since_boot = timespec_nul();
|
||||
since_boot_period = PeriodOfFrequency(real_frequence);
|
||||
RequestIRQ0(since_boot_divisor);
|
||||
}
|
||||
|
||||
static void OnIRQ0(CPU::InterruptRegisters* regs, void* /*user*/)
|
||||
{
|
||||
since_boot = timespec_add(since_boot, since_boot_period);
|
||||
Scheduler::Switch(regs);
|
||||
|
||||
// TODO: There is a horrible bug that causes Sortix to only receive
|
||||
// one IRQ0 on my laptop, but it works in virtual machines. But
|
||||
// re-requesting an addtional time seems to work. Hacky and ugly.
|
||||
static bool did_ugly_irq0_hack = false;
|
||||
if ( !did_ugly_irq0_hack )
|
||||
{
|
||||
RequestIRQ0(since_boot_divisor);
|
||||
did_ugly_irq0_hack = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool TryGet(clockid_t clock, struct timespec* time, struct timespec* res)
|
||||
Clock* GetClock(clockid_t clock)
|
||||
{
|
||||
switch ( clock )
|
||||
{
|
||||
case CLOCK_REALTIME:
|
||||
return errno = ENOTSUP, false;
|
||||
case CLOCK_MONOTONIC:
|
||||
case CLOCK_BOOT:
|
||||
case CLOCK_INIT:
|
||||
// TODO: Is is possible to implement the below as atomic operations?
|
||||
Interrupt::Disable();
|
||||
if ( time )
|
||||
*time = since_boot;
|
||||
if ( res )
|
||||
*res = since_boot_period;
|
||||
Interrupt::Enable();
|
||||
return true;
|
||||
default:
|
||||
return errno = EINVAL, false;
|
||||
case CLOCK_REALTIME: return realtime_clock;
|
||||
case CLOCK_BOOT: return uptime_clock;
|
||||
case CLOCK_INIT: return uptime_clock;
|
||||
case CLOCK_MONOTONIC: return uptime_clock;
|
||||
default: return errno = ENOTSUP, (Clock*) NULL;
|
||||
}
|
||||
}
|
||||
|
||||
struct timespec Get(clockid_t clock, struct timespec* res)
|
||||
struct timespec Get(clockid_t clockid)
|
||||
{
|
||||
struct timespec ret;
|
||||
if ( !TryGet(clock, &ret, res) )
|
||||
return timespec_nul();
|
||||
return ret;
|
||||
Clock* clock = GetClock(clockid);
|
||||
if ( clock )
|
||||
{
|
||||
struct timespec now;
|
||||
clock->Get(&now, NULL);
|
||||
return now;
|
||||
}
|
||||
return timespec_nul();
|
||||
}
|
||||
|
||||
uintmax_t MicrosecondsOfTimespec(struct timespec time)
|
||||
void OnTick(struct timespec tick_period)
|
||||
{
|
||||
uintmax_t seconds = time.tv_sec;
|
||||
uintmax_t nano_seconds = time.tv_nsec;
|
||||
return seconds * 1000000 + nano_seconds / 1000;
|
||||
}
|
||||
|
||||
// TODO: This system call is for compatibility. Provide user-space with better
|
||||
// facilities such as sys_clock_gettime.
|
||||
static int sys_uptime(uintmax_t* usecssinceboot)
|
||||
{
|
||||
struct timespec now;
|
||||
if ( !TryGet(CLOCK_BOOT, &now, NULL) )
|
||||
return -1;
|
||||
uintmax_t ret = MicrosecondsOfTimespec(now);
|
||||
if ( !CopyToUser(usecssinceboot, &ret, sizeof(ret)) )
|
||||
return -1;
|
||||
return 0;
|
||||
realtime_clock->Advance(tick_period);
|
||||
uptime_clock->Advance(tick_period);
|
||||
}
|
||||
|
||||
void Init()
|
||||
{
|
||||
Interrupt::RegisterHandler(Interrupt::IRQ0, &OnIRQ0, NULL);
|
||||
Syscall::Register(SYSCALL_UPTIME, (void*) sys_uptime);
|
||||
InitializeTimer(100/*Hz*/);
|
||||
if ( !(realtime_clock = new Clock()) )
|
||||
Panic("Unable to allocate realtime clock");
|
||||
if ( !(uptime_clock = new Clock()) )
|
||||
Panic("Unable to allocate uptime clock");
|
||||
CPUInit();
|
||||
}
|
||||
|
||||
} // namespace Time
|
||||
|
|
134
sortix/timer.cpp
Normal file
134
sortix/timer.cpp
Normal file
|
@ -0,0 +1,134 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
Sortix is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
timer.cpp
|
||||
Clock and timer facility.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include <assert.h>
|
||||
#include <timespec.h>
|
||||
|
||||
#include <sortix/kernel/platform.h>
|
||||
#include <sortix/kernel/clock.h>
|
||||
#include <sortix/kernel/kthread.h>
|
||||
#include <sortix/kernel/interrupt.h>
|
||||
#include <sortix/kernel/timer.h>
|
||||
|
||||
namespace Sortix {
|
||||
|
||||
// The caller should protect the timer using a lock if multiple threads can
|
||||
// access it.
|
||||
|
||||
Timer::Timer()
|
||||
{
|
||||
value = { timespec_nul(), timespec_nul() };
|
||||
clock = NULL;
|
||||
prev_timer = NULL;
|
||||
next_timer = NULL;
|
||||
callback = NULL;
|
||||
user = NULL;
|
||||
num_firings_scheduled = 0;
|
||||
num_overrun_events = 0;
|
||||
flags = 0;
|
||||
}
|
||||
|
||||
Timer::~Timer()
|
||||
{
|
||||
// TODO: Is this the right thing?
|
||||
// The user of this object should cancel all pending triggers before calling
|
||||
// the destructor. We could try to cancel the timer, but if we fail the race
|
||||
// the handler function will be called, and that function may access the
|
||||
// timer object. We'd then have to wait for the function to finish and then
|
||||
// continue the destuction. This is a bit complex and fragile, so we'll just
|
||||
// make it the rule that all outstanding requests must be cancelled before
|
||||
// our destruction. The caller should know better than we how to cancel.
|
||||
assert(!(flags & TIMER_ACTIVE));
|
||||
}
|
||||
|
||||
void Timer::Attach(Clock* the_clock)
|
||||
{
|
||||
assert(!clock);
|
||||
assert(the_clock);
|
||||
clock = the_clock;
|
||||
}
|
||||
|
||||
void Timer::Detach()
|
||||
{
|
||||
assert(!(flags & TIMER_ACTIVE));
|
||||
assert(clock);
|
||||
clock = NULL;
|
||||
}
|
||||
|
||||
void Timer::Cancel()
|
||||
{
|
||||
if ( clock )
|
||||
clock->Cancel(this);
|
||||
}
|
||||
|
||||
void Timer::GetInternal(struct itimerspec* current)
|
||||
{
|
||||
if ( !(this->flags & TIMER_ACTIVE ) )
|
||||
current->it_value = timespec_nul(),
|
||||
current->it_interval = timespec_nul();
|
||||
else if ( flags & TIMER_ABSOLUTE )
|
||||
current->it_value = timespec_sub(value.it_value, clock->current_time),
|
||||
current->it_interval = value.it_interval;
|
||||
else
|
||||
*current = value;
|
||||
}
|
||||
|
||||
void Timer::Get(struct itimerspec* current)
|
||||
{
|
||||
assert(clock);
|
||||
|
||||
clock->LockClock();
|
||||
GetInternal(current);
|
||||
clock->UnlockClock();
|
||||
}
|
||||
|
||||
void Timer::Set(struct itimerspec* value, struct itimerspec* ovalue, int flags,
|
||||
void (*callback)(Clock*, Timer*, void*), void* user)
|
||||
{
|
||||
assert(clock);
|
||||
|
||||
clock->LockClock();
|
||||
|
||||
// Dequeue this timer if it is already armed.
|
||||
if ( flags & TIMER_ACTIVE )
|
||||
clock->Unlink(this);
|
||||
|
||||
// Let the caller know how much time was left on the timer.
|
||||
if ( ovalue )
|
||||
GetInternal(ovalue);
|
||||
|
||||
// Arm the timer if a value was specified.
|
||||
if ( timespec_lt(timespec_nul(), value->it_value) )
|
||||
{
|
||||
this->value = *value;
|
||||
this->flags = flags;
|
||||
this->callback = callback;
|
||||
this->user = user;
|
||||
clock->Register(this);
|
||||
}
|
||||
|
||||
clock->UnlockClock();
|
||||
}
|
||||
|
||||
} // namespace Sortix
|
237
sortix/user-timer.cpp
Normal file
237
sortix/user-timer.cpp
Normal file
|
@ -0,0 +1,237 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
Sortix is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
user-timer.cpp
|
||||
Timers that send signals when triggered.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <timespec.h>
|
||||
|
||||
#include <sortix/clock.h>
|
||||
#include <sortix/signal.h>
|
||||
#include <sortix/sigevent.h>
|
||||
#include <sortix/time.h>
|
||||
|
||||
#include <sortix/kernel/platform.h>
|
||||
#include <sortix/kernel/copy.h>
|
||||
#include <sortix/kernel/kthread.h>
|
||||
#include <sortix/kernel/time.h>
|
||||
#include <sortix/kernel/syscall.h>
|
||||
#include <sortix/kernel/user-timer.h>
|
||||
|
||||
#include "process.h"
|
||||
|
||||
// TODO: Memset all user timers in process constructor.
|
||||
|
||||
namespace Sortix {
|
||||
|
||||
// TODO: We also need to fetch the pthread attr if there is one.
|
||||
static bool FetchSigevent(struct sigevent* dst, const struct sigevent* src)
|
||||
{
|
||||
if ( src )
|
||||
return CopyFromUser(dst, src, sizeof(struct sigevent));
|
||||
dst->sigev_notify = SIGEV_SIGNAL;
|
||||
dst->sigev_signo = SIGALRM;
|
||||
// TODO: "and the sigev_value member having the value of the timer ID."
|
||||
// - Does POSIX want the caller to be psychic and should we write back the
|
||||
// final default sigevent?
|
||||
return true;
|
||||
}
|
||||
|
||||
static UserTimer* LookupUserTimer(Process* process, timer_t timerid)
|
||||
{
|
||||
if ( PROCESS_TIMER_NUM_MAX <= timerid )
|
||||
return errno = EINVAL, (UserTimer*) NULL;
|
||||
UserTimer* user_timer = &process->user_timers[timerid];
|
||||
if ( !user_timer->timer.IsAttached() )
|
||||
return errno = EINVAL, (UserTimer*) NULL;
|
||||
return user_timer;
|
||||
}
|
||||
|
||||
static Timer* LookupTimer(Process* process, timer_t timerid)
|
||||
{
|
||||
UserTimer* user_timer = LookupUserTimer(process, timerid);
|
||||
return user_timer ? &user_timer->timer : (Timer*) NULL;
|
||||
}
|
||||
|
||||
static int sys_timer_create(clockid_t clockid, struct sigevent* sigevp,
|
||||
timer_t* timerid_ptr)
|
||||
{
|
||||
Process* process = CurrentProcess();
|
||||
ScopedLock lock(&process->user_timers_lock);
|
||||
|
||||
Clock* clock = Time::GetClock(clockid);
|
||||
if ( !clock )
|
||||
return -1;
|
||||
|
||||
struct sigevent sigev;
|
||||
if ( !FetchSigevent(&sigev, sigevp) )
|
||||
return -1;
|
||||
|
||||
// Allocate a timer for this request.
|
||||
timer_t timerid;
|
||||
for ( timerid = 0; timerid < PROCESS_TIMER_NUM_MAX; timerid++ )
|
||||
{
|
||||
Timer* timer = &process->user_timers[timerid].timer;
|
||||
if ( timer->IsAttached() )
|
||||
continue;
|
||||
timer->Attach(clock);
|
||||
break;
|
||||
}
|
||||
|
||||
if ( PROCESS_TIMER_NUM_MAX <= timerid )
|
||||
return -1;
|
||||
|
||||
if ( !CopyToUser(timerid_ptr, &timerid, sizeof(timerid)) )
|
||||
{
|
||||
process->user_timers[timerid].timer.Detach();
|
||||
return -1;
|
||||
}
|
||||
|
||||
process->user_timers[timerid].process = process;
|
||||
process->user_timers[timerid].event = sigev;
|
||||
process->user_timers[timerid].timerid = timerid;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sys_timer_delete(timer_t timerid)
|
||||
{
|
||||
Process* process = CurrentProcess();
|
||||
ScopedLock lock(&process->user_timers_lock);
|
||||
|
||||
Timer* timer = LookupTimer(process, timerid);
|
||||
if ( !timer )
|
||||
return -1;
|
||||
|
||||
timer->Cancel();
|
||||
timer->Detach();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sys_timer_getoverrun(timer_t timerid)
|
||||
{
|
||||
Process* process = CurrentProcess();
|
||||
ScopedLock lock(&process->user_timers_lock);
|
||||
|
||||
Timer* timer = LookupTimer(process, timerid);
|
||||
if ( !timer )
|
||||
return -1;
|
||||
|
||||
// TODO: This is not fully kept track of yet.
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sys_timer_gettime(timer_t timerid, struct itimerspec* user_value)
|
||||
{
|
||||
Process* process = CurrentProcess();
|
||||
ScopedLock lock(&process->user_timers_lock);
|
||||
|
||||
Timer* timer = LookupTimer(process, timerid);
|
||||
if ( !timer )
|
||||
return -1;
|
||||
|
||||
struct itimerspec value;
|
||||
timer->Get(&value);
|
||||
|
||||
return CopyToUser(user_value, &value, sizeof(value)) ? 0 : -1;
|
||||
}
|
||||
|
||||
static void timer_callback(Clock* /*clock*/, Timer* timer, void* user)
|
||||
{
|
||||
UserTimer* user_timer = (UserTimer*) user;
|
||||
Process* process = user_timer->process;
|
||||
ScopedLock lock(&process->user_timers_lock);
|
||||
|
||||
size_t current_overrun = timer->num_overrun_events;
|
||||
// TODO: This delivery facility is insufficient! sigevent is much more
|
||||
// powerful than sending a simple old-school signal.
|
||||
// TODO: If the last signal from last time is still being processed, we need
|
||||
// to handle the sum of overrun. I'm not sure how to handle overrun
|
||||
// properly, so we'll just pretend to user-space it never happens when
|
||||
// it does and we do some of the bookkeeping.
|
||||
(void) current_overrun;
|
||||
process->DeliverSignal(user_timer->event.sigev_signo);
|
||||
}
|
||||
|
||||
static int sys_timer_settime(timer_t timerid, int flags,
|
||||
const struct itimerspec* user_value,
|
||||
struct itimerspec* user_ovalue)
|
||||
{
|
||||
Process* process = CurrentProcess();
|
||||
ScopedLock lock(&process->user_timers_lock);
|
||||
|
||||
UserTimer* user_timer = LookupUserTimer(process, timerid);
|
||||
if ( !user_timer )
|
||||
return -1;
|
||||
|
||||
Timer* timer = &user_timer->timer;
|
||||
|
||||
struct itimerspec value, ovalue;
|
||||
if ( !CopyFromUser(&value, user_value, sizeof(value)) )
|
||||
return -1;
|
||||
|
||||
if ( timespec_lt(value.it_value, timespec_nul()) ||
|
||||
timespec_lt(value.it_interval, timespec_nul()) ||
|
||||
(flags & ~(TIMER_ABSTIME)) != 0 )
|
||||
return errno = EINVAL, -1;
|
||||
|
||||
int timer_flags = 0;
|
||||
if ( flags & TIMER_ABSTIME )
|
||||
timer_flags |= TIMER_ABSOLUTE;
|
||||
|
||||
timer->Set(&value, &ovalue, timer_flags, timer_callback, user_timer);
|
||||
|
||||
// Let the caller know how much time was left on the timer.
|
||||
if ( user_ovalue && !CopyToUser(user_ovalue, &ovalue, sizeof(ovalue)) )
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sys_uptime(uintmax_t* usecssinceboot)
|
||||
{
|
||||
struct timespec now;
|
||||
Clock* clock = Time::GetClock(CLOCK_BOOT);
|
||||
clock->Get(&now, NULL);
|
||||
|
||||
uintmax_t seconds = now.tv_sec;
|
||||
uintmax_t nano_seconds = now.tv_nsec;
|
||||
uintmax_t ret = seconds * 1000000 + nano_seconds / 1000;
|
||||
|
||||
return CopyToUser(usecssinceboot, &ret, sizeof(ret)) ? 0 : -1;
|
||||
}
|
||||
|
||||
void UserTimer::Init()
|
||||
{
|
||||
Syscall::Register(SYSCALL_TIMER_CREATE, (void*) sys_timer_create);
|
||||
Syscall::Register(SYSCALL_TIMER_DELETE, (void*) sys_timer_delete);
|
||||
Syscall::Register(SYSCALL_TIMER_GETOVERRUN, (void*) sys_timer_getoverrun);
|
||||
Syscall::Register(SYSCALL_TIMER_GETTIME, (void*) sys_timer_gettime);
|
||||
Syscall::Register(SYSCALL_TIMER_SETTIME, (void*) sys_timer_settime);
|
||||
Syscall::Register(SYSCALL_UPTIME, (void*) sys_uptime);
|
||||
}
|
||||
|
||||
} // namespace Sortix
|
122
sortix/x86-family/time.cpp
Normal file
122
sortix/x86-family/time.cpp
Normal file
|
@ -0,0 +1,122 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
Sortix is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
x86-family/time.cpp
|
||||
Retrieving the current time.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <timespec.h>
|
||||
|
||||
#include <sortix/timespec.h>
|
||||
|
||||
#include <sortix/kernel/platform.h>
|
||||
#include <sortix/kernel/clock.h>
|
||||
#include <sortix/kernel/interrupt.h>
|
||||
#include <sortix/kernel/scheduler.h>
|
||||
#include <sortix/kernel/time.h>
|
||||
|
||||
#include "../cpu.h"
|
||||
|
||||
namespace Sortix {
|
||||
namespace Time {
|
||||
|
||||
static uint16_t DivisorOfFrequency(long frequency)
|
||||
{
|
||||
// The value we send to the PIT is the value to divide it's input clock
|
||||
// (1193180 Hz) by, to get our required frequency. Note that the divisor
|
||||
// must be small enough to fit into 16 bits.
|
||||
return 1193180 / frequency;
|
||||
}
|
||||
|
||||
static long FrequencyOfDivisor(uint16_t divisor)
|
||||
{
|
||||
return 1193180 / divisor;
|
||||
}
|
||||
|
||||
static long RealFrequencyOfFrequency(long frequency)
|
||||
{
|
||||
return FrequencyOfDivisor(DivisorOfFrequency(frequency));
|
||||
}
|
||||
|
||||
static struct timespec PeriodOfFrequency(long frequency)
|
||||
{
|
||||
long period_ns = 1000000000L / frequency;
|
||||
return timespec_make(0, period_ns);
|
||||
}
|
||||
|
||||
static void RequestIRQ0(uint16_t divisor)
|
||||
{
|
||||
CPU::OutPortB(0x43, 0x36);
|
||||
CPU::OutPortB(0x40, divisor >> 0 & 0xFF);
|
||||
CPU::OutPortB(0x40, divisor >> 8 & 0xFF);
|
||||
}
|
||||
|
||||
extern Clock* realtime_clock;
|
||||
extern Clock* uptime_clock;
|
||||
|
||||
static struct timespec tick_period;
|
||||
static long tick_frequency;
|
||||
static uint16_t tick_divisor;
|
||||
|
||||
static void OnIRQ0(CPU::InterruptRegisters* regs, void* /*user*/)
|
||||
{
|
||||
OnTick(tick_period);
|
||||
Scheduler::Switch(regs);
|
||||
|
||||
// TODO: There is a horrible bug that causes Sortix to only receive
|
||||
// one IRQ0 on my laptop, but it works in virtual machines. But
|
||||
// re-requesting an addtional time seems to work. Hacky and ugly.
|
||||
// TODO: Confirm whether this still happens and whether it is trigged by
|
||||
// another bug in my system.
|
||||
static bool did_ugly_irq0_hack = false;
|
||||
if ( !did_ugly_irq0_hack )
|
||||
RequestIRQ0(tick_divisor),
|
||||
did_ugly_irq0_hack = true;
|
||||
}
|
||||
|
||||
void CPUInit()
|
||||
{
|
||||
// Estimate the rate that interrupts will be coming at.
|
||||
long desired_frequency = 100/*Hz*/;
|
||||
tick_frequency = RealFrequencyOfFrequency(desired_frequency);
|
||||
tick_divisor = DivisorOfFrequency(tick_frequency);
|
||||
tick_period = PeriodOfFrequency(tick_frequency);
|
||||
|
||||
// Initialize the clocks on this system.
|
||||
realtime_clock->SetCallableFromInterrupts(true);
|
||||
uptime_clock->SetCallableFromInterrupts(true);
|
||||
struct timespec nul_time = timespec_nul();
|
||||
realtime_clock->Set(&nul_time, &tick_period);
|
||||
uptime_clock->Set(&nul_time, &tick_period);
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
// Handle timer interrupts if they arrive.
|
||||
Interrupt::RegisterHandler(Interrupt::IRQ0, &OnIRQ0, NULL);
|
||||
|
||||
// Request a timer interrupt now that we can handle them safely.
|
||||
RequestIRQ0(tick_divisor);
|
||||
}
|
||||
|
||||
} // namespace Time
|
||||
} // namespace Sortix
|
Loading…
Add table
Reference in a new issue