1 /*-
2 * Copyright (c) 2000 Jake Burkholder <jake@freebsd.org>.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD: releng/6.4/sys/kern/kern_condvar.c 181936 2008-08-20 19:07:21Z jhb $");
29
30 #include "opt_ktrace.h"
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/lock.h>
35 #include <sys/mutex.h>
36 #include <sys/proc.h>
37 #include <sys/kernel.h>
38 #include <sys/ktr.h>
39 #include <sys/condvar.h>
40 #include <sys/sched.h>
41 #include <sys/signalvar.h>
42 #include <sys/sleepqueue.h>
43 #include <sys/resourcevar.h>
44 #ifdef KTRACE
45 #include <sys/uio.h>
46 #include <sys/ktrace.h>
47 #endif
48
49 /*
50 * Common sanity checks for cv_wait* functions.
51 */
52 #define CV_ASSERT(cvp, mp, td) do { \
53 KASSERT((td) != NULL, ("%s: curthread NULL", __func__)); \
54 KASSERT(TD_IS_RUNNING(td), ("%s: not TDS_RUNNING", __func__)); \
55 KASSERT((cvp) != NULL, ("%s: cvp NULL", __func__)); \
56 KASSERT((mp) != NULL, ("%s: mp NULL", __func__)); \
57 mtx_assert((mp), MA_OWNED | MA_NOTRECURSED); \
58 } while (0)
59
60 /*
61 * Initialize a condition variable. Must be called before use.
62 */
63 void
64 cv_init(struct cv *cvp, const char *desc)
65 {
66
67 cvp->cv_description = desc;
68 cvp->cv_waiters = 0;
69 }
70
71 /*
72 * Destroy a condition variable. The condition variable must be re-initialized
73 * in order to be re-used.
74 */
75 void
76 cv_destroy(struct cv *cvp)
77 {
78 #ifdef INVARIANTS
79 struct sleepqueue *sq;
80
81 sleepq_lock(cvp);
82 sq = sleepq_lookup(cvp);
83 sleepq_release(cvp);
84 KASSERT(sq == NULL, ("%s: associated sleep queue non-empty", __func__));
85 #endif
86 }
87
88 /*
89 * Wait on a condition variable. The current thread is placed on the condition
90 * variable's wait queue and suspended. A cv_signal or cv_broadcast on the same
91 * condition variable will resume the thread. The mutex is released before
92 * sleeping and will be held on return. It is recommended that the mutex be
93 * held when cv_signal or cv_broadcast are called.
94 */
95 void
96 cv_wait(struct cv *cvp, struct mtx *mp)
97 {
98 struct thread *td;
99 WITNESS_SAVE_DECL(mp);
100
101 td = curthread;
102 #ifdef KTRACE
103 if (KTRPOINT(td, KTR_CSW))
104 ktrcsw(1, 0);
105 #endif
106 CV_ASSERT(cvp, mp, td);
107 WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, &mp->mtx_object,
108 "Waiting on \"%s\"", cvp->cv_description);
109 WITNESS_SAVE(&mp->mtx_object, mp);
110
111 if (cold || panicstr) {
112 /*
113 * During autoconfiguration, just give interrupts
114 * a chance, then just return. Don't run any other
115 * thread or panic below, in case this is the idle
116 * process and already asleep.
117 */
118 return;
119 }
120
121 sleepq_lock(cvp);
122
123 cvp->cv_waiters++;
124 DROP_GIANT();
125 mtx_unlock(mp);
126
127 sleepq_add(cvp, &mp->mtx_object, cvp->cv_description, SLEEPQ_CONDVAR,
128 0);
129 sleepq_wait(cvp);
130
131 #ifdef KTRACE
132 if (KTRPOINT(td, KTR_CSW))
133 ktrcsw(0, 0);
134 #endif
135 PICKUP_GIANT();
136 mtx_lock(mp);
137 WITNESS_RESTORE(&mp->mtx_object, mp);
138 }
139
140 /*
141 * Wait on a condition variable, allowing interruption by signals. Return 0 if
142 * the thread was resumed with cv_signal or cv_broadcast, EINTR or ERESTART if
143 * a signal was caught. If ERESTART is returned the system call should be
144 * restarted if possible.
145 */
146 int
147 cv_wait_sig(struct cv *cvp, struct mtx *mp)
148 {
149 struct thread *td;
150 struct proc *p;
151 int rval;
152 WITNESS_SAVE_DECL(mp);
153
154 td = curthread;
155 p = td->td_proc;
156 #ifdef KTRACE
157 if (KTRPOINT(td, KTR_CSW))
158 ktrcsw(1, 0);
159 #endif
160 CV_ASSERT(cvp, mp, td);
161 WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, &mp->mtx_object,
162 "Waiting on \"%s\"", cvp->cv_description);
163 WITNESS_SAVE(&mp->mtx_object, mp);
164
165 if (cold || panicstr) {
166 /*
167 * After a panic, or during autoconfiguration, just give
168 * interrupts a chance, then just return; don't run any other
169 * procs or panic below, in case this is the idle process and
170 * already asleep.
171 */
172 return (0);
173 }
174
175 sleepq_lock(cvp);
176
177 cvp->cv_waiters++;
178 DROP_GIANT();
179 mtx_unlock(mp);
180
181 sleepq_add(cvp, &mp->mtx_object, cvp->cv_description, SLEEPQ_CONDVAR |
182 SLEEPQ_INTERRUPTIBLE, 0);
183 rval = sleepq_wait_sig(cvp);
184
185 #ifdef KTRACE
186 if (KTRPOINT(td, KTR_CSW))
187 ktrcsw(0, 0);
188 #endif
189 PICKUP_GIANT();
190 mtx_lock(mp);
191 WITNESS_RESTORE(&mp->mtx_object, mp);
192
193 return (rval);
194 }
195
196 /*
197 * Wait on a condition variable for at most timo/hz seconds. Returns 0 if the
198 * process was resumed by cv_signal or cv_broadcast, EWOULDBLOCK if the timeout
199 * expires.
200 */
201 int
202 cv_timedwait(struct cv *cvp, struct mtx *mp, int timo)
203 {
204 struct thread *td;
205 int rval;
206 WITNESS_SAVE_DECL(mp);
207
208 td = curthread;
209 rval = 0;
210 #ifdef KTRACE
211 if (KTRPOINT(td, KTR_CSW))
212 ktrcsw(1, 0);
213 #endif
214 CV_ASSERT(cvp, mp, td);
215 WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, &mp->mtx_object,
216 "Waiting on \"%s\"", cvp->cv_description);
217 WITNESS_SAVE(&mp->mtx_object, mp);
218
219 if (cold || panicstr) {
220 /*
221 * After a panic, or during autoconfiguration, just give
222 * interrupts a chance, then just return; don't run any other
223 * thread or panic below, in case this is the idle process and
224 * already asleep.
225 */
226 return 0;
227 }
228
229 sleepq_lock(cvp);
230
231 cvp->cv_waiters++;
232 DROP_GIANT();
233 mtx_unlock(mp);
234
235 sleepq_add(cvp, &mp->mtx_object, cvp->cv_description, SLEEPQ_CONDVAR,
236 0);
237 sleepq_set_timeout(cvp, timo);
238 rval = sleepq_timedwait(cvp);
239
240 #ifdef KTRACE
241 if (KTRPOINT(td, KTR_CSW))
242 ktrcsw(0, 0);
243 #endif
244 PICKUP_GIANT();
245 mtx_lock(mp);
246 WITNESS_RESTORE(&mp->mtx_object, mp);
247
248 return (rval);
249 }
250
251 /*
252 * Wait on a condition variable for at most timo/hz seconds, allowing
253 * interruption by signals. Returns 0 if the thread was resumed by cv_signal
254 * or cv_broadcast, EWOULDBLOCK if the timeout expires, and EINTR or ERESTART if
255 * a signal was caught.
256 */
257 int
258 cv_timedwait_sig(struct cv *cvp, struct mtx *mp, int timo)
259 {
260 struct thread *td;
261 struct proc *p;
262 int rval;
263 WITNESS_SAVE_DECL(mp);
264
265 td = curthread;
266 p = td->td_proc;
267 rval = 0;
268 #ifdef KTRACE
269 if (KTRPOINT(td, KTR_CSW))
270 ktrcsw(1, 0);
271 #endif
272 CV_ASSERT(cvp, mp, td);
273 WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, &mp->mtx_object,
274 "Waiting on \"%s\"", cvp->cv_description);
275 WITNESS_SAVE(&mp->mtx_object, mp);
276
277 if (cold || panicstr) {
278 /*
279 * After a panic, or during autoconfiguration, just give
280 * interrupts a chance, then just return; don't run any other
281 * thread or panic below, in case this is the idle process and
282 * already asleep.
283 */
284 return 0;
285 }
286
287 sleepq_lock(cvp);
288
289 cvp->cv_waiters++;
290 DROP_GIANT();
291 mtx_unlock(mp);
292
293 sleepq_add(cvp, &mp->mtx_object, cvp->cv_description, SLEEPQ_CONDVAR |
294 SLEEPQ_INTERRUPTIBLE, 0);
295 sleepq_set_timeout(cvp, timo);
296 rval = sleepq_timedwait_sig(cvp);
297
298 #ifdef KTRACE
299 if (KTRPOINT(td, KTR_CSW))
300 ktrcsw(0, 0);
301 #endif
302 PICKUP_GIANT();
303 mtx_lock(mp);
304 WITNESS_RESTORE(&mp->mtx_object, mp);
305
306 return (rval);
307 }
308
309 /*
310 * Signal a condition variable, wakes up one waiting thread. Will also wakeup
311 * the swapper if the process is not in memory, so that it can bring the
312 * sleeping process in. Note that this may also result in additional threads
313 * being made runnable. Should be called with the same mutex as was passed to
314 * cv_wait held.
315 */
316 void
317 cv_signal(struct cv *cvp)
318 {
319
320 sleepq_lock(cvp);
321 if (cvp->cv_waiters > 0) {
322 cvp->cv_waiters--;
323 sleepq_signal(cvp, SLEEPQ_CONDVAR, -1, 0);
324 } else
325 sleepq_release(cvp);
326 }
327
328 /*
329 * Broadcast a signal to a condition variable. Wakes up all waiting threads.
330 * Should be called with the same mutex as was passed to cv_wait held.
331 */
332 void
333 cv_broadcastpri(struct cv *cvp, int pri)
334 {
335
336 sleepq_lock(cvp);
337 if (cvp->cv_waiters > 0) {
338 cvp->cv_waiters = 0;
339 sleepq_broadcast(cvp, SLEEPQ_CONDVAR, pri, 0);
340 } else
341 sleepq_release(cvp);
342 }
Cache object: 620d027d718d12581d2f236a08653e1e
|