metal-cos/sys/kern/timer.c
2015-01-16 15:25:13 -08:00

113 lines
2.2 KiB
C

#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include <sys/kassert.h>
#include <sys/queue.h>
#include <sys/spinlock.h>
#include <sys/kmem.h>
#include <sys/ktime.h>
#include <sys/ktimer.h>
#define TIMER_WHEEL_LENGTH 256
int timerHead = 0;
uint64_t timerNow = 0;
LIST_HEAD(TimerWheelHead, TimerEvent) timerSlot[TIMER_WHEEL_LENGTH];
Spinlock timerLock;
Slab timerSlab;
DEFINE_SLAB(TimerEvent, &timerSlab);
void
Timer_Init()
{
int i;
Spinlock_Init(&timerLock, "Timer Lock");
Slab_Init(&timerSlab, "TimerEvent Slab", sizeof(TimerEvent), 16);
// Initialize wheel
timerHead = 0;
timerNow = KTime_GetEpoch();
for (i = 0; i < TIMER_WHEEL_LENGTH; i++) {
LIST_INIT(&timerSlot[i]);
}
}
TimerEvent *
Timer_Create(uint64_t timeout, TimerCB cb, void *arg)
{
int slot;
TimerEvent *evt = TimerEvent_Alloc();
evt->refCount = 2; // One for the wheel and one for the callee
evt->timeout = timerNow + timeout;
evt->cb = cb;
evt->arg = arg;
Spinlock_Lock(&timerLock);
slot = (timerHead + timeout + TIMER_WHEEL_LENGTH - 1) % TIMER_WHEEL_LENGTH;
// XXX: should insert into tail
LIST_INSERT_HEAD(&timerSlot[slot], evt, timerQueue);
Spinlock_Unlock(&timerLock);
return evt;
}
void
Timer_Retain(TimerEvent *evt)
{
ASSERT(evt->refCount != 0);
__sync_fetch_and_add(&evt->refCount, 1);
}
void
Timer_Release(TimerEvent *evt)
{
ASSERT(evt->refCount != 0);
if (__sync_fetch_and_sub(&evt->refCount, 1) == 1) {
TimerEvent_Free(evt);
}
}
void
Timer_Cancel(TimerEvent *evt)
{
Spinlock_Lock(&timerLock);
LIST_REMOVE(evt, timerQueue);
Timer_Release(evt);
Spinlock_Unlock(&timerLock);
}
void
Timer_Process()
{
uint64_t now = KTime_GetEpoch();
Spinlock_Lock(&timerLock);
while (now > timerNow) {
TimerEvent *it, *tmp;
// Dispatch pending timer events
LIST_FOREACH_SAFE(it, &timerSlot[timerHead], timerQueue, tmp) {
if (it->timeout <= now) {
(it->cb)(it->arg);
LIST_REMOVE(it, timerQueue);
Timer_Release(it);
}
}
// Rotate wheel forward
timerNow = timerNow + 1;
timerHead = (timerHead + 1) % TIMER_WHEEL_LENGTH;
}
Spinlock_Unlock(&timerLock);
}