| // Copyright 2019 The Fuchsia Authors |
| // |
| // Use of this source code is governed by a MIT-style |
| // license that can be found in the LICENSE file or at |
| // https://opensource.org/licenses/MIT |
| |
| #ifndef ZIRCON_KERNEL_INCLUDE_KERNEL_WAIT_QUEUE_INTERNAL_H_ |
| #define ZIRCON_KERNEL_INCLUDE_KERNEL_WAIT_QUEUE_INTERNAL_H_ |
| |
| #include <lib/ktrace.h> |
| #include <platform.h> |
| #include <zircon/errors.h> |
| |
| #include <kernel/sched.h> |
| #include <kernel/thread.h> |
| #include <kernel/wait.h> |
| |
| namespace internal { |
| |
| void wait_queue_insert(WaitQueue* wait, Thread* t) TA_REQ(thread_lock); |
| void wait_queue_remove_head(Thread* t) TA_REQ(thread_lock); |
| void wait_queue_remove_thread(Thread* t) TA_REQ(thread_lock); |
| void wait_queue_timeout_handler(Timer* timer, zx_time_t now, void* arg); |
| |
| // Used by WaitQueue and OwnedWaitQueue to manage changes to the maximum |
| // priority of a wait queue due to external effects (thread priority change, |
| // thread timeout, thread killed). Do not call this function from an external |
| // site. |
| bool wait_queue_waiters_priority_changed(WaitQueue* wq, int old_prio) TA_REQ(thread_lock); |
| |
| // Remove a thread from a wait queue, maintain the wait queue's internal count, |
| // and update the wait_queue specific bookkeeping in the thread in the process. |
| inline void wait_queue_dequeue_thread_internal(WaitQueue* wait, Thread* t, |
| zx_status_t wait_queue_error) TA_REQ(thread_lock) { |
| DEBUG_ASSERT(t != nullptr); |
| DEBUG_ASSERT(list_in_list(&t->wait_queue_state_.queue_node_)); |
| DEBUG_ASSERT(t->state_ == THREAD_BLOCKED || t->state_ == THREAD_BLOCKED_READ_LOCK); |
| DEBUG_ASSERT(t->blocking_wait_queue_ == wait); |
| |
| wait_queue_remove_thread(t); |
| wait->collection_.count_--; |
| t->blocked_status_ = wait_queue_error; |
| t->blocking_wait_queue_ = NULL; |
| } |
| |
| // Notes for wait_queue_block_etc_(pre|post). |
| // |
| // Currently, there are two variants of wait_queues in Zircon. The standard |
| // WaitQueue (used for most tasks) and the specialized |
| // OwnedWaitQueues (used for mutexes/futexes/brwlocks, and anything else which |
| // needs to have a concept of priority inheritance). |
| // |
| // The "Block" operation for these two versions are _almost_ identical. The |
| // only real difference between the two is that the OWQ implementation needs to |
| // stop after we have decided that we are actually going to block the thread, |
| // but before the timeout timer is armed and the thread is actually blocked, in |
| // order to update it's PI chain bookkeeping. |
| // |
| // Instead of duplicating the code, or exposing a code-injection mechanism into |
| // the public API, we split the code into two inline functions that we hide in |
| // internal:: instead. The first (pre) performs all of the checks and bookkeeping |
| // up-to the point of arming the timer and blocking, the second (post) finishes |
| // the job. |
| // |
| // The traditional WaitQueue implementation of |
| // wait_queue_block_etc just calls these two functions back to back, relying on |
| // the inlining to generate the original function. The OwnedWaitQueue |
| // implementation does the same, but injects its bookkeeping at the appropriate |
| // point. |
| // |
| // Nothing but these two specific pieces of code should *ever* need to call |
| // these functions. Users should *always* be using either |
| // wait_queue_block_etc/wait_queue_block (or the WaitQueue wrappers of the |
| // same), or OwnedWaitQueue::BlockAndAssignOwner instead. |
| // |
| inline zx_status_t wait_queue_block_etc_pre(WaitQueue* wait, const Deadline& deadline, |
| uint signal_mask, ResourceOwnership reason) |
| TA_REQ(thread_lock) { |
| Thread* current_thread = Thread::Current::Get(); |
| |
| if (deadline.when() != ZX_TIME_INFINITE && deadline.when() <= current_time()) { |
| return ZX_ERR_TIMED_OUT; |
| } |
| |
| if (current_thread->interruptable_ && (unlikely(current_thread->signals_ & ~signal_mask))) { |
| if (current_thread->signals_ & THREAD_SIGNAL_KILL) { |
| return ZX_ERR_INTERNAL_INTR_KILLED; |
| } else if (current_thread->signals_ & THREAD_SIGNAL_SUSPEND) { |
| return ZX_ERR_INTERNAL_INTR_RETRY; |
| } |
| } |
| |
| wait_queue_insert(wait, current_thread); |
| wait->collection_.count_++; |
| current_thread->state_ = |
| (reason == ResourceOwnership::Normal) ? THREAD_BLOCKED : THREAD_BLOCKED_READ_LOCK; |
| current_thread->blocking_wait_queue_ = wait; |
| current_thread->blocked_status_ = ZX_OK; |
| |
| return ZX_OK; |
| } |
| |
| inline zx_status_t wait_queue_block_etc_post(WaitQueue* wait, const Deadline& deadline) |
| TA_REQ(thread_lock) { |
| Thread* current_thread = Thread::Current::Get(); |
| Timer timer; |
| |
| // if the deadline is nonzero or noninfinite, set a callback to yank us out of the queue |
| if (deadline.when() != ZX_TIME_INFINITE) { |
| timer.Set(deadline, wait_queue_timeout_handler, (void*)current_thread); |
| } |
| |
| ktrace_ptr(TAG_KWAIT_BLOCK, wait, 0, 0); |
| |
| sched_block(); |
| |
| ktrace_ptr(TAG_KWAIT_UNBLOCK, wait, current_thread->blocked_status_, 0); |
| |
| // we don't really know if the timer fired or not, so it's better safe to try to cancel it |
| if (deadline.when() != ZX_TIME_INFINITE) { |
| timer.Cancel(); |
| } |
| |
| return current_thread->blocked_status_; |
| } |
| |
| } // namespace internal |
| |
| #endif // ZIRCON_KERNEL_INCLUDE_KERNEL_WAIT_QUEUE_INTERNAL_H_ |