Home | History | Annotate | Download | only in server
      1 /* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
      2  * Use of this source code is governed by a BSD-style license that can be
      3  * found in the LICENSE file.
      4  */
      5 
      6 #include "cras_types.h"
      7 #include "cras_util.h"
      8 #include "utlist.h"
      9 
     10 #include <time.h>
     11 
     12 /* Represents an armed timer.
     13  * Members:
     14  *    ts - timespec at which the timer should fire.
     15  *    cb - Callback to call when the timer expires.
     16  *    cb_data - Data passed to the callback.
     17  */
     18 struct cras_timer {
     19 	struct timespec ts;
     20 	void (*cb)(struct cras_timer *t, void *data);
     21 	void *cb_data;
     22 	struct cras_timer *next, *prev;
     23 };
     24 
     25 /* Timer Manager, keeps a list of active timers. */
     26 struct cras_tm {
     27 	struct cras_timer *timers;
     28 };
     29 
     30 /* Local Functions. */
     31 
     32 /* Adds ms milliseconds to ts. */
     33 static inline void add_ms_ts(struct timespec *ts, unsigned int ms)
     34 {
     35 	if (ms >= 1000) {
     36 		ts->tv_sec += ms / 1000;
     37 		ms %= 1000;
     38 	}
     39 	ts->tv_nsec += ms * 1000000L;
     40 	if (ts->tv_nsec >= 1000000000L) {
     41 		ts->tv_sec += ts->tv_nsec / 1000000000L;
     42 		ts->tv_nsec %= 1000000000L;
     43 	}
     44 }
     45 
     46 /* Checks if timespec a is less than b. */
     47 static inline int timespec_sooner(const struct timespec *a,
     48 				  const struct timespec *b)
     49 {
     50 	return (a->tv_sec < b->tv_sec ||
     51 		(a->tv_sec == b->tv_sec && a->tv_nsec <= b->tv_nsec));
     52 }
     53 
     54 /* Exported Interface. */
     55 
     56 struct cras_timer *cras_tm_create_timer(
     57 		struct cras_tm *tm,
     58 		unsigned int ms,
     59 		void (*cb)(struct cras_timer *t, void *data),
     60 		void *cb_data)
     61 {
     62 	struct cras_timer *t;
     63 
     64 	t = calloc(1, sizeof(*t));
     65 	if (!t)
     66 		return NULL;
     67 
     68 	t->cb = cb;
     69 	t->cb_data = cb_data;
     70 
     71 	clock_gettime(CLOCK_MONOTONIC_RAW, &t->ts);
     72 	add_ms_ts(&t->ts, ms);
     73 
     74 	DL_APPEND(tm->timers, t);
     75 
     76 	return t;
     77 }
     78 
     79 void cras_tm_cancel_timer(struct cras_tm *tm, struct cras_timer *t)
     80 {
     81 	DL_DELETE(tm->timers, t);
     82 	free(t);
     83 }
     84 
     85 struct cras_tm *cras_tm_init()
     86 {
     87 	return calloc(1, sizeof(struct cras_tm));
     88 }
     89 
     90 void cras_tm_deinit(struct cras_tm *tm)
     91 {
     92 	struct cras_timer *t;
     93 
     94 	DL_FOREACH(tm->timers, t) {
     95 		DL_DELETE(tm->timers, t);
     96 		free(t);
     97 	}
     98 	free(tm);
     99 }
    100 
    101 int cras_tm_get_next_timeout(const struct cras_tm *tm, struct timespec *ts)
    102 {
    103 	struct cras_timer *t;
    104 	struct timespec now;
    105 	struct timespec *min;
    106 
    107 	if (!tm->timers)
    108 		return 0;
    109 
    110 	min = &tm->timers->ts;
    111 	DL_FOREACH(tm->timers, t)
    112 		if (timespec_sooner(&t->ts, min))
    113 			min = &t->ts;
    114 
    115 	clock_gettime(CLOCK_MONOTONIC_RAW, &now);
    116 
    117 	if (timespec_sooner(min, &now)) {
    118 		/* Timer already expired. */
    119 		ts->tv_sec = ts->tv_nsec = 0;
    120 		return 1;
    121 	}
    122 
    123 	subtract_timespecs(min, &now, ts);
    124 	return 1;
    125 }
    126 
    127 void cras_tm_call_callbacks(struct cras_tm *tm)
    128 {
    129 	struct timespec now;
    130 	struct cras_timer *t, *next;
    131 
    132 	clock_gettime(CLOCK_MONOTONIC_RAW, &now);
    133 
    134 	/* Don't use DL_FOREACH to iterate timers because in each loop the
    135 	 * next timer pointer is stored for later access but it could be
    136 	 * cancelled and freed in current timer's callback causing invalid
    137 	 * memory access. */
    138 	t = tm->timers;
    139 	while (t) {
    140 		next = t->next;
    141 		if (timespec_sooner(&t->ts, &now)) {
    142 			t->cb(t, t->cb_data);
    143 			/* Update next timer because it could have been modified
    144 			 * in t->cb(). */
    145 			next = t->next;
    146 			cras_tm_cancel_timer(tm, t);
    147 		}
    148 		t = next;
    149 	}
    150 }
    151