xref: /haiku/src/libs/compat/freebsd_network/callout.cpp (revision 481f986b59e7782458dcc5fe98ad59a57480e5db)
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 			while (true) {
49 				struct callout* c = (callout*)list_get_next_item(&sTimers, c);
50 				if (c == NULL)
51 					break;
52 
53 				if (c->due < system_time()) {
54 					struct mtx *mutex = c->c_mtx;
55 
56 					// execute timer
57 					list_remove_item(&sTimers, c);
58 					c->due = -1;
59 					sCurrentCallout = c;
60 
61 					mutex_unlock(&sLock);
62 
63 					if (mutex != NULL)
64 						mtx_lock(mutex);
65 
66 					c->c_func(c->c_arg);
67 
68 					if (mutex != NULL)
69 						mtx_unlock(mutex);
70 
71 					mutex_lock(&sLock);
72 
73 					sCurrentCallout = NULL;
74 					c = NULL;
75 						// restart scanning as we unlocked the list
76 				} else {
77 					// calculate new timeout
78 					if (c->due < timeout)
79 						timeout = c->due;
80 				}
81 			}
82 
83 			sTimeout = timeout;
84 			mutex_unlock(&sLock);
85 		}
86 
87 		status = acquire_sem_etc(sWaitSem, 1, B_ABSOLUTE_TIMEOUT, timeout);
88 			// the wait sem normally can't be acquired, so we
89 			// have to look at the status value the call returns:
90 			//
91 			// B_OK - a new timer has been added or canceled
92 			// B_TIMED_OUT - look for timers to be executed
93 			// B_BAD_SEM_ID - we are asked to quit
94 	} while (status != B_BAD_SEM_ID);
95 
96 	return B_OK;
97 }
98 
99 
100 // #pragma mark - private API
101 
102 
103 status_t
104 init_callout(void)
105 {
106 	list_init(&sTimers);
107 	sTimeout = B_INFINITE_TIMEOUT;
108 
109 	status_t status = B_OK;
110 	mutex_init(&sLock, "fbsd callout");
111 
112 	sWaitSem = create_sem(0, "fbsd callout wait");
113 	if (sWaitSem < 0) {
114 		status = sWaitSem;
115 		goto err1;
116 	}
117 
118 	sThread = spawn_kernel_thread(callout_thread, "fbsd callout",
119 		B_URGENT_DISPLAY_PRIORITY, NULL);
120 	if (sThread < 0) {
121 		status = sThread;
122 		goto err2;
123 	}
124 
125 	return resume_thread(sThread);
126 
127 err1:
128 	mutex_destroy(&sLock);
129 err2:
130 	delete_sem(sWaitSem);
131 	return status;
132 }
133 
134 
135 void
136 uninit_callout(void)
137 {
138 	delete_sem(sWaitSem);
139 	mutex_lock(&sLock);
140 
141 	mutex_destroy(&sLock);
142 
143 	status_t status;
144 	wait_for_thread(sThread, &status);
145 }
146 
147 
148 // #pragma mark - public API
149 
150 
151 void
152 callout_init(struct callout *callout, int mpsafe)
153 {
154 	if (mpsafe)
155 		callout_init_mtx(callout, NULL, 0);
156 	else
157 		callout_init_mtx(callout, &Giant, 0);
158 }
159 
160 
161 void
162 callout_init_mtx(struct callout *c, struct mtx *mtx, int flags)
163 {
164 	c->due = 0;
165 	c->flags = 0;
166 
167 	c->c_arg = NULL;
168 	c->c_func = NULL;
169 	c->c_mtx = mtx;
170 	c->c_flags = flags;
171 }
172 
173 
174 int
175 callout_reset(struct callout *c, int when, void (*func)(void *), void *arg)
176 {
177 	int canceled = callout_stop(c);
178 
179 	MutexLocker locker(sLock);
180 
181 	c->c_func = func;
182 	c->c_arg = arg;
183 
184 	TRACE("callout_reset %p, func %p, arg %p\n", c, c->c_func, c->c_arg);
185 
186 	if (when >= 0) {
187 		// reschedule or add this timer
188 		if (c->due <= 0)
189 			list_add_item(&sTimers, c);
190 
191 		c->due = system_time() + when;
192 
193 		// notify timer about the change if necessary
194 		if (sTimeout > c->due)
195 			release_sem(sWaitSem);
196 	}
197 
198 	return canceled;
199 }
200 
201 
202 int
203 callout_schedule(struct callout *callout, int toTicks)
204 {
205 	return callout_reset(callout, toTicks, callout->c_func, callout->c_arg);
206 }
207 
208 
209 int
210 _callout_stop_safe(struct callout *c, int safe)
211 {
212 	MutexLocker locker(sLock);
213 
214 	TRACE("_callout_stop_safe %p, func %p, arg %p\n", c, c->c_func, c->c_arg);
215 
216 	if (c->due <= 0)
217 		return 0;
218 
219 	// this timer is scheduled, cancel it
220 	list_remove_item(&sTimers, c);
221 	c->due = 0;
222 	return 1;
223 }
224 
225 
226 int
227 callout_pending(struct callout *c)
228 {
229 	return c->due > 0;
230 }
231 
232 
233 int
234 callout_active(struct callout *c)
235 {
236 	return c == sCurrentCallout;
237 }
238