Goto sanos source index
//
// timer.c
//
// Timer functions
// Derived from Finn Arne Gangstad's kernel timer implementation
//
// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. Neither the name of the project nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
#include <os/krnl.h>
#define TVN_BITS 6
#define TVR_BITS 8
#define TVN_SIZE (1 << TVN_BITS)
#define TVR_SIZE (1 << TVR_BITS)
#define TVN_MASK (TVN_SIZE - 1)
#define TVR_MASK (TVR_SIZE - 1)
struct timer_vec {
int index;
struct timer_link vec[TVN_SIZE];
};
struct timer_vec_root {
int index;
struct timer_link vec[TVR_SIZE];
};
static unsigned int timer_ticks = 0;
static struct timer_vec tv5;
static struct timer_vec tv4;
static struct timer_vec tv3;
static struct timer_vec tv2;
static struct timer_vec_root tv1;
static struct timer_vec * const tvecs[] = {
(struct timer_vec *)&tv1, &tv2, &tv3, &tv4, &tv5
};
#define NOOF_TVECS (sizeof(tvecs) / sizeof(tvecs[0]))
//
// attach_timer
//
static void attach_timer(struct timer *timer) {
unsigned int expires = timer->expires;
unsigned int idx = expires - timer_ticks;
struct timer_link *vec;
if (idx < TVR_SIZE) {
int i = expires & TVR_MASK;
vec = tv1.vec + i;
} else if (idx < 1 << (TVR_BITS + TVN_BITS)) {
int i = (expires >> TVR_BITS) & TVN_MASK;
vec = tv2.vec + i;
} else if (idx < 1 << (TVR_BITS + 2 * TVN_BITS)) {
int i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK;
vec = tv3.vec + i;
} else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS)) {
int i = (expires >> (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK;
vec = tv4.vec + i;
} else if ((signed long) idx < 0) {
// Can happen if you add a timer with expires == timer_ticks,
// or you set a timer to go off in the past
vec = tv1.vec + tv1.index;
} else {
int i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK;
vec = tv5.vec + i;
}
timer->link.next = vec;
timer->link.prev = vec->prev;
vec->prev->next = &timer->link;
vec->prev = &timer->link;
timer->active = 1;
}
//
// detach_timer
//
static int detach_timer(struct timer *timer) {
if (!timer->active) return 0;
timer->link.next->prev = timer->link.prev;
timer->link.prev->next = timer->link.next;
timer->active = 0;
return 1;
}
//
// init_timers
//
void init_timers() {
int i;
for (i = 0; i < TVN_SIZE; i++) {
tv5.vec[i].next = tv5.vec[i].prev = tv5.vec + i;
tv4.vec[i].next = tv4.vec[i].prev = tv4.vec + i;
tv3.vec[i].next = tv3.vec[i].prev = tv3.vec + i;
tv2.vec[i].next = tv2.vec[i].prev = tv2.vec + i;
}
for (i = 0; i < TVR_SIZE; i++) {
tv1.vec[i].next = tv1.vec[i].prev = tv1.vec + i;
}
}
//
// init_timer
//
void init_timer(struct timer *timer, timerproc_t handler, void *arg) {
timer->link.next = NULL;
timer->link.next = NULL;
timer->expires = 0;
timer->active = 0;
timer->handler = handler;
timer->arg = arg;
}
//
// add_timer
//
void add_timer(struct timer *timer) {
if (timer->active) {
kprintf("timer: timer is already active\n");
return;
}
attach_timer(timer);
}
//
// del_timer
//
int del_timer(struct timer *timer) {
int rc;
rc = detach_timer(timer);
timer->link.next = NULL;
timer->link.prev = NULL;
return rc;
}
//
// mod_timer
//
int mod_timer(struct timer *timer, unsigned int expires) {
int rc;
timer->expires = expires;
rc = detach_timer(timer);
attach_timer(timer);
return rc;
}
//
// cascade_timers
//
// Cascade all the timers from tv up one level. We are removing
// all timers from the list, so we don't have to detach them
// individually, just clear the list afterwards.
//
static void cascade_timers(struct timer_vec *tv) {
struct timer_link *head, *curr, *next;
head = tv->vec + tv->index;
curr = head->next;
while (curr != head) {
struct timer *timer;
timer = (struct timer *) curr;
next = curr->next;
attach_timer(timer);
curr = next;
}
head->next = head->prev = head;
tv->index = (tv->index + 1) & TVN_MASK;
}
//
// run_timer_list
//
void run_timer_list() {
while ((long) (ticks - timer_ticks) >= 0) {
struct timer_link *head, *curr;
if (!tv1.index) {
int n = 1;
do { cascade_timers(tvecs[n]); } while (tvecs[n]->index == 1 && ++n < NOOF_TVECS);
}
while (1) {
struct timer *timer;
timerproc_t handler;
void *arg;
head = tv1.vec + tv1.index;
curr = head->next;
if (curr == head) break;
timer = (struct timer *) curr;
handler = timer->handler;
arg = timer->arg;
detach_timer(timer);
timer->link.next = timer->link.prev = NULL;
handler(arg);
}
timer_ticks++;
tv1.index = (tv1.index + 1) & TVR_MASK;
}
}
//
// tmr_sleep
//
// End sleep state for thread
//
static void tmr_sleep(void *arg) {
struct thread *t = arg;
mark_thread_ready(t, 1, 0);
}
//
// msleep
//
// Sleep for a number of milliseconds
//
int msleep(unsigned int millisecs) {
struct timer timer;
int rc;
if (millisecs == 0) {
yield();
rc = 0;
} else {
init_timer(&timer, tmr_sleep, self());
timer.expires = ticks + millisecs / MSECS_PER_TICK;
add_timer(&timer);
rc = enter_alertable_wait(THREAD_WAIT_SLEEP);
if (rc == -EINTR) {
rc = (timer.expires - ticks) * MSECS_PER_TICK;
if (rc < 0) rc = 0;
}
del_timer(&timer);
}
return rc;
}