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. */
add_ms_ts(struct timespec * ts,unsigned int ms)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. */
timespec_sooner(const struct timespec * a,const struct timespec * 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
cras_tm_create_timer(struct cras_tm * tm,unsigned int ms,void (* cb)(struct cras_timer * t,void * data),void * cb_data)56 struct cras_timer *cras_tm_create_timer(struct cras_tm *tm, unsigned int ms,
57 void (*cb)(struct cras_timer *t,
58 void *data),
59 void *cb_data)
60 {
61 struct cras_timer *t;
62
63 t = calloc(1, sizeof(*t));
64 if (!t)
65 return NULL;
66
67 t->cb = cb;
68 t->cb_data = cb_data;
69
70 clock_gettime(CLOCK_MONOTONIC_RAW, &t->ts);
71 add_ms_ts(&t->ts, ms);
72
73 DL_APPEND(tm->timers, t);
74
75 return t;
76 }
77
cras_tm_cancel_timer(struct cras_tm * tm,struct cras_timer * t)78 void cras_tm_cancel_timer(struct cras_tm *tm, struct cras_timer *t)
79 {
80 DL_DELETE(tm->timers, t);
81 free(t);
82 }
83
cras_tm_init()84 struct cras_tm *cras_tm_init()
85 {
86 return calloc(1, sizeof(struct cras_tm));
87 }
88
cras_tm_deinit(struct cras_tm * tm)89 void cras_tm_deinit(struct cras_tm *tm)
90 {
91 struct cras_timer *t;
92
93 DL_FOREACH (tm->timers, t) {
94 DL_DELETE(tm->timers, t);
95 free(t);
96 }
97 free(tm);
98 }
99
cras_tm_get_next_timeout(const struct cras_tm * tm,struct timespec * ts)100 int cras_tm_get_next_timeout(const struct cras_tm *tm, struct timespec *ts)
101 {
102 struct cras_timer *t;
103 struct timespec now;
104 struct timespec *min;
105
106 if (!tm->timers)
107 return 0;
108
109 min = &tm->timers->ts;
110 DL_FOREACH (tm->timers, t)
111 if (timespec_sooner(&t->ts, min))
112 min = &t->ts;
113
114 clock_gettime(CLOCK_MONOTONIC_RAW, &now);
115
116 if (timespec_sooner(min, &now)) {
117 /* Timer already expired. */
118 ts->tv_sec = ts->tv_nsec = 0;
119 return 1;
120 }
121
122 subtract_timespecs(min, &now, ts);
123 return 1;
124 }
125
cras_tm_call_callbacks(struct cras_tm * tm)126 void cras_tm_call_callbacks(struct cras_tm *tm)
127 {
128 struct timespec now;
129 struct cras_timer *t, *next;
130
131 clock_gettime(CLOCK_MONOTONIC_RAW, &now);
132
133 /* Don't use DL_FOREACH to iterate timers because in each loop the
134 * next timer pointer is stored for later access but it could be
135 * cancelled and freed in current timer's callback causing invalid
136 * memory access. */
137 t = tm->timers;
138 while (t) {
139 next = t->next;
140 if (timespec_sooner(&t->ts, &now)) {
141 t->cb(t, t->cb_data);
142 /* Update next timer because it could have been modified
143 * in t->cb(). */
144 next = t->next;
145 cras_tm_cancel_timer(tm, t);
146 }
147 t = next;
148 }
149 }
150