Changes in kernel/generic/src/synch/waitq.c [6ec34bb:1d432f9] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
kernel/generic/src/synch/waitq.c
r6ec34bb r1d432f9 33 33 /** 34 34 * @file 35 * @brief 35 * @brief Wait queue. 36 36 * 37 37 * Wait queue is the basic synchronization primitive upon which all … … 41 41 * fashion. Conditional operation as well as timeouts and interruptions 42 42 * are supported. 43 * 43 44 */ 44 45 … … 56 57 #include <arch/cycle.h> 57 58 58 static void waitq_sleep_timed_out(void * data);59 static void waitq_sleep_timed_out(void *); 59 60 60 61 /** Initialize wait queue … … 62 63 * Initialize wait queue. 63 64 * 64 * @param wq Pointer to wait queue to be initialized. 65 * @param wq Pointer to wait queue to be initialized. 66 * 65 67 */ 66 68 void waitq_initialize(waitq_t *wq) 67 69 { 68 spinlock_initialize(&wq->lock, "waitq_lock");70 irq_spinlock_initialize(&wq->lock, "wq.lock"); 69 71 list_initialize(&wq->head); 70 72 wq->missed_wakeups = 0; … … 81 83 * timeout at all. 82 84 * 83 * @param data Pointer to the thread that called waitq_sleep_timeout(). 85 * @param data Pointer to the thread that called waitq_sleep_timeout(). 86 * 84 87 */ 85 88 void waitq_sleep_timed_out(void *data) 86 89 { 87 thread_t *t = (thread_t *) data; 88 waitq_t *wq; 90 thread_t *thread = (thread_t *) data; 89 91 bool do_wakeup = false; 90 92 DEADLOCK_PROBE_INIT(p_wqlock); 91 92 spinlock_lock(&threads_lock);93 if (!thread_exists(t ))93 94 irq_spinlock_lock(&threads_lock, false); 95 if (!thread_exists(thread)) 94 96 goto out; 95 97 96 98 grab_locks: 97 spinlock_lock(&t->lock); 98 if ((wq = t->sleep_queue)) { /* assignment */ 99 if (!spinlock_trylock(&wq->lock)) { 100 spinlock_unlock(&t->lock); 99 irq_spinlock_lock(&thread->lock, false); 100 101 waitq_t *wq; 102 if ((wq = thread->sleep_queue)) { /* Assignment */ 103 if (!irq_spinlock_trylock(&wq->lock)) { 104 irq_spinlock_unlock(&thread->lock, false); 101 105 DEADLOCK_PROBE(p_wqlock, DEADLOCK_THRESHOLD); 102 goto grab_locks; /* avoid deadlock */ 103 } 104 105 list_remove(&t->wq_link); 106 t->saved_context = t->sleep_timeout_context; 106 /* Avoid deadlock */ 107 goto grab_locks; 108 } 109 110 list_remove(&thread->wq_link); 111 thread->saved_context = thread->sleep_timeout_context; 107 112 do_wakeup = true; 108 t ->sleep_queue = NULL;109 spinlock_unlock(&wq->lock);110 } 111 112 t ->timeout_pending = false;113 spinlock_unlock(&t->lock);113 thread->sleep_queue = NULL; 114 irq_spinlock_unlock(&wq->lock, false); 115 } 116 117 thread->timeout_pending = false; 118 irq_spinlock_unlock(&thread->lock, false); 114 119 115 120 if (do_wakeup) 116 thread_ready(t );117 121 thread_ready(thread); 122 118 123 out: 119 spinlock_unlock(&threads_lock);124 irq_spinlock_unlock(&threads_lock, false); 120 125 } 121 126 … … 125 130 * If the thread is not found sleeping, no action is taken. 126 131 * 127 * @param t Thread to be interrupted. 128 */ 129 void waitq_interrupt_sleep(thread_t *t) 130 { 132 * @param thread Thread to be interrupted. 133 * 134 */ 135 void waitq_interrupt_sleep(thread_t *thread) 136 { 137 bool do_wakeup = false; 138 DEADLOCK_PROBE_INIT(p_wqlock); 139 140 irq_spinlock_lock(&threads_lock, true); 141 if (!thread_exists(thread)) 142 goto out; 143 144 grab_locks: 145 irq_spinlock_lock(&thread->lock, false); 146 131 147 waitq_t *wq; 132 bool do_wakeup = false; 133 ipl_t ipl; 134 DEADLOCK_PROBE_INIT(p_wqlock); 135 136 ipl = interrupts_disable(); 137 spinlock_lock(&threads_lock); 138 if (!thread_exists(t)) 139 goto out; 140 141 grab_locks: 142 spinlock_lock(&t->lock); 143 if ((wq = t->sleep_queue)) { /* assignment */ 144 if (!(t->sleep_interruptible)) { 148 if ((wq = thread->sleep_queue)) { /* Assignment */ 149 if (!(thread->sleep_interruptible)) { 145 150 /* 146 151 * The sleep cannot be interrupted. 152 * 147 153 */ 148 spinlock_unlock(&t->lock);154 irq_spinlock_unlock(&thread->lock, false); 149 155 goto out; 150 156 } 151 152 if (! spinlock_trylock(&wq->lock)) {153 spinlock_unlock(&t->lock);157 158 if (!irq_spinlock_trylock(&wq->lock)) { 159 irq_spinlock_unlock(&thread->lock, false); 154 160 DEADLOCK_PROBE(p_wqlock, DEADLOCK_THRESHOLD); 155 goto grab_locks; /* avoid deadlock */ 156 } 157 158 if (t->timeout_pending && timeout_unregister(&t->sleep_timeout)) 159 t->timeout_pending = false; 160 161 list_remove(&t->wq_link); 162 t->saved_context = t->sleep_interruption_context; 161 /* Avoid deadlock */ 162 goto grab_locks; 163 } 164 165 if ((thread->timeout_pending) && 166 (timeout_unregister(&thread->sleep_timeout))) 167 thread->timeout_pending = false; 168 169 list_remove(&thread->wq_link); 170 thread->saved_context = thread->sleep_interruption_context; 163 171 do_wakeup = true; 164 t ->sleep_queue = NULL;165 spinlock_unlock(&wq->lock);166 } 167 spinlock_unlock(&t->lock);168 172 thread->sleep_queue = NULL; 173 irq_spinlock_unlock(&wq->lock, false); 174 } 175 irq_spinlock_unlock(&thread->lock, false); 176 169 177 if (do_wakeup) 170 thread_ready(t );171 178 thread_ready(thread); 179 172 180 out: 173 spinlock_unlock(&threads_lock); 174 interrupts_restore(ipl); 181 irq_spinlock_unlock(&threads_lock, true); 175 182 } 176 183 … … 180 187 * is sleeping interruptibly. 181 188 * 182 * @param wq Pointer to wait queue. 189 * @param wq Pointer to wait queue. 190 * 183 191 */ 184 192 void waitq_unsleep(waitq_t *wq) 185 193 { 186 ipl_t ipl; 187 188 ipl = interrupts_disable(); 189 spinlock_lock(&wq->lock); 190 194 irq_spinlock_lock(&wq->lock, true); 195 191 196 if (!list_empty(&wq->head)) { 192 thread_t *t; 193 194 t = list_get_instance(wq->head.next, thread_t, wq_link); 195 spinlock_lock(&t->lock); 196 ASSERT(t->sleep_interruptible); 197 if (t->timeout_pending && timeout_unregister(&t->sleep_timeout)) 198 t->timeout_pending = false; 199 list_remove(&t->wq_link); 200 t->saved_context = t->sleep_interruption_context; 201 t->sleep_queue = NULL; 202 spinlock_unlock(&t->lock); 203 thread_ready(t); 204 } 205 206 spinlock_unlock(&wq->lock); 207 interrupts_restore(ipl); 208 } 197 thread_t *thread = list_get_instance(wq->head.next, thread_t, wq_link); 198 199 irq_spinlock_lock(&thread->lock, false); 200 201 ASSERT(thread->sleep_interruptible); 202 203 if ((thread->timeout_pending) && 204 (timeout_unregister(&thread->sleep_timeout))) 205 thread->timeout_pending = false; 206 207 list_remove(&thread->wq_link); 208 thread->saved_context = thread->sleep_interruption_context; 209 thread->sleep_queue = NULL; 210 211 irq_spinlock_unlock(&thread->lock, false); 212 thread_ready(thread); 213 } 214 215 irq_spinlock_unlock(&wq->lock, true); 216 } 217 218 #define PARAM_NON_BLOCKING(flags, usec) \ 219 (((flags) & SYNCH_FLAGS_NON_BLOCKING) && ((usec) == 0)) 209 220 210 221 /** Sleep until either wakeup, timeout or interruption occurs … … 218 229 * and all the *_timeout() functions use it. 219 230 * 220 * @param wq 221 * @param usec 222 * @param flags 231 * @param wq Pointer to wait queue. 232 * @param usec Timeout in microseconds. 233 * @param flags Specify mode of the sleep. 223 234 * 224 235 * The sleep can be interrupted only if the 225 236 * SYNCH_FLAGS_INTERRUPTIBLE bit is specified in flags. 226 * 237 * 227 238 * If usec is greater than zero, regardless of the value of the 228 239 * SYNCH_FLAGS_NON_BLOCKING bit in flags, the call will not return until either 229 * timeout, interruption or wakeup comes. 240 * timeout, interruption or wakeup comes. 230 241 * 231 242 * If usec is zero and the SYNCH_FLAGS_NON_BLOCKING bit is not set in flags, … … 235 246 * call will immediately return, reporting either success or failure. 236 247 * 237 * @return Returns one of ESYNCH_WOULD_BLOCK, ESYNCH_TIMEOUT, 238 * ESYNCH_INTERRUPTED, ESYNCH_OK_ATOMIC and 239 * ESYNCH_OK_BLOCKED. 240 * 241 * @li ESYNCH_WOULD_BLOCK means that the sleep failed because at the time of 242 * the call there was no pending wakeup. 243 * 244 * @li ESYNCH_TIMEOUT means that the sleep timed out. 245 * 246 * @li ESYNCH_INTERRUPTED means that somebody interrupted the sleeping thread. 247 * 248 * @li ESYNCH_OK_ATOMIC means that the sleep succeeded and that there was 249 * a pending wakeup at the time of the call. The caller was not put 250 * asleep at all. 251 * 252 * @li ESYNCH_OK_BLOCKED means that the sleep succeeded; the full sleep was 253 * attempted. 254 */ 255 int waitq_sleep_timeout(waitq_t *wq, uint32_t usec, int flags) 256 { 257 ipl_t ipl; 258 int rc; 259 260 ipl = waitq_sleep_prepare(wq); 261 rc = waitq_sleep_timeout_unsafe(wq, usec, flags); 248 * @return ESYNCH_WOULD_BLOCK, meaning that the sleep failed because at the 249 * time of the call there was no pending wakeup 250 * @return ESYNCH_TIMEOUT, meaning that the sleep timed out. 251 * @return ESYNCH_INTERRUPTED, meaning that somebody interrupted the sleeping 252 * thread. 253 * @return ESYNCH_OK_ATOMIC, meaning that the sleep succeeded and that there 254 * was a pending wakeup at the time of the call. The caller was not put 255 * asleep at all. 256 * @return ESYNCH_OK_BLOCKED, meaning that the sleep succeeded; the full sleep 257 * was attempted. 258 * 259 */ 260 int waitq_sleep_timeout(waitq_t *wq, uint32_t usec, unsigned int flags) 261 { 262 ASSERT((!PREEMPTION_DISABLED) || (PARAM_NON_BLOCKING(flags, usec))); 263 264 ipl_t ipl = waitq_sleep_prepare(wq); 265 int rc = waitq_sleep_timeout_unsafe(wq, usec, flags); 262 266 waitq_sleep_finish(wq, rc, ipl); 263 267 return rc; … … 269 273 * and interrupts disabled. 270 274 * 271 * @param wq Wait queue. 272 * 273 * @return Interrupt level as it existed on entry to this function. 275 * @param wq Wait queue. 276 * 277 * @return Interrupt level as it existed on entry to this function. 278 * 274 279 */ 275 280 ipl_t waitq_sleep_prepare(waitq_t *wq) … … 279 284 restart: 280 285 ipl = interrupts_disable(); 281 282 if (THREAD) { /* needed during system initiailzation */286 287 if (THREAD) { /* Needed during system initiailzation */ 283 288 /* 284 289 * Busy waiting for a delayed timeout. … … 287 292 * Simply, the thread is not allowed to go to sleep if 288 293 * there are timeouts in progress. 294 * 289 295 */ 290 spinlock_lock(&THREAD->lock); 296 irq_spinlock_lock(&THREAD->lock, false); 297 291 298 if (THREAD->timeout_pending) { 292 spinlock_unlock(&THREAD->lock);299 irq_spinlock_unlock(&THREAD->lock, false); 293 300 interrupts_restore(ipl); 294 301 goto restart; 295 302 } 296 spinlock_unlock(&THREAD->lock); 297 } 298 299 spinlock_lock(&wq->lock); 303 304 irq_spinlock_unlock(&THREAD->lock, false); 305 } 306 307 irq_spinlock_lock(&wq->lock, false); 300 308 return ipl; 301 309 } … … 307 315 * lock is released. 308 316 * 309 * @param wq Wait queue. 310 * @param rc Return code of waitq_sleep_timeout_unsafe(). 311 * @param ipl Interrupt level returned by waitq_sleep_prepare(). 317 * @param wq Wait queue. 318 * @param rc Return code of waitq_sleep_timeout_unsafe(). 319 * @param ipl Interrupt level returned by waitq_sleep_prepare(). 320 * 312 321 */ 313 322 void waitq_sleep_finish(waitq_t *wq, int rc, ipl_t ipl) … … 316 325 case ESYNCH_WOULD_BLOCK: 317 326 case ESYNCH_OK_ATOMIC: 318 spinlock_unlock(&wq->lock);327 irq_spinlock_unlock(&wq->lock, false); 319 328 break; 320 329 default: 321 330 break; 322 331 } 332 323 333 interrupts_restore(ipl); 324 334 } … … 330 340 * and followed by a call to waitq_sleep_finish(). 331 341 * 332 * @param wq See waitq_sleep_timeout(). 333 * @param usec See waitq_sleep_timeout(). 334 * @param flags See waitq_sleep_timeout(). 335 * 336 * @return See waitq_sleep_timeout(). 337 */ 338 int waitq_sleep_timeout_unsafe(waitq_t *wq, uint32_t usec, int flags) 339 { 340 /* checks whether to go to sleep at all */ 342 * @param wq See waitq_sleep_timeout(). 343 * @param usec See waitq_sleep_timeout(). 344 * @param flags See waitq_sleep_timeout(). 345 * 346 * @return See waitq_sleep_timeout(). 347 * 348 */ 349 int waitq_sleep_timeout_unsafe(waitq_t *wq, uint32_t usec, unsigned int flags) 350 { 351 /* Checks whether to go to sleep at all */ 341 352 if (wq->missed_wakeups) { 342 353 wq->missed_wakeups--; 343 354 return ESYNCH_OK_ATOMIC; 344 } 345 else { 346 if ((flags & SYNCH_FLAGS_NON_BLOCKING) && (usec == 0)) { 347 /* return immediatelly instead of going to sleep */ 355 } else { 356 if (PARAM_NON_BLOCKING(flags, usec)) { 357 /* Return immediatelly instead of going to sleep */ 348 358 return ESYNCH_WOULD_BLOCK; 349 359 } … … 352 362 /* 353 363 * Now we are firmly decided to go to sleep. 364 * 354 365 */ 355 spinlock_lock(&THREAD->lock);356 366 irq_spinlock_lock(&THREAD->lock, false); 367 357 368 if (flags & SYNCH_FLAGS_INTERRUPTIBLE) { 358 359 369 /* 360 370 * If the thread was already interrupted, 361 371 * don't go to sleep at all. 372 * 362 373 */ 363 374 if (THREAD->interrupted) { 364 spinlock_unlock(&THREAD->lock);365 spinlock_unlock(&wq->lock);375 irq_spinlock_unlock(&THREAD->lock, false); 376 irq_spinlock_unlock(&wq->lock, false); 366 377 return ESYNCH_INTERRUPTED; 367 378 } 368 379 369 380 /* 370 381 * Set context that will be restored if the sleep 371 382 * of this thread is ever interrupted. 383 * 372 384 */ 373 385 THREAD->sleep_interruptible = true; … … 375 387 /* Short emulation of scheduler() return code. */ 376 388 THREAD->last_cycle = get_cycle(); 377 spinlock_unlock(&THREAD->lock);389 irq_spinlock_unlock(&THREAD->lock, false); 378 390 return ESYNCH_INTERRUPTED; 379 391 } 380 381 } else { 392 } else 382 393 THREAD->sleep_interruptible = false; 383 } 384 394 385 395 if (usec) { 386 396 /* We use the timeout variant. */ … … 388 398 /* Short emulation of scheduler() return code. */ 389 399 THREAD->last_cycle = get_cycle(); 390 spinlock_unlock(&THREAD->lock);400 irq_spinlock_unlock(&THREAD->lock, false); 391 401 return ESYNCH_TIMEOUT; 392 402 } 403 393 404 THREAD->timeout_pending = true; 394 405 timeout_register(&THREAD->sleep_timeout, (uint64_t) usec, 395 406 waitq_sleep_timed_out, THREAD); 396 407 } 397 408 398 409 list_append(&THREAD->wq_link, &wq->head); 399 410 400 411 /* 401 412 * Suspend execution. 413 * 402 414 */ 403 415 THREAD->state = Sleeping; 404 416 THREAD->sleep_queue = wq; 405 406 spinlock_unlock(&THREAD->lock);407 417 418 irq_spinlock_unlock(&THREAD->lock, false); 419 408 420 /* wq->lock is released in scheduler_separated_stack() */ 409 scheduler(); 421 scheduler(); 410 422 411 423 return ESYNCH_OK_BLOCKED; 412 424 } 413 414 425 415 426 /** Wake up first thread sleeping in a wait queue … … 421 432 * timeout. 422 433 * 423 * @param wq Pointer to wait queue. 424 * @param mode Wakeup mode. 434 * @param wq Pointer to wait queue. 435 * @param mode Wakeup mode. 436 * 425 437 */ 426 438 void waitq_wakeup(waitq_t *wq, wakeup_mode_t mode) 427 439 { 428 ipl_t ipl; 429 430 ipl = interrupts_disable(); 431 spinlock_lock(&wq->lock); 432 440 irq_spinlock_lock(&wq->lock, true); 433 441 _waitq_wakeup_unsafe(wq, mode); 434 435 spinlock_unlock(&wq->lock); 436 interrupts_restore(ipl); 442 irq_spinlock_unlock(&wq->lock, true); 437 443 } 438 444 … … 442 448 * assumes wq->lock is already locked and interrupts are already disabled. 443 449 * 444 * @param wq Pointer to wait queue. 445 * @param mode If mode is WAKEUP_FIRST, then the longest waiting 446 * thread, if any, is woken up. If mode is WAKEUP_ALL, then 447 * all waiting threads, if any, are woken up. If there are 448 * no waiting threads to be woken up, the missed wakeup is 449 * recorded in the wait queue. 450 * @param wq Pointer to wait queue. 451 * @param mode If mode is WAKEUP_FIRST, then the longest waiting 452 * thread, if any, is woken up. If mode is WAKEUP_ALL, then 453 * all waiting threads, if any, are woken up. If there are 454 * no waiting threads to be woken up, the missed wakeup is 455 * recorded in the wait queue. 456 * 450 457 */ 451 458 void _waitq_wakeup_unsafe(waitq_t *wq, wakeup_mode_t mode) 452 459 { 453 thread_t *t;454 460 size_t count = 0; 455 461 456 loop: 462 ASSERT(interrupts_disabled()); 463 ASSERT(irq_spinlock_locked(&wq->lock)); 464 465 loop: 457 466 if (list_empty(&wq->head)) { 458 467 wq->missed_wakeups++; 459 if ( count && mode == WAKEUP_ALL)468 if ((count) && (mode == WAKEUP_ALL)) 460 469 wq->missed_wakeups--; 470 461 471 return; 462 472 } 463 473 464 474 count++; 465 t = list_get_instance(wq->head.next, thread_t, wq_link);475 thread_t *thread = list_get_instance(wq->head.next, thread_t, wq_link); 466 476 467 477 /* … … 475 485 * invariant must hold: 476 486 * 477 * t ->sleep_queue != NULL <=> tsleeps in a wait queue487 * thread->sleep_queue != NULL <=> thread sleeps in a wait queue 478 488 * 479 489 * For an observer who locks the thread, the invariant 480 490 * holds only when the lock is held prior to removing 481 491 * it from the wait queue. 492 * 482 493 */ 483 spinlock_lock(&t->lock); 484 list_remove(&t->wq_link); 485 486 if (t->timeout_pending && timeout_unregister(&t->sleep_timeout)) 487 t->timeout_pending = false; 488 t->sleep_queue = NULL; 489 spinlock_unlock(&t->lock); 490 491 thread_ready(t); 492 494 irq_spinlock_lock(&thread->lock, false); 495 list_remove(&thread->wq_link); 496 497 if ((thread->timeout_pending) && 498 (timeout_unregister(&thread->sleep_timeout))) 499 thread->timeout_pending = false; 500 501 thread->sleep_queue = NULL; 502 irq_spinlock_unlock(&thread->lock, false); 503 504 thread_ready(thread); 505 493 506 if (mode == WAKEUP_ALL) 494 507 goto loop; 495 508 } 496 509 510 /** Get the missed wakeups count. 511 * 512 * @param wq Pointer to wait queue. 513 * @return The wait queue's missed_wakeups count. 514 */ 515 int waitq_count_get(waitq_t *wq) 516 { 517 int cnt; 518 519 irq_spinlock_lock(&wq->lock, true); 520 cnt = wq->missed_wakeups; 521 irq_spinlock_unlock(&wq->lock, true); 522 523 return cnt; 524 } 525 526 /** Set the missed wakeups count. 527 * 528 * @param wq Pointer to wait queue. 529 * @param val New value of the missed_wakeups count. 530 */ 531 void waitq_count_set(waitq_t *wq, int val) 532 { 533 irq_spinlock_lock(&wq->lock, true); 534 wq->missed_wakeups = val; 535 irq_spinlock_unlock(&wq->lock, true); 536 } 537 497 538 /** @} 498 539 */
Note:
See TracChangeset
for help on using the changeset viewer.