1 /* $NetBSD: kern_condvar.c,v 1.14 2007/11/06 00:42:41 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.14 2007/11/06 00:42:41 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 59 static syncobj_t cv_syncobj = { 60 SOBJ_SLEEPQ_SORTED, 61 cv_unsleep, 62 sleepq_changepri, 63 sleepq_lendpri, 64 syncobj_noowner, 65 }; 66 67 static const char deadcv[] = "deadcv"; 68 69 /* 70 * cv_init: 71 * 72 * Initialize a condition variable for use. 73 */ 74 void 75 cv_init(kcondvar_t *cv, const char *wmesg) 76 { 77 78 KASSERT(wmesg != NULL); 79 80 cv->cv_wmesg = wmesg; 81 cv->cv_waiters = 0; 82 } 83 84 /* 85 * cv_destroy: 86 * 87 * Tear down a condition variable. 88 */ 89 void 90 cv_destroy(kcondvar_t *cv) 91 { 92 93 #ifdef DIAGNOSTIC 94 KASSERT(cv->cv_wmesg != deadcv && cv->cv_wmesg != NULL); 95 KASSERT(cv->cv_waiters == 0); 96 cv->cv_wmesg = deadcv; 97 #endif 98 } 99 100 /* 101 * cv_enter: 102 * 103 * Look up and lock the sleep queue corresponding to the given 104 * condition variable, and increment the number of waiters. 105 */ 106 static inline sleepq_t * 107 cv_enter(kcondvar_t *cv, kmutex_t *mtx, lwp_t *l) 108 { 109 sleepq_t *sq; 110 111 KASSERT(cv->cv_wmesg != deadcv && cv->cv_wmesg != NULL); 112 KASSERT((l->l_pflag & LP_INTR) == 0 || panicstr != NULL); 113 114 l->l_cv_signalled = 0; 115 l->l_kpriority = true; 116 sq = sleeptab_lookup(&sleeptab, cv); 117 cv->cv_waiters++; 118 sleepq_enter(sq, l); 119 sleepq_enqueue(sq, 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_wait: 173 * 174 * Wait non-interruptably on a condition variable until awoken. 175 */ 176 void 177 cv_wait(kcondvar_t *cv, kmutex_t *mtx) 178 { 179 lwp_t *l = curlwp; 180 sleepq_t *sq; 181 182 KASSERT(mutex_owned(mtx)); 183 184 if (sleepq_dontsleep(l)) { 185 (void)sleepq_abort(mtx, 0); 186 return; 187 } 188 189 sq = cv_enter(cv, mtx, l); 190 (void)sleepq_block(0, false); 191 (void)cv_exit(cv, mtx, l, 0); 192 } 193 194 /* 195 * cv_wait_sig: 196 * 197 * Wait on a condition variable until a awoken or a signal is received. 198 * Will also return early if the process is exiting. Returns zero if 199 * awoken normallly, ERESTART if a signal was received and the system 200 * call is restartable, or EINTR otherwise. 201 */ 202 int 203 cv_wait_sig(kcondvar_t *cv, kmutex_t *mtx) 204 { 205 lwp_t *l = curlwp; 206 sleepq_t *sq; 207 int error; 208 209 KASSERT(mutex_owned(mtx)); 210 211 if (sleepq_dontsleep(l)) 212 return sleepq_abort(mtx, 0); 213 214 sq = cv_enter(cv, mtx, l); 215 error = sleepq_block(0, true); 216 return cv_exit(cv, mtx, l, error); 217 } 218 219 /* 220 * cv_timedwait: 221 * 222 * Wait on a condition variable until awoken or the specified timeout 223 * expires. Returns zero if awoken normally or EWOULDBLOCK if the 224 * timeout expired. 225 */ 226 int 227 cv_timedwait(kcondvar_t *cv, kmutex_t *mtx, int timo) 228 { 229 lwp_t *l = curlwp; 230 sleepq_t *sq; 231 int error; 232 233 KASSERT(mutex_owned(mtx)); 234 235 if (sleepq_dontsleep(l)) 236 return sleepq_abort(mtx, 0); 237 238 sq = cv_enter(cv, mtx, l); 239 error = sleepq_block(timo, false); 240 return cv_exit(cv, mtx, l, error); 241 } 242 243 /* 244 * cv_timedwait_sig: 245 * 246 * Wait on a condition variable until a timeout expires, awoken or a 247 * signal is received. Will also return early if the process is 248 * exiting. Returns zero if awoken normallly, EWOULDBLOCK if the 249 * timeout expires, ERESTART if a signal was received and the system 250 * call is restartable, or EINTR otherwise. 251 */ 252 int 253 cv_timedwait_sig(kcondvar_t *cv, kmutex_t *mtx, int timo) 254 { 255 lwp_t *l = curlwp; 256 sleepq_t *sq; 257 int error; 258 259 KASSERT(mutex_owned(mtx)); 260 261 if (sleepq_dontsleep(l)) 262 return sleepq_abort(mtx, 0); 263 264 sq = cv_enter(cv, mtx, l); 265 error = sleepq_block(timo, true); 266 return cv_exit(cv, mtx, l, error); 267 } 268 269 /* 270 * cv_signal: 271 * 272 * Wake the highest priority LWP waiting on a condition variable. 273 * Must be called with the interlocking mutex held. 274 */ 275 void 276 cv_signal(kcondvar_t *cv) 277 { 278 lwp_t *l; 279 sleepq_t *sq; 280 281 if (cv->cv_waiters == 0) 282 return; 283 284 /* 285 * cv->cv_waiters may be stale and have dropped to zero, but 286 * while holding the interlock (the mutex passed to cv_wait() 287 * and similar) we will see non-zero values when it matters. 288 */ 289 290 sq = sleeptab_lookup(&sleeptab, cv); 291 if (cv->cv_waiters != 0) { 292 cv->cv_waiters--; 293 l = sleepq_wake(sq, cv, 1); 294 l->l_cv_signalled = 1; 295 } else 296 sleepq_unlock(sq); 297 } 298 299 /* 300 * cv_broadcast: 301 * 302 * Wake all LWPs waiting on a condition variable. Must be called 303 * with the interlocking mutex held. 304 */ 305 void 306 cv_broadcast(kcondvar_t *cv) 307 { 308 sleepq_t *sq; 309 u_int cnt; 310 311 if (cv->cv_waiters == 0) 312 return; 313 314 sq = sleeptab_lookup(&sleeptab, cv); 315 if ((cnt = cv->cv_waiters) != 0) { 316 cv->cv_waiters = 0; 317 sleepq_wake(sq, cv, cnt); 318 } else 319 sleepq_unlock(sq); 320 } 321 322 /* 323 * cv_wakeup: 324 * 325 * Wake all LWPs waiting on a condition variable. For cases 326 * where the address may be waited on by mtsleep()/tsleep(). 327 * Not a documented call. 328 */ 329 void 330 cv_wakeup(kcondvar_t *cv) 331 { 332 sleepq_t *sq; 333 334 sq = sleeptab_lookup(&sleeptab, cv); 335 cv->cv_waiters = 0; 336 sleepq_wake(sq, cv, (u_int)-1); 337 } 338 339 /* 340 * cv_has_waiters: 341 * 342 * For diagnostic assertions: return non-zero if a condition 343 * variable has waiters. 344 */ 345 bool 346 cv_has_waiters(kcondvar_t *cv) 347 { 348 349 /* No need to interlock here */ 350 return cv->cv_waiters != 0; 351 } 352