1/*
2 * Copyright 2010, Axel D��rfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "device.h"
8
9#include <lock.h>
10#include <thread.h>
11
12extern "C" {
13#	include <sys/callout.h>
14#	include <sys/mutex.h>
15}
16
17#include <util/AutoLock.h>
18
19
20//#define TRACE_CALLOUT
21#ifdef TRACE_CALLOUT
22#	define TRACE(x...) dprintf(x)
23#else
24#	define TRACE(x...) ;
25#endif
26
27
28static struct list sTimers;
29static mutex sLock;
30static sem_id sWaitSem;
31static callout* sCurrentCallout;
32static thread_id sThread;
33static bigtime_t sTimeout;
34
35
36static status_t
37callout_thread(void* /*data*/)
38{
39	status_t status = B_OK;
40
41	do {
42		bigtime_t timeout = B_INFINITE_TIMEOUT;
43
44		if (status == B_TIMED_OUT || status == B_OK) {
45			// scan timers for new timeout and/or execute a timer
46			mutex_lock(&sLock);
47
48			struct callout* c = NULL;
49			while (true) {
50				c = (callout*)list_get_next_item(&sTimers, c);
51				if (c == NULL)
52					break;
53
54				if (c->due < system_time()) {
55					struct mtx *mutex = c->c_mtx;
56
57					// execute timer
58					list_remove_item(&sTimers, c);
59					c->due = -1;
60					sCurrentCallout = c;
61
62					mutex_unlock(&sLock);
63
64					if (mutex != NULL)
65						mtx_lock(mutex);
66
67					c->c_func(c->c_arg);
68
69					if (mutex != NULL)
70						mtx_unlock(mutex);
71
72					mutex_lock(&sLock);
73
74					sCurrentCallout = NULL;
75					c = NULL;
76						// restart scanning as we unlocked the list
77				} else {
78					// calculate new timeout
79					if (c->due < timeout)
80						timeout = c->due;
81				}
82			}
83
84			sTimeout = timeout;
85			mutex_unlock(&sLock);
86		}
87
88		status = acquire_sem_etc(sWaitSem, 1, B_ABSOLUTE_TIMEOUT, timeout);
89			// the wait sem normally can't be acquired, so we
90			// have to look at the status value the call returns:
91			//
92			// B_OK - a new timer has been added or canceled
93			// B_TIMED_OUT - look for timers to be executed
94			// B_BAD_SEM_ID - we are asked to quit
95	} while (status != B_BAD_SEM_ID);
96
97	return B_OK;
98}
99
100
101// #pragma mark - private API
102
103
104status_t
105init_callout(void)
106{
107	list_init(&sTimers);
108	sTimeout = B_INFINITE_TIMEOUT;
109
110	status_t status = B_OK;
111	mutex_init(&sLock, "fbsd callout");
112
113	sWaitSem = create_sem(0, "fbsd callout wait");
114	if (sWaitSem < 0) {
115		status = sWaitSem;
116		goto err1;
117	}
118
119	sThread = spawn_kernel_thread(callout_thread, "fbsd callout",
120		B_DISPLAY_PRIORITY, NULL);
121	if (sThread < 0) {
122		status = sThread;
123		goto err2;
124	}
125
126	return resume_thread(sThread);
127
128err1:
129	mutex_destroy(&sLock);
130err2:
131	delete_sem(sWaitSem);
132	return status;
133}
134
135
136void
137uninit_callout(void)
138{
139	delete_sem(sWaitSem);
140	mutex_lock(&sLock);
141
142	mutex_destroy(&sLock);
143
144	status_t status;
145	wait_for_thread(sThread, &status);
146}
147
148
149// #pragma mark - public API
150
151
152void
153callout_init(struct callout *callout, int mpsafe)
154{
155	if (mpsafe)
156		callout_init_mtx(callout, NULL, 0);
157	else
158		callout_init_mtx(callout, &Giant, 0);
159}
160
161
162void
163callout_init_mtx(struct callout *c, struct mtx *mtx, int flags)
164{
165	c->due = 0;
166	c->flags = 0;
167
168	c->c_arg = NULL;
169	c->c_func = NULL;
170	c->c_mtx = mtx;
171	c->c_flags = flags;
172}
173
174
175int
176callout_reset(struct callout *c, int ticks, void (*func)(void *), void *arg)
177{
178	int canceled = callout_stop(c);
179
180	MutexLocker locker(sLock);
181
182	c->c_func = func;
183	c->c_arg = arg;
184
185	TRACE("callout_reset %p, func %p, arg %p\n", c, c->c_func, c->c_arg);
186
187	if (ticks >= 0) {
188		// reschedule or add this timer
189		if (c->due <= 0)
190			list_add_item(&sTimers, c);
191
192		c->due = system_time() + ticks_to_usecs(ticks);
193
194		// notify timer about the change if necessary
195		if (sTimeout > c->due)
196			release_sem(sWaitSem);
197	}
198
199	return canceled;
200}
201
202
203int
204callout_schedule(struct callout *callout, int ticks)
205{
206	return callout_reset(callout, ticks, callout->c_func, callout->c_arg);
207}
208
209
210int
211_callout_stop_safe(struct callout *c, int safe)
212{
213	MutexLocker locker(sLock);
214
215	TRACE("_callout_stop_safe %p, func %p, arg %p\n", c, c->c_func, c->c_arg);
216
217	if (c->due <= 0)
218		return 0;
219
220	// this timer is scheduled, cancel it
221	list_remove_item(&sTimers, c);
222	c->due = 0;
223	return 1;
224}
225
226
227int
228callout_pending(struct callout *c)
229{
230	return c->due > 0;
231}
232
233
234int
235callout_active(struct callout *c)
236{
237	return c == sCurrentCallout;
238}
239