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 /**
9  * @file
10  * @brief  Event wait and signal functions for threads.
11  * @defgroup event Events
12  *
13  * An event is a subclass of a wait queue.
14  *
15  * Threads wait for events, with optional timeouts.
16  *
17  * Events are "signaled", releasing waiting threads to continue.
18  * Signals may be one-shot signals (EVENT_FLAG_AUTOUNSIGNAL), in which
19  * case one signal releases only one thread, at which point it is
20  * automatically cleared. Otherwise, signals release all waiting threads
21  * to continue immediately until the signal is manually cleared with
22  * event_unsignal().
23  *
24  * @{
25  */
26 
27 #include <assert.h>
28 #include <debug.h>
29 #include <err.h>
30 #include <kernel/event.h>
31 #include <kernel/spinlock.h>
32 #include <kernel/thread.h>
33 #include <kernel/thread_lock.h>
34 #include <sys/types.h>
35 #include <zircon/types.h>
36 
37 /**
38  * @brief  Initialize an event object
39  *
40  * @param e        Event object to initialize
41  * @param initial  Initial value for "signaled" state
42  * @param flags    0 or EVENT_FLAG_AUTOUNSIGNAL
43  */
event_init(event_t * e,bool initial,uint flags)44 void event_init(event_t* e, bool initial, uint flags) {
45     *e = (event_t)EVENT_INITIAL_VALUE(*e, initial, flags);
46 }
47 
48 /**
49  * @brief  Destroy an event object.
50  *
51  * Event's resources are freed and it may no longer be
52  * used until event_init() is called again.
53  * Will panic if there are any threads still waiting.
54  *
55  * @param e        Event object to initialize
56  */
event_destroy(event_t * e)57 void event_destroy(event_t* e) {
58     DEBUG_ASSERT(e->magic == EVENT_MAGIC);
59 
60     e->magic = 0;
61     e->signaled = false;
62     e->flags = 0;
63     wait_queue_destroy(&e->wait);
64 }
65 
event_wait_worker(event_t * e,zx_time_t deadline,TimerSlack slack,bool interruptable,uint signal_mask)66 static zx_status_t event_wait_worker(event_t* e,
67                                      zx_time_t deadline,
68                                      TimerSlack slack,
69                                      bool interruptable,
70                                      uint signal_mask) {
71     thread_t* current_thread = get_current_thread();
72     zx_status_t ret = ZX_OK;
73 
74     DEBUG_ASSERT(e->magic == EVENT_MAGIC);
75     DEBUG_ASSERT(!arch_blocking_disallowed());
76 
77     Guard<spin_lock_t, IrqSave> guard{ThreadLock::Get()};
78 
79     current_thread->interruptable = interruptable;
80 
81     if (e->signaled) {
82         /* signaled, we're going to fall through */
83         if (e->flags & EVENT_FLAG_AUTOUNSIGNAL) {
84             /* autounsignal flag lets one thread fall through before unsignaling */
85             e->signaled = false;
86         }
87     } else {
88         /* unsignaled, block here */
89         ret = wait_queue_block_etc(&e->wait, deadline, slack, signal_mask);
90     }
91 
92     current_thread->interruptable = false;
93 
94     return ret;
95 }
96 
97 /**
98  * @brief  Wait for event to be signaled
99  *
100  * If the event has already been signaled, this function
101  * returns immediately.  Otherwise, the current thread
102  * goes to sleep until the event object is signaled,
103  * the deadline is reached, or the event object is destroyed
104  * by another thread.
105  *
106  * @param e        Event object
107  * @param deadline Deadline to abort at, in ns
108  * @param interruptable  Allowed to interrupt if thread is signaled
109  *
110  * @return  0 on success, ZX_ERR_TIMED_OUT on timeout,
111  *          other values depending on wait_result value
112  *          when event_signal_etc is used.
113  */
event_wait_deadline(event_t * e,zx_time_t deadline,bool interruptable)114 zx_status_t event_wait_deadline(event_t* e, zx_time_t deadline, bool interruptable) {
115     return event_wait_worker(e, deadline, kNoSlack, interruptable, 0);
116 }
117 
118 /**
119  * @brief  Wait for event to be signaled
120  *
121  * If the event has already been signaled, this function
122  * returns immediately.  Otherwise, the current thread
123  * goes to sleep until the event object is signaled or destroyed by another
124  * thread, the slack-adjusted deadline is reached, or the calling thread is
125  * interrupted.
126  *
127  * @param e        Event object
128  * @param deadline Deadline to timeout at
129  * @param slack    Allowed deviation from the deadline
130  *
131  * @return  0 on success, ZX_ERR_TIMED_OUT on timeout,
132  *          other values depending on wait_result value
133  *          when event_signal_etc is used.
134  */
event_wait_interruptable(event_t * e,zx_time_t deadline,TimerSlack slack)135 zx_status_t event_wait_interruptable(event_t* e, zx_time_t deadline, TimerSlack slack) {
136     return event_wait_worker(e, deadline, slack, true, 0);
137 }
138 
139 /**
140  * @brief  Wait for event to be signaled, ignoring existing signals in
141            |signal_mask|.
142  *
143  * If the event has already been signaled (except for signals in
144  * |signal_mask|), this function returns immediately.
145  * Otherwise, the current thread goes to sleep until the event object is
146  * signaled, or the event object is destroyed by another thread.
147  * There is no deadline, and the caller must be interruptable.
148  *
149  * @param e        Event object
150  *
151  * @return  0 on success, other values depending on wait_result value
152  *          when event_signal_etc is used.
153  */
event_wait_with_mask(event_t * e,uint signal_mask)154 zx_status_t event_wait_with_mask(event_t* e, uint signal_mask) {
155     return event_wait_worker(e, ZX_TIME_INFINITE, kNoSlack, true, signal_mask);
156 }
157 
event_signal_internal(event_t * e,bool reschedule,zx_status_t wait_result)158 static int event_signal_internal(event_t* e, bool reschedule,
159                                  zx_status_t wait_result) TA_REQ(thread_lock) {
160     DEBUG_ASSERT(e->magic == EVENT_MAGIC);
161 
162     int wake_count = 0;
163 
164     if (!e->signaled) {
165         if (e->flags & EVENT_FLAG_AUTOUNSIGNAL) {
166             /* try to release one thread and leave unsignaled if successful */
167             if ((wake_count = wait_queue_wake_one(&e->wait, reschedule, wait_result)) <= 0) {
168                 /*
169                  * if we didn't actually find a thread to wake up, go to
170                  * signaled state and let the next call to event_wait
171                  * unsignal the event.
172                  */
173                 e->signaled = true;
174             }
175         } else {
176             /* release all threads and remain signaled */
177             e->signaled = true;
178             wake_count = wait_queue_wake_all(&e->wait, reschedule, wait_result);
179         }
180     }
181 
182     return wake_count;
183 }
184 
185 /**
186  * @brief  Signal an event
187  *
188  * Signals an event.  If EVENT_FLAG_AUTOUNSIGNAL is set in the event
189  * object's flags, only one waiting thread is allowed to proceed.  Otherwise,
190  * all waiting threads are allowed to proceed until such time as
191  * event_unsignal() is called.
192  *
193  * @param e           Event object
194  * @param reschedule  If true, waiting thread(s) are executed immediately,
195  *                    and the current thread resumes only after the
196  *                    waiting threads have been satisfied. If false,
197  *                    waiting threads are placed at the head of the run
198  *                    queue.
199  * @param wait_result What status event_wait_deadline will return to the
200  *                    thread or threads that are woken up.
201  *
202  * @return  Returns the number of threads that have been unblocked.
203  */
event_signal_etc(event_t * e,bool reschedule,zx_status_t wait_result)204 int event_signal_etc(event_t* e, bool reschedule, zx_status_t wait_result) {
205     Guard<spin_lock_t, IrqSave> guard{ThreadLock::Get()};
206     return event_signal_internal(e, reschedule, wait_result);
207 }
208 
209 /**
210  * @brief  Signal an event
211  *
212  * Signals an event.  If EVENT_FLAG_AUTOUNSIGNAL is set in the event
213  * object's flags, only one waiting thread is allowed to proceed.  Otherwise,
214  * all waiting threads are allowed to proceed until such time as
215  * event_unsignal() is called.
216  *
217  * @param e           Event object
218  * @param reschedule  If true, waiting thread(s) are executed immediately,
219  *                    and the current thread resumes only after the
220  *                    waiting threads have been satisfied. If false,
221  *                    waiting threads are placed at the head of the run
222  *                    queue.
223  *
224  * @return  Returns the number of threads that have been unblocked.
225  */
event_signal(event_t * e,bool reschedule)226 int event_signal(event_t* e, bool reschedule) {
227     Guard<spin_lock_t, IrqSave> guard{ThreadLock::Get()};
228     return event_signal_internal(e, reschedule, ZX_OK);
229 }
230 
231 /* same as above, but the thread lock must already be held */
event_signal_thread_locked(event_t * e)232 int event_signal_thread_locked(event_t* e) {
233     DEBUG_ASSERT(arch_ints_disabled());
234     DEBUG_ASSERT(spin_lock_held(&thread_lock));
235 
236     return event_signal_internal(e, false, ZX_OK);
237 }
238 
239 /**
240  * @brief  Clear the "signaled" property of an event
241  *
242  * Used mainly for event objects without the EVENT_FLAG_AUTOUNSIGNAL
243  * flag.  Once this function is called, threads that call event_wait()
244  * functions will once again need to wait until the event object
245  * is signaled.
246  *
247  * @param e  Event object
248  *
249  * @return  Returns ZX_OK on success.
250  */
event_unsignal(event_t * e)251 zx_status_t event_unsignal(event_t* e) {
252     DEBUG_ASSERT(e->magic == EVENT_MAGIC);
253 
254     e->signaled = false;
255 
256     return ZX_OK;
257 }
258