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