227 lines
6.2 KiB
C
227 lines
6.2 KiB
C
/**
|
|
* \file
|
|
* \brief Deferred events (ie. timers)
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2009, 2011, 2012, 2013, ETH Zurich.
|
|
* All rights reserved.
|
|
*
|
|
* This file is distributed under the terms in the attached LICENSE file.
|
|
* If you do not find this file, copies can be found by writing to:
|
|
* ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
|
|
*/
|
|
|
|
#include <aos/aos.h>
|
|
#include <aos/deferred.h>
|
|
#include <aos/waitset_chan.h>
|
|
#include <stdio.h>
|
|
#include <aos/systime.h>
|
|
|
|
// FIXME: why do I need quite so many dispatcher headers?
|
|
#include <aos/dispatch.h>
|
|
#include <aos/dispatcher_arch.h>
|
|
#include <aos/curdispatcher_arch.h>
|
|
#include <barrelfish_kpi/dispatcher_shared.h>
|
|
|
|
#include "waitset_chan_priv.h"
|
|
|
|
static void update_wakeup_disabled(dispatcher_handle_t dh)
|
|
{
|
|
struct dispatcher_generic *dg = get_dispatcher_generic(dh);
|
|
struct dispatcher_shared_generic *ds = get_dispatcher_shared_generic(dh);
|
|
|
|
if (dg->deferred_events == NULL) {
|
|
ds->wakeup = 0;
|
|
} else {
|
|
ds->wakeup = dg->deferred_events->time;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief Returns the system time when the current dispatcher was last dispatched
|
|
*/
|
|
systime_t get_system_time(void)
|
|
{
|
|
return systime_now();
|
|
}
|
|
|
|
void deferred_event_init(struct deferred_event *event)
|
|
{
|
|
assert(event != NULL);
|
|
waitset_chanstate_init(&event->waitset_state, CHANTYPE_DEFERRED);
|
|
event->next = event->prev = NULL;
|
|
event->time = 0;
|
|
}
|
|
|
|
/**
|
|
* \brief Register a deferred event
|
|
*
|
|
* \param ws Waitset
|
|
* \param delay Delay in microseconds
|
|
* \param closure Event closure to execute
|
|
* \param event Storage for event metadata
|
|
*/
|
|
errval_t deferred_event_register(struct deferred_event *event,
|
|
struct waitset *ws, delayus_t delay,
|
|
struct event_closure closure)
|
|
{
|
|
errval_t err;
|
|
|
|
dispatcher_handle_t dh = disp_disable();
|
|
err = waitset_chan_register_disabled(ws, &event->waitset_state, closure);
|
|
if (err_is_ok(err)) {
|
|
struct dispatcher_generic *dg = get_dispatcher_generic(dh);
|
|
|
|
// determine absolute time for event
|
|
event->time = systime_now() + ns_to_systime((uint64_t)delay * 1000);
|
|
// enqueue in sorted list of pending timers
|
|
for (struct deferred_event *e = dg->deferred_events, *p = NULL; ;
|
|
p = e, e = e->next) {
|
|
if (e == NULL || e->time > event->time) {
|
|
if (p == NULL) { // insert at head
|
|
assert(e == dg->deferred_events);
|
|
event->prev = NULL;
|
|
event->next = e;
|
|
if (e != NULL) {
|
|
e->prev = event;
|
|
}
|
|
dg->deferred_events = event;
|
|
} else {
|
|
event->next = e;
|
|
event->prev = p;
|
|
p->next = event;
|
|
if (e != NULL) {
|
|
e->prev = event;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
update_wakeup_disabled(dh);
|
|
|
|
disp_enable(dh);
|
|
|
|
return err;
|
|
}
|
|
|
|
static void usleep_callback(void *val)
|
|
{
|
|
bool *wakeup = val;
|
|
*wakeup = true;
|
|
}
|
|
|
|
errval_t barrelfish_usleep(delayus_t delay)
|
|
{
|
|
struct deferred_event dev;
|
|
struct waitset ws;
|
|
errval_t err;
|
|
bool wakeup = false;
|
|
|
|
deferred_event_init(&dev);
|
|
waitset_init(&ws);
|
|
err = deferred_event_register(&dev, &ws, delay,
|
|
MKCLOSURE(usleep_callback, &wakeup));
|
|
if(err_is_fail(err)) {
|
|
return err;
|
|
}
|
|
|
|
while(!wakeup) {
|
|
err = event_dispatch(&ws);
|
|
if(err_is_fail(err)) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
return waitset_destroy(&ws);
|
|
}
|
|
|
|
/**
|
|
* \brief Cancel a deferred event that has not yet fired
|
|
*/
|
|
errval_t deferred_event_cancel(struct deferred_event *event)
|
|
{
|
|
enum ws_chanstate chanstate = event->waitset_state.state;
|
|
dispatcher_handle_t handle = disp_disable();
|
|
errval_t err = waitset_chan_deregister_disabled(&event->waitset_state, handle);
|
|
if (err_is_ok(err) && chanstate != CHAN_PENDING) {
|
|
// remove from dispatcher queue
|
|
struct dispatcher_generic *disp = get_dispatcher_generic(handle);
|
|
if (event->prev == NULL) {
|
|
disp->deferred_events = event->next;
|
|
} else {
|
|
event->prev->next = event->next;
|
|
}
|
|
if (event->next != NULL) {
|
|
event->next->prev = event->prev;
|
|
}
|
|
update_wakeup_disabled(handle);
|
|
}
|
|
|
|
disp_enable(handle);
|
|
return err;
|
|
}
|
|
|
|
static void periodic_event_handler(void *arg)
|
|
{
|
|
struct periodic_event *e = arg;
|
|
assert(e != NULL);
|
|
|
|
// re-register (first, in case the handler wishes to cancel it)
|
|
errval_t err = deferred_event_register(&e->de, e->waitset, e->period,
|
|
MKCLOSURE(periodic_event_handler, e));
|
|
assert(err_is_ok(err));
|
|
|
|
// run handler
|
|
e->cl.handler(e->cl.arg);
|
|
}
|
|
|
|
/**
|
|
* \brief Create a periodic event
|
|
*
|
|
* A periodic event will repeatedly run a closure at a fixed rate until cancelled.
|
|
*
|
|
* \param event Storage for event state
|
|
* \param ws Waitset
|
|
* \param period Period, in microseconds
|
|
* \param closure Closure to run
|
|
*/
|
|
errval_t periodic_event_create(struct periodic_event *event, struct waitset *ws,
|
|
delayus_t period, struct event_closure closure)
|
|
{
|
|
assert(event != NULL);
|
|
deferred_event_init(&event->de);
|
|
event->cl = closure;
|
|
event->waitset = ws;
|
|
event->period = period;
|
|
return deferred_event_register(&event->de, ws, period,
|
|
MKCLOSURE(periodic_event_handler, event));
|
|
}
|
|
|
|
/// Cancel a periodic event
|
|
errval_t periodic_event_cancel(struct periodic_event *event)
|
|
{
|
|
return deferred_event_cancel(&event->de);
|
|
}
|
|
|
|
|
|
/// Trigger any pending deferred events, while disabled
|
|
void trigger_deferred_events_disabled(dispatcher_handle_t dh, systime_t now)
|
|
{
|
|
struct dispatcher_generic *dg = get_dispatcher_generic(dh);
|
|
struct deferred_event *e;
|
|
errval_t err;
|
|
|
|
for (e = dg->deferred_events; e != NULL && e->time <= now; e = e->next) {
|
|
err = waitset_chan_trigger_disabled(&e->waitset_state, dh);
|
|
assert_disabled(err_is_ok(err));
|
|
}
|
|
dg->deferred_events = e;
|
|
if (e != NULL) {
|
|
e->prev = NULL;
|
|
}
|
|
update_wakeup_disabled(dh);
|
|
}
|