/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "cras_types.h"
#include "cras_util.h"
#include "utlist.h"
#include <time.h>
/* Represents an armed timer.
* Members:
* ts - timespec at which the timer should fire.
* cb - Callback to call when the timer expires.
* cb_data - Data passed to the callback.
*/
struct cras_timer {
struct timespec ts;
void (*cb)(struct cras_timer *t, void *data);
void *cb_data;
struct cras_timer *next, *prev;
};
/* Timer Manager, keeps a list of active timers. */
struct cras_tm {
struct cras_timer *timers;
};
/* Local Functions. */
/* Adds ms milliseconds to ts. */
static inline void add_ms_ts(struct timespec *ts, unsigned int ms)
{
if (ms >= 1000) {
ts->tv_sec += ms / 1000;
ms %= 1000;
}
ts->tv_nsec += ms * 1000000L;
if (ts->tv_nsec >= 1000000000L) {
ts->tv_sec += ts->tv_nsec / 1000000000L;
ts->tv_nsec %= 1000000000L;
}
}
/* Checks if timespec a is less than b. */
static inline int timespec_sooner(const struct timespec *a,
const struct timespec *b)
{
return (a->tv_sec < b->tv_sec ||
(a->tv_sec == b->tv_sec && a->tv_nsec <= b->tv_nsec));
}
/* Exported Interface. */
struct cras_timer *cras_tm_create_timer(
struct cras_tm *tm,
unsigned int ms,
void (*cb)(struct cras_timer *t, void *data),
void *cb_data)
{
struct cras_timer *t;
t = calloc(1, sizeof(*t));
if (!t)
return NULL;
t->cb = cb;
t->cb_data = cb_data;
clock_gettime(CLOCK_MONOTONIC_RAW, &t->ts);
add_ms_ts(&t->ts, ms);
DL_APPEND(tm->timers, t);
return t;
}
void cras_tm_cancel_timer(struct cras_tm *tm, struct cras_timer *t)
{
DL_DELETE(tm->timers, t);
free(t);
}
struct cras_tm *cras_tm_init()
{
return calloc(1, sizeof(struct cras_tm));
}
void cras_tm_deinit(struct cras_tm *tm)
{
struct cras_timer *t;
DL_FOREACH(tm->timers, t) {
DL_DELETE(tm->timers, t);
free(t);
}
free(tm);
}
int cras_tm_get_next_timeout(const struct cras_tm *tm, struct timespec *ts)
{
struct cras_timer *t;
struct timespec now;
struct timespec *min;
if (!tm->timers)
return 0;
min = &tm->timers->ts;
DL_FOREACH(tm->timers, t)
if (timespec_sooner(&t->ts, min))
min = &t->ts;
clock_gettime(CLOCK_MONOTONIC_RAW, &now);
if (timespec_sooner(min, &now)) {
/* Timer already expired. */
ts->tv_sec = ts->tv_nsec = 0;
return 1;
}
subtract_timespecs(min, &now, ts);
return 1;
}
void cras_tm_call_callbacks(struct cras_tm *tm)
{
struct timespec now;
struct cras_timer *t, *next;
clock_gettime(CLOCK_MONOTONIC_RAW, &now);
/* Don't use DL_FOREACH to iterate timers because in each loop the
* next timer pointer is stored for later access but it could be
* cancelled and freed in current timer's callback causing invalid
* memory access. */
t = tm->timers;
while (t) {
next = t->next;
if (timespec_sooner(&t->ts, &now)) {
t->cb(t, t->cb_data);
/* Update next timer because it could have been modified
* in t->cb(). */
next = t->next;
cras_tm_cancel_timer(tm, t);
}
t = next;
}
}