kern_condvar.c revision 1.13 1 /* $NetBSD: kern_condvar.c,v 1.13 2007/10/08 14:07:08 ad Exp $ */
2
3 /*-
4 * Copyright (c) 2006, 2007 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Andrew Doran.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 /*
40 * Kernel condition variable implementation, modeled after those found in
41 * Solaris, a description of which can be found in:
42 *
43 * Solaris Internals: Core Kernel Architecture, Jim Mauro and
44 * Richard McDougall.
45 */
46
47 #include <sys/cdefs.h>
48 __KERNEL_RCSID(0, "$NetBSD: kern_condvar.c,v 1.13 2007/10/08 14:07:08 ad Exp $");
49
50 #include <sys/param.h>
51 #include <sys/proc.h>
52 #include <sys/sched.h>
53 #include <sys/systm.h>
54 #include <sys/condvar.h>
55 #include <sys/sleepq.h>
56
57 static void cv_unsleep(lwp_t *);
58 static void cv_changepri(lwp_t *, pri_t);
59
60 static syncobj_t cv_syncobj = {
61 SOBJ_SLEEPQ_SORTED,
62 cv_unsleep,
63 cv_changepri,
64 sleepq_lendpri,
65 syncobj_noowner,
66 };
67
68 static const char deadcv[] = "deadcv";
69
70 /*
71 * cv_init:
72 *
73 * Initialize a condition variable for use.
74 */
75 void
76 cv_init(kcondvar_t *cv, const char *wmesg)
77 {
78
79 KASSERT(wmesg != NULL);
80
81 cv->cv_wmesg = wmesg;
82 cv->cv_waiters = 0;
83 }
84
85 /*
86 * cv_destroy:
87 *
88 * Tear down a condition variable.
89 */
90 void
91 cv_destroy(kcondvar_t *cv)
92 {
93
94 #ifdef DIAGNOSTIC
95 KASSERT(cv->cv_wmesg != deadcv && cv->cv_wmesg != NULL);
96 KASSERT(cv->cv_waiters == 0);
97 cv->cv_wmesg = deadcv;
98 #endif
99 }
100
101 /*
102 * cv_enter:
103 *
104 * Look up and lock the sleep queue corresponding to the given
105 * condition variable, and increment the number of waiters.
106 */
107 static inline sleepq_t *
108 cv_enter(kcondvar_t *cv, kmutex_t *mtx, lwp_t *l)
109 {
110 sleepq_t *sq;
111
112 KASSERT(cv->cv_wmesg != deadcv && cv->cv_wmesg != NULL);
113 KASSERT((l->l_flag & LW_INTR) == 0 || panicstr != NULL);
114
115 l->l_cv_signalled = 0;
116 sq = sleeptab_lookup(&sleeptab, cv);
117 cv->cv_waiters++;
118 sleepq_enter(sq, l);
119 sleepq_enqueue(sq, sched_kpri(l), cv, cv->cv_wmesg, &cv_syncobj);
120 mutex_exit(mtx);
121
122 return sq;
123 }
124
125 /*
126 * cv_exit:
127 *
128 * After resuming execution, check to see if we have been restarted
129 * as a result of cv_signal(). If we have, but cannot take the
130 * wakeup (because of eg a pending Unix signal or timeout) then try
131 * to ensure that another LWP sees it. This is necessary because
132 * there may be multiple waiters, and at least one should take the
133 * wakeup if possible.
134 */
135 static inline int
136 cv_exit(kcondvar_t *cv, kmutex_t *mtx, lwp_t *l, const int error)
137 {
138
139 mutex_enter(mtx);
140 if (__predict_false(error != 0) && l->l_cv_signalled != 0)
141 cv_signal(cv);
142
143 KASSERT(cv->cv_wmesg != deadcv && cv->cv_wmesg != NULL);
144
145 return error;
146 }
147
148 /*
149 * cv_unsleep:
150 *
151 * Remove an LWP from the condition variable and sleep queue. This
152 * is called when the LWP has not been awoken normally but instead
153 * interrupted: for example, when a signal is received. Must be
154 * called with the LWP locked, and must return it unlocked.
155 */
156 static void
157 cv_unsleep(lwp_t *l)
158 {
159 kcondvar_t *cv;
160
161 KASSERT(l->l_wchan != NULL);
162 KASSERT(lwp_locked(l, l->l_sleepq->sq_mutex));
163
164 cv = (kcondvar_t *)(uintptr_t)l->l_wchan;
165 KASSERT(cv->cv_wmesg != deadcv && cv->cv_wmesg != NULL);
166 cv->cv_waiters--;
167
168 sleepq_unsleep(l);
169 }
170
171 /*
172 * cv_changepri:
173 *
174 * Adjust the real (user) priority of an LWP blocked on a CV.
175 */
176 static void
177 cv_changepri(lwp_t *l, pri_t pri)
178 {
179 sleepq_t *sq = l->l_sleepq;
180 pri_t opri;
181
182 KASSERT(lwp_locked(l, sq->sq_mutex));
183
184 opri = lwp_eprio(l);
185 l->l_usrpri = pri;
186 l->l_priority = sched_kpri(l);
187
188 if (lwp_eprio(l) != opri) {
189 TAILQ_REMOVE(&sq->sq_queue, l, l_sleepchain);
190 sleepq_insert(sq, l, l->l_syncobj);
191 }
192 }
193
194 /*
195 * cv_wait:
196 *
197 * Wait non-interruptably on a condition variable until awoken.
198 */
199 void
200 cv_wait(kcondvar_t *cv, kmutex_t *mtx)
201 {
202 lwp_t *l = curlwp;
203 sleepq_t *sq;
204
205 KASSERT(mutex_owned(mtx));
206
207 if (sleepq_dontsleep(l)) {
208 (void)sleepq_abort(mtx, 0);
209 return;
210 }
211
212 sq = cv_enter(cv, mtx, l);
213 (void)sleepq_block(0, false);
214 (void)cv_exit(cv, mtx, l, 0);
215 }
216
217 /*
218 * cv_wait_sig:
219 *
220 * Wait on a condition variable until a awoken or a signal is received.
221 * Will also return early if the process is exiting. Returns zero if
222 * awoken normallly, ERESTART if a signal was received and the system
223 * call is restartable, or EINTR otherwise.
224 */
225 int
226 cv_wait_sig(kcondvar_t *cv, kmutex_t *mtx)
227 {
228 lwp_t *l = curlwp;
229 sleepq_t *sq;
230 int error;
231
232 KASSERT(mutex_owned(mtx));
233
234 if (sleepq_dontsleep(l))
235 return sleepq_abort(mtx, 0);
236
237 sq = cv_enter(cv, mtx, l);
238 error = sleepq_block(0, true);
239 return cv_exit(cv, mtx, l, error);
240 }
241
242 /*
243 * cv_timedwait:
244 *
245 * Wait on a condition variable until awoken or the specified timeout
246 * expires. Returns zero if awoken normally or EWOULDBLOCK if the
247 * timeout expired.
248 */
249 int
250 cv_timedwait(kcondvar_t *cv, kmutex_t *mtx, int timo)
251 {
252 lwp_t *l = curlwp;
253 sleepq_t *sq;
254 int error;
255
256 KASSERT(mutex_owned(mtx));
257
258 if (sleepq_dontsleep(l))
259 return sleepq_abort(mtx, 0);
260
261 sq = cv_enter(cv, mtx, l);
262 error = sleepq_block(timo, false);
263 return cv_exit(cv, mtx, l, error);
264 }
265
266 /*
267 * cv_timedwait_sig:
268 *
269 * Wait on a condition variable until a timeout expires, awoken or a
270 * signal is received. Will also return early if the process is
271 * exiting. Returns zero if awoken normallly, EWOULDBLOCK if the
272 * timeout expires, ERESTART if a signal was received and the system
273 * call is restartable, or EINTR otherwise.
274 */
275 int
276 cv_timedwait_sig(kcondvar_t *cv, kmutex_t *mtx, int timo)
277 {
278 lwp_t *l = curlwp;
279 sleepq_t *sq;
280 int error;
281
282 KASSERT(mutex_owned(mtx));
283
284 if (sleepq_dontsleep(l))
285 return sleepq_abort(mtx, 0);
286
287 sq = cv_enter(cv, mtx, l);
288 error = sleepq_block(timo, true);
289 return cv_exit(cv, mtx, l, error);
290 }
291
292 /*
293 * cv_signal:
294 *
295 * Wake the highest priority LWP waiting on a condition variable.
296 * Must be called with the interlocking mutex held.
297 */
298 void
299 cv_signal(kcondvar_t *cv)
300 {
301 lwp_t *l;
302 sleepq_t *sq;
303
304 if (cv->cv_waiters == 0)
305 return;
306
307 /*
308 * cv->cv_waiters may be stale and have dropped to zero, but
309 * while holding the interlock (the mutex passed to cv_wait()
310 * and similar) we will see non-zero values when it matters.
311 */
312
313 sq = sleeptab_lookup(&sleeptab, cv);
314 if (cv->cv_waiters != 0) {
315 cv->cv_waiters--;
316 l = sleepq_wake(sq, cv, 1);
317 l->l_cv_signalled = 1;
318 } else
319 sleepq_unlock(sq);
320 }
321
322 /*
323 * cv_broadcast:
324 *
325 * Wake all LWPs waiting on a condition variable. Must be called
326 * with the interlocking mutex held.
327 */
328 void
329 cv_broadcast(kcondvar_t *cv)
330 {
331 sleepq_t *sq;
332 u_int cnt;
333
334 if (cv->cv_waiters == 0)
335 return;
336
337 sq = sleeptab_lookup(&sleeptab, cv);
338 if ((cnt = cv->cv_waiters) != 0) {
339 cv->cv_waiters = 0;
340 sleepq_wake(sq, cv, cnt);
341 } else
342 sleepq_unlock(sq);
343 }
344
345 /*
346 * cv_wakeup:
347 *
348 * Wake all LWPs waiting on a condition variable. For cases
349 * where the address may be waited on by mtsleep()/tsleep().
350 * Not a documented call.
351 */
352 void
353 cv_wakeup(kcondvar_t *cv)
354 {
355 sleepq_t *sq;
356
357 sq = sleeptab_lookup(&sleeptab, cv);
358 cv->cv_waiters = 0;
359 sleepq_wake(sq, cv, (u_int)-1);
360 }
361
362 /*
363 * cv_has_waiters:
364 *
365 * For diagnostic assertions: return non-zero if a condition
366 * variable has waiters.
367 */
368 bool
369 cv_has_waiters(kcondvar_t *cv)
370 {
371
372 /* No need to interlock here */
373 return cv->cv_waiters != 0;
374 }
375