/* 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 /* 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; } }