xref: /haiku/src/libs/compat/freebsd_network/callout.cpp (revision 220d04022750f40f8bac8f01fa551211e28d04f2)
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 
12 extern "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 
28 static struct list sTimers;
29 static mutex sLock;
30 static sem_id sWaitSem;
31 static callout* sCurrentCallout;
32 static thread_id sThread;
33 static bigtime_t sTimeout;
34 
35 
36 static status_t
37 callout_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 
104 status_t
105 init_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 
128 err1:
129 	mutex_destroy(&sLock);
130 err2:
131 	delete_sem(sWaitSem);
132 	return status;
133 }
134 
135 
136 void
137 uninit_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 
152 void
153 callout_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 
162 void
163 callout_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 
175 int
176 callout_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 
203 int
204 callout_schedule(struct callout *callout, int ticks)
205 {
206 	return callout_reset(callout, ticks, callout->c_func, callout->c_arg);
207 }
208 
209 
210 int
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 
227 int
228 callout_pending(struct callout *c)
229 {
230 	return c->due > 0;
231 }
232 
233 
234 int
235 callout_active(struct callout *c)
236 {
237 	return c == sCurrentCallout;
238 }
239