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