Changes in kernel/generic/src/synch/waitq.c [6c4a56f:1d432f9] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
kernel/generic/src/synch/waitq.c
r6c4a56f 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 … … 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 … … 124 130 * If the thread is not found sleeping, no action is taken. 125 131 * 126 * @param t Thread to be interrupted. 127 */ 128 void waitq_interrupt_sleep(thread_t *t) 129 { 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 130 147 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)) { 148 if ((wq = thread->sleep_queue)) { /* Assignment */ 149 if (!(thread->sleep_interruptible)) { 144 150 /* 145 151 * The sleep cannot be interrupted. 152 * 146 153 */ 147 spinlock_unlock(&t->lock);154 irq_spinlock_unlock(&thread->lock, false); 148 155 goto out; 149 156 } 150 151 if (! spinlock_trylock(&wq->lock)) {152 spinlock_unlock(&t->lock);157 158 if (!irq_spinlock_trylock(&wq->lock)) { 159 irq_spinlock_unlock(&thread->lock, false); 153 160 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; 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; 162 171 do_wakeup = true; 163 t ->sleep_queue = NULL;164 spinlock_unlock(&wq->lock);165 } 166 spinlock_unlock(&t->lock);167 172 thread->sleep_queue = NULL; 173 irq_spinlock_unlock(&wq->lock, false); 174 } 175 irq_spinlock_unlock(&thread->lock, false); 176 168 177 if (do_wakeup) 169 thread_ready(t );170 178 thread_ready(thread); 179 171 180 out: 172 spinlock_unlock(&threads_lock); 173 interrupts_restore(ipl); 181 irq_spinlock_unlock(&threads_lock, true); 174 182 } 175 183 … … 179 187 * is sleeping interruptibly. 180 188 * 181 * @param wq Pointer to wait queue. 189 * @param wq Pointer to wait queue. 190 * 182 191 */ 183 192 void waitq_unsleep(waitq_t *wq) 184 193 { 185 ipl_t ipl; 186 187 ipl = interrupts_disable(); 188 spinlock_lock(&wq->lock); 189 194 irq_spinlock_lock(&wq->lock, true); 195 190 196 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 } 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)) 208 220 209 221 /** Sleep until either wakeup, timeout or interruption occurs … … 217 229 * and all the *_timeout() functions use it. 218 230 * 219 * @param wq 220 * @param usec 221 * @param flags 231 * @param wq Pointer to wait queue. 232 * @param usec Timeout in microseconds. 233 * @param flags Specify mode of the sleep. 222 234 * 223 235 * The sleep can be interrupted only if the 224 236 * SYNCH_FLAGS_INTERRUPTIBLE bit is specified in flags. 225 * 237 * 226 238 * If usec is greater than zero, regardless of the value of the 227 239 * SYNCH_FLAGS_NON_BLOCKING bit in flags, the call will not return until either 228 * timeout, interruption or wakeup comes. 240 * timeout, interruption or wakeup comes. 229 241 * 230 242 * If usec is zero and the SYNCH_FLAGS_NON_BLOCKING bit is not set in flags, … … 234 246 * call will immediately return, reporting either success or failure. 235 247 * 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); 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); 261 266 waitq_sleep_finish(wq, rc, ipl); 262 267 return rc; … … 268 273 * and interrupts disabled. 269 274 * 270 * @param wq Wait queue. 271 * 272 * @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 * 273 279 */ 274 280 ipl_t waitq_sleep_prepare(waitq_t *wq) … … 278 284 restart: 279 285 ipl = interrupts_disable(); 280 281 if (THREAD) { /* needed during system initiailzation */286 287 if (THREAD) { /* Needed during system initiailzation */ 282 288 /* 283 289 * Busy waiting for a delayed timeout. … … 286 292 * Simply, the thread is not allowed to go to sleep if 287 293 * there are timeouts in progress. 294 * 288 295 */ 289 spinlock_lock(&THREAD->lock); 296 irq_spinlock_lock(&THREAD->lock, false); 297 290 298 if (THREAD->timeout_pending) { 291 spinlock_unlock(&THREAD->lock);299 irq_spinlock_unlock(&THREAD->lock, false); 292 300 interrupts_restore(ipl); 293 301 goto restart; 294 302 } 295 spinlock_unlock(&THREAD->lock); 296 } 297 298 spinlock_lock(&wq->lock); 303 304 irq_spinlock_unlock(&THREAD->lock, false); 305 } 306 307 irq_spinlock_lock(&wq->lock, false); 299 308 return ipl; 300 309 } … … 306 315 * lock is released. 307 316 * 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(). 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 * 311 321 */ 312 322 void waitq_sleep_finish(waitq_t *wq, int rc, ipl_t ipl) … … 315 325 case ESYNCH_WOULD_BLOCK: 316 326 case ESYNCH_OK_ATOMIC: 317 spinlock_unlock(&wq->lock);327 irq_spinlock_unlock(&wq->lock, false); 318 328 break; 319 329 default: 320 330 break; 321 331 } 332 322 333 interrupts_restore(ipl); 323 334 } … … 329 340 * and followed by a call to waitq_sleep_finish(). 330 341 * 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 */ 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 */ 340 352 if (wq->missed_wakeups) { 341 353 wq->missed_wakeups--; 342 354 return ESYNCH_OK_ATOMIC; 343 } 344 else { 345 if ((flags & SYNCH_FLAGS_NON_BLOCKING) && (usec == 0)) { 346 /* return immediatelly instead of going to sleep */ 355 } else { 356 if (PARAM_NON_BLOCKING(flags, usec)) { 357 /* Return immediatelly instead of going to sleep */ 347 358 return ESYNCH_WOULD_BLOCK; 348 359 } … … 351 362 /* 352 363 * Now we are firmly decided to go to sleep. 364 * 353 365 */ 354 spinlock_lock(&THREAD->lock);355 366 irq_spinlock_lock(&THREAD->lock, false); 367 356 368 if (flags & SYNCH_FLAGS_INTERRUPTIBLE) { 357 358 369 /* 359 370 * If the thread was already interrupted, 360 371 * don't go to sleep at all. 372 * 361 373 */ 362 374 if (THREAD->interrupted) { 363 spinlock_unlock(&THREAD->lock);364 spinlock_unlock(&wq->lock);375 irq_spinlock_unlock(&THREAD->lock, false); 376 irq_spinlock_unlock(&wq->lock, false); 365 377 return ESYNCH_INTERRUPTED; 366 378 } 367 379 368 380 /* 369 381 * Set context that will be restored if the sleep 370 382 * of this thread is ever interrupted. 383 * 371 384 */ 372 385 THREAD->sleep_interruptible = true; 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.