Changes in kernel/generic/src/synch/waitq.c [df58e44:6c4a56f] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
kernel/generic/src/synch/waitq.c
rdf58e44 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 96 spinlock_lock(&t->lock); 97 if ((wq = t->sleep_queue)) { /* assignment */ 98 if (!spinlock_trylock(&wq->lock)) { 99 spinlock_unlock(&t->lock); 100 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 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 114 if (do_wakeup) 115 thread_ready(t); 116 117 out: 118 spinlock_unlock(&threads_lock); 119 } 120 121 /** Interrupt sleeping thread. 122 * 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 { 101 130 waitq_t *wq; 102 if ((wq = thread->sleep_queue)) { /* Assignment */103 if (!irq_spinlock_trylock(&wq->lock)) {104 irq_spinlock_unlock(&thread->lock, false);105 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;112 do_wakeup = true;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);119 120 if (do_wakeup)121 thread_ready(thread);122 123 out:124 irq_spinlock_unlock(&threads_lock, false);125 }126 127 /** Interrupt sleeping thread.128 *129 * This routine attempts to interrupt a thread from its sleep in130 * a waitqueue. If the thread is not found sleeping, no action131 * is taken.132 *133 * The threads_lock must be already held and interrupts must be134 * 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 131 bool do_wakeup = false; 132 ipl_t ipl; 142 133 DEADLOCK_PROBE_INIT(p_wqlock); 143 144 /*145 * The thread is quaranteed to exist because146 * threads_lock is held.147 */148 134 135 ipl = interrupts_disable(); 136 spinlock_lock(&threads_lock); 137 if (!thread_exists(t)) 138 goto out; 139 149 140 grab_locks: 150 irq_spinlock_lock(&thread->lock, false); 151 152 waitq_t *wq; 153 if ((wq = thread->sleep_queue)) { /* Assignment */ 154 if (!(thread->sleep_interruptible)) { 141 spinlock_lock(&t->lock); 142 if ((wq = t->sleep_queue)) { /* assignment */ 143 if (!(t->sleep_interruptible)) { 155 144 /* 156 145 * The sleep cannot be interrupted. 157 146 */ 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); 147 spinlock_unlock(&t->lock); 148 goto out; 149 } 150 151 if (!spinlock_trylock(&wq->lock)) { 152 spinlock_unlock(&t->lock); 165 153 DEADLOCK_PROBE(p_wqlock, DEADLOCK_THRESHOLD); 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; 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; 175 162 do_wakeup = true; 176 thread->sleep_queue = NULL; 177 irq_spinlock_unlock(&wq->lock, false); 178 } 179 180 irq_spinlock_unlock(&thread->lock, false); 181 163 t->sleep_queue = NULL; 164 spinlock_unlock(&wq->lock); 165 } 166 spinlock_unlock(&t->lock); 167 182 168 if (do_wakeup) 183 thread_ready(thread); 169 thread_ready(t); 170 171 out: 172 spinlock_unlock(&threads_lock); 173 interrupts_restore(ipl); 184 174 } 185 175 … … 189 179 * is sleeping interruptibly. 190 180 * 191 * @param wq Pointer to wait queue. 192 * 181 * @param wq Pointer to wait queue. 193 182 */ 194 183 void waitq_unsleep(waitq_t *wq) 195 184 { 196 irq_spinlock_lock(&wq->lock, true); 197 185 ipl_t ipl; 186 187 ipl = interrupts_disable(); 188 spinlock_lock(&wq->lock); 189 198 190 if (!list_empty(&wq->head)) { 199 thread_t *t hread = list_get_instance(wq->head.next, thread_t, wq_link);191 thread_t *t; 200 192 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)) 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 } 222 208 223 209 /** Sleep until either wakeup, timeout or interruption occurs … … 231 217 * and all the *_timeout() functions use it. 232 218 * 233 * @param wq 234 * @param usec 235 * @param flags 219 * @param wq Pointer to wait queue. 220 * @param usec Timeout in microseconds. 221 * @param flags Specify mode of the sleep. 236 222 * 237 223 * The sleep can be interrupted only if the 238 224 * SYNCH_FLAGS_INTERRUPTIBLE bit is specified in flags. 239 * 225 * 240 226 * If usec is greater than zero, regardless of the value of the 241 227 * SYNCH_FLAGS_NON_BLOCKING bit in flags, the call will not return until either 242 * timeout, interruption or wakeup comes. 228 * timeout, interruption or wakeup comes. 243 229 * 244 230 * If usec is zero and the SYNCH_FLAGS_NON_BLOCKING bit is not set in flags, … … 248 234 * call will immediately return, reporting either success or failure. 249 235 * 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); 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); 268 261 waitq_sleep_finish(wq, rc, ipl); 269 262 return rc; … … 275 268 * and interrupts disabled. 276 269 * 277 * @param wq Wait queue. 278 * 279 * @return Interrupt level as it existed on entry to this function. 280 * 270 * @param wq Wait queue. 271 * 272 * @return Interrupt level as it existed on entry to this function. 281 273 */ 282 274 ipl_t waitq_sleep_prepare(waitq_t *wq) … … 286 278 restart: 287 279 ipl = interrupts_disable(); 288 289 if (THREAD) { /* Needed during system initiailzation */280 281 if (THREAD) { /* needed during system initiailzation */ 290 282 /* 291 283 * Busy waiting for a delayed timeout. … … 294 286 * Simply, the thread is not allowed to go to sleep if 295 287 * there are timeouts in progress. 296 *297 288 */ 298 irq_spinlock_lock(&THREAD->lock, false); 299 289 spinlock_lock(&THREAD->lock); 300 290 if (THREAD->timeout_pending) { 301 irq_spinlock_unlock(&THREAD->lock, false);291 spinlock_unlock(&THREAD->lock); 302 292 interrupts_restore(ipl); 303 293 goto restart; 304 294 } 305 306 irq_spinlock_unlock(&THREAD->lock, false); 307 } 308 309 irq_spinlock_lock(&wq->lock, false); 295 spinlock_unlock(&THREAD->lock); 296 } 297 298 spinlock_lock(&wq->lock); 310 299 return ipl; 311 300 } … … 317 306 * lock is released. 318 307 * 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 * 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(). 323 311 */ 324 312 void waitq_sleep_finish(waitq_t *wq, int rc, ipl_t ipl) … … 327 315 case ESYNCH_WOULD_BLOCK: 328 316 case ESYNCH_OK_ATOMIC: 329 irq_spinlock_unlock(&wq->lock, false);317 spinlock_unlock(&wq->lock); 330 318 break; 331 319 default: 332 320 break; 333 321 } 334 335 322 interrupts_restore(ipl); 336 323 } … … 342 329 * and followed by a call to waitq_sleep_finish(). 343 330 * 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 */ 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 */ 354 340 if (wq->missed_wakeups) { 355 341 wq->missed_wakeups--; 356 342 return ESYNCH_OK_ATOMIC; 357 } else { 358 if (PARAM_NON_BLOCKING(flags, usec)) { 359 /* 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 */ 360 347 return ESYNCH_WOULD_BLOCK; 361 348 } … … 364 351 /* 365 352 * Now we are firmly decided to go to sleep. 366 *367 353 */ 368 irq_spinlock_lock(&THREAD->lock, false);369 354 spinlock_lock(&THREAD->lock); 355 370 356 if (flags & SYNCH_FLAGS_INTERRUPTIBLE) { 357 371 358 /* 372 359 * If the thread was already interrupted, … … 374 361 */ 375 362 if (THREAD->interrupted) { 376 irq_spinlock_unlock(&THREAD->lock, false);377 irq_spinlock_unlock(&wq->lock, false);363 spinlock_unlock(&THREAD->lock); 364 spinlock_unlock(&wq->lock); 378 365 return ESYNCH_INTERRUPTED; 379 366 } 380 367 381 368 /* 382 369 * Set context that will be restored if the sleep … … 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.