1 // Copyright 2016 The Fuchsia Authors
2 // Copyright (c) 2008-2014 Travis Geiselbrecht
3 //
4 // Use of this source code is governed by a MIT-style
5 // license that can be found in the LICENSE file or at
6 // https://opensource.org/licenses/MIT
7 
8 #pragma once
9 
10 #include <arch/defines.h>
11 #include <arch/ops.h>
12 #include <arch/thread.h>
13 #include <kernel/spinlock.h>
14 #include <kernel/timer.h>
15 #include <kernel/timer_slack.h>
16 #include <list.h>
17 #include <sys/types.h>
18 #include <zircon/compiler.h>
19 #include <zircon/types.h>
20 
21 __BEGIN_CDECLS
22 
23 extern spin_lock_t thread_lock;
24 
25 // wait queue stuff
26 #define WAIT_QUEUE_MAGIC (0x77616974) // 'wait'
27 
28 typedef struct wait_queue {
29     int magic;
30     int count;
31     struct list_node heads;
32 } wait_queue_t;
33 
34 #define WAIT_QUEUE_INITIAL_VALUE(q)             \
35     {                                           \
36         .magic = WAIT_QUEUE_MAGIC,              \
37         .count = 0,                             \
38         .heads = LIST_INITIAL_VALUE((q).heads), \
39     }
40 
41 // wait queue primitive
42 // NOTE: must be inside critical section when using these
43 void wait_queue_init(wait_queue_t* wait);
44 
45 void wait_queue_destroy(wait_queue_t*);
46 
47 // block on a wait queue.
48 // return status is whatever the caller of wait_queue_wake_*() specifies.
49 // a deadline other than ZX_TIME_INFINITE will abort at the specified time
50 // and return ZX_ERR_TIMED_OUT. a deadline in the past will immediately return.
51 
52 zx_status_t wait_queue_block(wait_queue_t*, zx_time_t deadline) TA_REQ(thread_lock);
53 
54 // block on a wait queue, ignoring existing signals in |signal_mask|.
55 // return status is whatever the caller of wait_queue_wake_*() specifies or
56 // ZX_ERR_TIMED_OUT if the slack-adjusted deadline has elapsed or is in the past.
57 // will never timeout when called with a deadline of ZX_TIME_INFINITE.
58 zx_status_t wait_queue_block_etc(wait_queue_t*,
59                                  zx_time_t deadline,
60                                  TimerSlack slack,
61                                  uint signal_mask) TA_REQ(thread_lock);
62 
63 // returns the highest priority of all the blocked threads on this wait queue.
64 // returns -1 if no threads are blocked.
65 
66 int wait_queue_blocked_priority(wait_queue_t*) TA_REQ(thread_lock);
67 
68 // returns the current highest priority blocked thread on this wait queue, or
69 // null if no threads are blocked.
70 struct thread* wait_queue_peek(wait_queue_t*) TA_REQ(thread_lock);
71 
72 // release one or more threads from the wait queue.
73 // reschedule = should the system reschedule if any is released.
74 // wait_queue_error = what wait_queue_block() should return for the blocking thread.
75 
76 int wait_queue_wake_one(wait_queue_t*, bool reschedule,
77                         zx_status_t wait_queue_error) TA_REQ(thread_lock);
78 int wait_queue_wake_all(wait_queue_t*, bool reschedule,
79                         zx_status_t wait_queue_error) TA_REQ(thread_lock);
80 struct thread* wait_queue_dequeue_one(wait_queue_t* wait,
81                                       zx_status_t wait_queue_error) TA_REQ(thread_lock);
82 
83 // is the wait queue currently empty
84 bool wait_queue_is_empty(wait_queue_t*) TA_REQ(thread_lock);
85 
86 // remove a specific thread out of a wait queue it's blocked on
87 zx_status_t wait_queue_unblock_thread(struct thread* t,
88                                       zx_status_t wait_queue_error) TA_REQ(thread_lock);
89 
90 // a thread's priority has changed, potentially modify the wait queue it's in
91 void wait_queue_priority_changed(struct thread* t,
92                                  int old_prio) TA_REQ(thread_lock);
93 
94 // validate that the queue of a given wait queue is valid
95 void wait_queue_validate_queue(wait_queue_t* wait) TA_REQ(thread_lock);
96 
97 __END_CDECLS
98 
99 #ifdef __cplusplus
100 
101 class WaitQueue {
102 public:
WaitQueue()103     WaitQueue() {}
~WaitQueue()104     ~WaitQueue() { wait_queue_destroy(&wq_); }
105 
106     WaitQueue(WaitQueue&) = delete;
107     WaitQueue(WaitQueue&&) = delete;
108     WaitQueue& operator=(WaitQueue&) = delete;
109     WaitQueue& operator=(WaitQueue&&) = delete;
110 
Block(zx_time_t deadline,TimerSlack slack)111     zx_status_t Block(zx_time_t deadline, TimerSlack slack) TA_REQ(thread_lock) {
112         return wait_queue_block_etc(&wq_, deadline, slack, 0);
113     }
114 
Peek()115     struct thread* Peek() TA_REQ(thread_lock) {
116         return wait_queue_peek(&wq_);
117     }
118 
WakeOne(bool reschedule,zx_status_t wait_queue_error)119     int WakeOne(bool reschedule, zx_status_t wait_queue_error) TA_REQ(thread_lock) {
120         return wait_queue_wake_one(&wq_, reschedule, wait_queue_error);
121     }
122 
IsEmpty()123     bool IsEmpty() TA_REQ(thread_lock) { return wait_queue_is_empty(&wq_); }
124 
UnblockThread(struct thread * t,zx_status_t wait_queue_error)125     static zx_status_t UnblockThread(struct thread* t, zx_status_t wait_queue_error)
126         TA_REQ(thread_lock) {
127         return wait_queue_unblock_thread(t, wait_queue_error);
128     }
129 
130 private:
131     wait_queue_t wq_ = WAIT_QUEUE_INITIAL_VALUE(wq_);
132 };
133 
134 #endif // __cplusplus
135