genode/repos/os/src/lib/dde_kit/timer.cc

215 lines
4.4 KiB
C++

/*
* \brief Timers and ticks
* \author Christian Helmuth
* \date 2008-10-22
*/
/*
* Copyright (C) 2008-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#include <util/list.h>
#include <os/alarm.h>
#include <timer_session/connection.h>
#include <base/printf.h>
extern "C" {
#include <dde_kit/timer.h>
}
#include "thread.h"
using namespace Genode;
/************************
** Timer tick symbols **
************************/
volatile unsigned long dde_kit_timer_ticks;
/***************************
** Timer thread and tick **
***************************/
class Timer_thread : Dde_kit::Thread, public Alarm_scheduler
{
private:
unsigned _period_in_ms;
void(*_init)(void *);
void *_priv;
Timer::Connection _timer;
List<dde_kit_timer> _destroy_list;
Lock _destroy_list_lock;
void _enqueue_destroy(dde_kit_timer *timer)
{
Lock::Guard lock_guard(_destroy_list_lock);
_destroy_list.insert(timer);
}
dde_kit_timer * _dequeue_next_destroy()
{
Lock::Guard lock_guard(_destroy_list_lock);
dde_kit_timer *timer = _destroy_list.first();
if (timer)
_destroy_list.remove(timer);
return timer;
}
public:
Timer_thread(unsigned hz, void(*init)(void *), void *priv)
: Dde_kit::Thread("timer"), _period_in_ms(1000/hz), _init(init), _priv(priv)
{
dde_kit_timer_ticks = 0;
start();
}
void entry()
{
dde_kit_thread_adopt_myself("timer");
/* call provided init function */
if (_init) _init(_priv);
/* timer tick loop */
while (true) {
dde_kit_timer *timer;
/*
* XXX This approach drifts with the execution time of handlers
* and timer destruction.
*/
_timer.msleep(_period_in_ms);
++dde_kit_timer_ticks;
/* execute all scheduled alarms */
Alarm_scheduler::handle(dde_kit_timer_ticks);
/* finish pending alarm object destruction */
while ((timer = _dequeue_next_destroy()))
destroy(env()->heap(), timer);
}
};
/**
* Schedule timer for destruction (garbage collection)
*
* \param timer timer fo destroy
*/
void destroy_timer(dde_kit_timer *timer)
{
_enqueue_destroy(timer);
}
};
static Timer_thread *_timer_thread;
/*************+******
** Timer facility **
********************/
class dde_kit_timer : public Alarm, public List<dde_kit_timer>::Element
{
private:
void (*_handler)(void *); /* handler function */
void *_priv; /* private handler token */
bool _pending; /* true if timer is pending */
protected:
bool on_alarm(unsigned) override
{
/* if timer is really pending, call registered handler function */
if (_pending) {
_pending = false;
_handler(_priv);
}
/* do not schedule again */
return false;
}
public:
dde_kit_timer(void (*handler)(void *), void *priv, unsigned long absolute_timeout)
: _handler(handler), _priv(priv), _pending(true) {
schedule(absolute_timeout); }
void schedule(unsigned long absolute_timeout)
{
_pending = true;
_timer_thread->schedule_absolute(this, absolute_timeout);
}
bool pending() const { return _pending; }
/**
* Schedule destruction of this timer on next tick
*
* Note: The timed event scheduler (alarm.h) does not allow to modify
* alarm objects in the on_alarm function. But, drivers do this
* frequently when modifying timer objects on timeout occurence.
*/
void destroy()
{
_pending = false;
_timer_thread->destroy_timer(this);
}
};
extern "C" struct dde_kit_timer *dde_kit_timer_add(void (*fn)(void *), void *priv,
unsigned long timeout)
{
try {
return new (env()->heap()) dde_kit_timer(fn, priv, timeout);
} catch (...) {
PERR("timer creation failed");
return 0;
}
}
extern "C" void dde_kit_timer_schedule_absolute(struct dde_kit_timer *timer, unsigned long timeout) {
timer->schedule(timeout); }
extern "C" void dde_kit_timer_del(struct dde_kit_timer *timer)
{
try {
timer->destroy();
} catch (...) { }
}
extern "C" int dde_kit_timer_pending(struct dde_kit_timer *timer) {
return timer->pending();
}
extern "C" void dde_kit_timer_init(void(*thread_init)(void *), void *priv)
{
try {
static Timer_thread t(DDE_KIT_HZ, thread_init, priv);
_timer_thread = &t;
} catch (...) {
PERR("Timer thread creation failed");
}
}