]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/kern/kern_condvar.c
This commit was generated by cvs2svn to compensate for changes in r178525,
[FreeBSD/FreeBSD.git] / sys / kern / kern_condvar.c
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$");
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, lock, 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((lock) != NULL, ("%s: lock NULL", __func__));           \
57 } while (0)
58
59 /*
60  * Initialize a condition variable.  Must be called before use.
61  */
62 void
63 cv_init(struct cv *cvp, const char *desc)
64 {
65
66         cvp->cv_description = desc;
67         cvp->cv_waiters = 0;
68 }
69
70 /*
71  * Destroy a condition variable.  The condition variable must be re-initialized
72  * in order to be re-used.
73  */
74 void
75 cv_destroy(struct cv *cvp)
76 {
77 #ifdef INVARIANTS
78         struct sleepqueue *sq;
79
80         sleepq_lock(cvp);
81         sq = sleepq_lookup(cvp);
82         sleepq_release(cvp);
83         KASSERT(sq == NULL, ("%s: associated sleep queue non-empty", __func__));
84 #endif
85 }
86
87 /*
88  * Wait on a condition variable.  The current thread is placed on the condition
89  * variable's wait queue and suspended.  A cv_signal or cv_broadcast on the same
90  * condition variable will resume the thread.  The mutex is released before
91  * sleeping and will be held on return.  It is recommended that the mutex be
92  * held when cv_signal or cv_broadcast are called.
93  */
94 void
95 _cv_wait(struct cv *cvp, struct lock_object *lock)
96 {
97         WITNESS_SAVE_DECL(lock_witness);
98         struct lock_class *class;
99         struct thread *td;
100         int lock_state;
101
102         td = curthread;
103 #ifdef KTRACE
104         if (KTRPOINT(td, KTR_CSW))
105                 ktrcsw(1, 0);
106 #endif
107         CV_ASSERT(cvp, lock, td);
108         WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, lock,
109             "Waiting on \"%s\"", cvp->cv_description);
110         WITNESS_SAVE(lock, lock_witness);
111         class = LOCK_CLASS(lock);
112
113         if (cold || panicstr) {
114                 /*
115                  * During autoconfiguration, just give interrupts
116                  * a chance, then just return.  Don't run any other
117                  * thread or panic below, in case this is the idle
118                  * process and already asleep.
119                  */
120                 return;
121         }
122
123         sleepq_lock(cvp);
124
125         cvp->cv_waiters++;
126         DROP_GIANT();
127
128         sleepq_add(cvp, lock, cvp->cv_description, SLEEPQ_CONDVAR, 0);
129         if (class->lc_flags & LC_SLEEPABLE)
130                 sleepq_release(cvp);
131         lock_state = class->lc_unlock(lock);
132         if (class->lc_flags & LC_SLEEPABLE)
133                 sleepq_lock(cvp);
134         sleepq_wait(cvp, 0);
135
136 #ifdef KTRACE
137         if (KTRPOINT(td, KTR_CSW))
138                 ktrcsw(0, 0);
139 #endif
140         PICKUP_GIANT();
141         class->lc_lock(lock, lock_state);
142         WITNESS_RESTORE(lock, lock_witness);
143 }
144
145 /*
146  * Wait on a condition variable.  This function differs from cv_wait by
147  * not aquiring the mutex after condition variable was signaled.
148  */
149 void
150 _cv_wait_unlock(struct cv *cvp, struct lock_object *lock)
151 {
152         struct lock_class *class;
153         struct thread *td;
154
155         td = curthread;
156 #ifdef KTRACE
157         if (KTRPOINT(td, KTR_CSW))
158                 ktrcsw(1, 0);
159 #endif
160         CV_ASSERT(cvp, lock, td);
161         WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, lock,
162             "Waiting on \"%s\"", cvp->cv_description);
163         class = LOCK_CLASS(lock);
164
165         if (cold || panicstr) {
166                 /*
167                  * During autoconfiguration, just give interrupts
168                  * a chance, then just return.  Don't run any other
169                  * thread or panic below, in case this is the idle
170                  * process and already asleep.
171                  */
172                 class->lc_unlock(lock);
173                 return;
174         }
175
176         sleepq_lock(cvp);
177
178         cvp->cv_waiters++;
179         DROP_GIANT();
180
181         sleepq_add(cvp, lock, cvp->cv_description, SLEEPQ_CONDVAR, 0);
182         if (class->lc_flags & LC_SLEEPABLE)
183                 sleepq_release(cvp);
184         class->lc_unlock(lock);
185         if (class->lc_flags & LC_SLEEPABLE)
186                 sleepq_lock(cvp);
187         sleepq_wait(cvp, 0);
188
189 #ifdef KTRACE
190         if (KTRPOINT(td, KTR_CSW))
191                 ktrcsw(0, 0);
192 #endif
193         PICKUP_GIANT();
194 }
195
196 /*
197  * Wait on a condition variable, allowing interruption by signals.  Return 0 if
198  * the thread was resumed with cv_signal or cv_broadcast, EINTR or ERESTART if
199  * a signal was caught.  If ERESTART is returned the system call should be
200  * restarted if possible.
201  */
202 int
203 _cv_wait_sig(struct cv *cvp, struct lock_object *lock)
204 {
205         WITNESS_SAVE_DECL(lock_witness);
206         struct lock_class *class;
207         struct thread *td;
208         struct proc *p;
209         int lock_state, rval;
210
211         td = curthread;
212         p = td->td_proc;
213 #ifdef KTRACE
214         if (KTRPOINT(td, KTR_CSW))
215                 ktrcsw(1, 0);
216 #endif
217         CV_ASSERT(cvp, lock, td);
218         WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, lock,
219             "Waiting on \"%s\"", cvp->cv_description);
220         WITNESS_SAVE(lock, lock_witness);
221         class = LOCK_CLASS(lock);
222
223         if (cold || panicstr) {
224                 /*
225                  * After a panic, or during autoconfiguration, just give
226                  * interrupts a chance, then just return; don't run any other
227                  * procs or panic below, in case this is the idle process and
228                  * already asleep.
229                  */
230                 return (0);
231         }
232
233         sleepq_lock(cvp);
234
235         cvp->cv_waiters++;
236         DROP_GIANT();
237
238         sleepq_add(cvp, lock, cvp->cv_description, SLEEPQ_CONDVAR |
239             SLEEPQ_INTERRUPTIBLE, 0);
240         if (class->lc_flags & LC_SLEEPABLE)
241                 sleepq_release(cvp);
242         lock_state = class->lc_unlock(lock);
243         if (class->lc_flags & LC_SLEEPABLE)
244                 sleepq_lock(cvp);
245         rval = sleepq_wait_sig(cvp, 0);
246
247 #ifdef KTRACE
248         if (KTRPOINT(td, KTR_CSW))
249                 ktrcsw(0, 0);
250 #endif
251         PICKUP_GIANT();
252         class->lc_lock(lock, lock_state);
253         WITNESS_RESTORE(lock, lock_witness);
254
255         return (rval);
256 }
257
258 /*
259  * Wait on a condition variable for at most timo/hz seconds.  Returns 0 if the
260  * process was resumed by cv_signal or cv_broadcast, EWOULDBLOCK if the timeout
261  * expires.
262  */
263 int
264 _cv_timedwait(struct cv *cvp, struct lock_object *lock, int timo)
265 {
266         WITNESS_SAVE_DECL(lock_witness);
267         struct lock_class *class;
268         struct thread *td;
269         int lock_state, rval;
270
271         td = curthread;
272         rval = 0;
273 #ifdef KTRACE
274         if (KTRPOINT(td, KTR_CSW))
275                 ktrcsw(1, 0);
276 #endif
277         CV_ASSERT(cvp, lock, td);
278         WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, lock,
279             "Waiting on \"%s\"", cvp->cv_description);
280         WITNESS_SAVE(lock, lock_witness);
281         class = LOCK_CLASS(lock);
282
283         if (cold || panicstr) {
284                 /*
285                  * After a panic, or during autoconfiguration, just give
286                  * interrupts a chance, then just return; don't run any other
287                  * thread or panic below, in case this is the idle process and
288                  * already asleep.
289                  */
290                 return 0;
291         }
292
293         sleepq_lock(cvp);
294
295         cvp->cv_waiters++;
296         DROP_GIANT();
297
298         sleepq_add(cvp, lock, cvp->cv_description, SLEEPQ_CONDVAR, 0);
299         sleepq_set_timeout(cvp, timo);
300         if (class->lc_flags & LC_SLEEPABLE)
301                 sleepq_release(cvp);
302         lock_state = class->lc_unlock(lock);
303         if (class->lc_flags & LC_SLEEPABLE)
304                 sleepq_lock(cvp);
305         rval = sleepq_timedwait(cvp, 0);
306
307 #ifdef KTRACE
308         if (KTRPOINT(td, KTR_CSW))
309                 ktrcsw(0, 0);
310 #endif
311         PICKUP_GIANT();
312         class->lc_lock(lock, lock_state);
313         WITNESS_RESTORE(lock, lock_witness);
314
315         return (rval);
316 }
317
318 /*
319  * Wait on a condition variable for at most timo/hz seconds, allowing
320  * interruption by signals.  Returns 0 if the thread was resumed by cv_signal
321  * or cv_broadcast, EWOULDBLOCK if the timeout expires, and EINTR or ERESTART if
322  * a signal was caught.
323  */
324 int
325 _cv_timedwait_sig(struct cv *cvp, struct lock_object *lock, int timo)
326 {
327         WITNESS_SAVE_DECL(lock_witness);
328         struct lock_class *class;
329         struct thread *td;
330         struct proc *p;
331         int lock_state, rval;
332
333         td = curthread;
334         p = td->td_proc;
335         rval = 0;
336 #ifdef KTRACE
337         if (KTRPOINT(td, KTR_CSW))
338                 ktrcsw(1, 0);
339 #endif
340         CV_ASSERT(cvp, lock, td);
341         WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, lock,
342             "Waiting on \"%s\"", cvp->cv_description);
343         WITNESS_SAVE(lock, lock_witness);
344         class = LOCK_CLASS(lock);
345
346         if (cold || panicstr) {
347                 /*
348                  * After a panic, or during autoconfiguration, just give
349                  * interrupts a chance, then just return; don't run any other
350                  * thread or panic below, in case this is the idle process and
351                  * already asleep.
352                  */
353                 return 0;
354         }
355
356         sleepq_lock(cvp);
357
358         cvp->cv_waiters++;
359         DROP_GIANT();
360
361         sleepq_add(cvp, lock, cvp->cv_description, SLEEPQ_CONDVAR |
362             SLEEPQ_INTERRUPTIBLE, 0);
363         sleepq_set_timeout(cvp, timo);
364         if (class->lc_flags & LC_SLEEPABLE)
365                 sleepq_release(cvp);
366         lock_state = class->lc_unlock(lock);
367         if (class->lc_flags & LC_SLEEPABLE)
368                 sleepq_lock(cvp);
369         rval = sleepq_timedwait_sig(cvp, 0);
370
371 #ifdef KTRACE
372         if (KTRPOINT(td, KTR_CSW))
373                 ktrcsw(0, 0);
374 #endif
375         PICKUP_GIANT();
376         class->lc_lock(lock, lock_state);
377         WITNESS_RESTORE(lock, lock_witness);
378
379         return (rval);
380 }
381
382 /*
383  * Signal a condition variable, wakes up one waiting thread.  Will also wakeup
384  * the swapper if the process is not in memory, so that it can bring the
385  * sleeping process in.  Note that this may also result in additional threads
386  * being made runnable.  Should be called with the same mutex as was passed to
387  * cv_wait held.
388  */
389 void
390 cv_signal(struct cv *cvp)
391 {
392
393         sleepq_lock(cvp);
394         if (cvp->cv_waiters > 0) {
395                 cvp->cv_waiters--;
396                 sleepq_signal(cvp, SLEEPQ_CONDVAR, 0, 0);
397         }
398         sleepq_release(cvp);
399 }
400
401 /*
402  * Broadcast a signal to a condition variable.  Wakes up all waiting threads.
403  * Should be called with the same mutex as was passed to cv_wait held.
404  */
405 void
406 cv_broadcastpri(struct cv *cvp, int pri)
407 {
408         /*
409          * XXX sleepq_broadcast pri argument changed from -1 meaning
410          * no pri to 0 meaning no pri.
411          */
412         if (pri == -1)
413                 pri = 0;
414         sleepq_lock(cvp);
415         if (cvp->cv_waiters > 0) {
416                 cvp->cv_waiters = 0;
417                 sleepq_broadcast(cvp, SLEEPQ_CONDVAR, pri, 0);
418         }
419         sleepq_release(cvp);
420 }