* Switches to another thread if we are still asleep on a sleep queue. * Returns with thread lock. */
| 543 | * Returns with thread lock. |
| 544 | */ |
| 545 | static void |
| 546 | sleepq_switch(const void *wchan, int pri) |
| 547 | { |
| 548 | struct sleepqueue_chain *sc; |
| 549 | struct sleepqueue *sq; |
| 550 | struct thread *td; |
| 551 | bool rtc_changed; |
| 552 | |
| 553 | td = curthread; |
| 554 | sc = SC_LOOKUP(wchan); |
| 555 | mtx_assert(&sc->sc_lock, MA_OWNED); |
| 556 | THREAD_LOCK_ASSERT(td, MA_OWNED); |
| 557 | |
| 558 | /* |
| 559 | * If we have a sleep queue, then we've already been woken up, so |
| 560 | * just return. |
| 561 | */ |
| 562 | if (td->td_sleepqueue != NULL) { |
| 563 | mtx_unlock_spin(&sc->sc_lock); |
| 564 | thread_unlock(td); |
| 565 | return; |
| 566 | } |
| 567 | |
| 568 | /* |
| 569 | * If TDF_TIMEOUT is set, then our sleep has been timed out |
| 570 | * already but we are still on the sleep queue, so dequeue the |
| 571 | * thread and return. |
| 572 | * |
| 573 | * Do the same if the real-time clock has been adjusted since this |
| 574 | * thread calculated its timeout based on that clock. This handles |
| 575 | * the following race: |
| 576 | * - The Ts thread needs to sleep until an absolute real-clock time. |
| 577 | * It copies the global rtc_generation into curthread->td_rtcgen, |
| 578 | * reads the RTC, and calculates a sleep duration based on that time. |
| 579 | * See umtxq_sleep() for an example. |
| 580 | * - The Tc thread adjusts the RTC, bumps rtc_generation, and wakes |
| 581 | * threads that are sleeping until an absolute real-clock time. |
| 582 | * See tc_setclock() and the POSIX specification of clock_settime(). |
| 583 | * - Ts reaches the code below. It holds the sleepqueue chain lock, |
| 584 | * so Tc has finished waking, so this thread must test td_rtcgen. |
| 585 | * (The declaration of td_rtcgen refers to this comment.) |
| 586 | */ |
| 587 | rtc_changed = td->td_rtcgen != 0 && td->td_rtcgen != rtc_generation; |
| 588 | if ((td->td_flags & TDF_TIMEOUT) || rtc_changed) { |
| 589 | if (rtc_changed) { |
| 590 | td->td_rtcgen = 0; |
| 591 | } |
| 592 | MPASS(TD_ON_SLEEPQ(td)); |
| 593 | sq = sleepq_lookup(wchan); |
| 594 | sleepq_remove_thread(sq, td); |
| 595 | mtx_unlock_spin(&sc->sc_lock); |
| 596 | thread_unlock(td); |
| 597 | return; |
| 598 | } |
| 599 | #ifdef SLEEPQUEUE_PROFILING |
| 600 | if (prof_enabled) |
| 601 | sleepq_profile(td->td_wmesg); |
| 602 | #endif |
no test coverage detected