summaryrefslogtreecommitdiff
path: root/cras/src/server/cras_tm.c
blob: c215fdc06d0bea5a41bd9a084daf67729771f315 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/* 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;
	}
}