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