1 /*
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 /**
10 * @file
11 * @brief Event wait and signal functions for threads.
12 * @defgroup event Events
13 *
14 * An event is a subclass of a wait queue.
15 *
16 * Threads wait for events, with optional timeouts.
17 *
18 * Events are "signaled", releasing waiting threads to continue.
19 * Signals may be one-shot signals (EVENT_FLAG_AUTOUNSIGNAL), in which
20 * case one signal releases only one thread, at which point it is
21 * automatically cleared. Otherwise, signals release all waiting threads
22 * to continue immediately until the signal is manually cleared with
23 * event_unsignal().
24 *
25 * @{
26 */
27
28 #include <assert.h>
29 #include <kernel/event.h>
30 #include <kernel/thread.h>
31 #include <lk/debug.h>
32 #include <lk/err.h>
33
34 /**
35 * @brief Initialize an event object
36 *
37 * @param e Event object to initialize
38 * @param initial Initial value for "signaled" state
39 * @param flags 0 or EVENT_FLAG_AUTOUNSIGNAL
40 */
event_init(event_t * e,bool initial,uint flags)41 void event_init(event_t *e, bool initial, uint flags) {
42 *e = (event_t)EVENT_INITIAL_VALUE(*e, initial, flags);
43 }
44
45 /**
46 * @brief Destroy an event object.
47 *
48 * Event's resources are freed and it may no longer be
49 * used until event_init() is called again. Any threads
50 * still waiting on the event will be resumed.
51 *
52 * @param e Event object to initialize
53 */
event_destroy(event_t * e)54 void event_destroy(event_t *e) {
55 DEBUG_ASSERT(e->magic == EVENT_MAGIC);
56
57 THREAD_LOCK(state);
58
59 e->magic = 0;
60 e->signaled = false;
61 e->flags = 0;
62 wait_queue_destroy(&e->wait, true);
63
64 THREAD_UNLOCK(state);
65 }
66
67 /**
68 * @brief Wait for event to be signaled
69 *
70 * If the event has already been signaled, this function
71 * returns immediately. Otherwise, the current thread
72 * goes to sleep until the event object is signaled,
73 * the timeout is reached, or the event object is destroyed
74 * by another thread.
75 *
76 * @param e Event object
77 * @param timeout Timeout value, in ms
78 *
79 * @return 0 on success, ERR_TIMED_OUT on timeout,
80 * other values on other errors.
81 */
event_wait_timeout(event_t * e,lk_time_t timeout)82 status_t event_wait_timeout(event_t *e, lk_time_t timeout) {
83 status_t ret = NO_ERROR;
84
85 DEBUG_ASSERT(e->magic == EVENT_MAGIC);
86
87 THREAD_LOCK(state);
88
89 if (e->signaled) {
90 /* signaled, we're going to fall through */
91 if (e->flags & EVENT_FLAG_AUTOUNSIGNAL) {
92 /* autounsignal flag lets one thread fall through before unsignaling */
93 e->signaled = false;
94 }
95 } else {
96 /* unsignaled, block here */
97 ret = wait_queue_block(&e->wait, timeout);
98 }
99
100 THREAD_UNLOCK(state);
101
102 return ret;
103 }
104
105 /**
106 * @brief Signal an event
107 *
108 * Signals an event. If EVENT_FLAG_AUTOUNSIGNAL is set in the event
109 * object's flags, only one waiting thread is allowed to proceed. Otherwise,
110 * all waiting threads are allowed to proceed until such time as
111 * event_unsignal() is called.
112 *
113 * @param e Event object
114 * @param reschedule If true, waiting thread(s) are executed immediately,
115 * and the current thread resumes only after the
116 * waiting threads have been satisfied. If false,
117 * waiting threads are placed at the end of the run
118 * queue.
119 *
120 * @return Returns NO_ERROR on success.
121 */
event_signal(event_t * e,bool reschedule)122 status_t event_signal(event_t *e, bool reschedule) {
123 DEBUG_ASSERT(e->magic == EVENT_MAGIC);
124
125 THREAD_LOCK(state);
126
127 if (!e->signaled) {
128 if (e->flags & EVENT_FLAG_AUTOUNSIGNAL) {
129 /* try to release one thread and leave unsignaled if successful */
130 if (wait_queue_wake_one(&e->wait, reschedule, NO_ERROR) <= 0) {
131 /*
132 * if we didn't actually find a thread to wake up, go to
133 * signaled state and let the next call to event_wait
134 * unsignal the event.
135 */
136 e->signaled = true;
137 }
138 } else {
139 /* release all threads and remain signaled */
140 e->signaled = true;
141 wait_queue_wake_all(&e->wait, reschedule, NO_ERROR);
142 }
143 }
144
145 THREAD_UNLOCK(state);
146
147 return NO_ERROR;
148 }
149
150 /**
151 * @brief Clear the "signaled" property of an event
152 *
153 * Used mainly for event objects without the EVENT_FLAG_AUTOUNSIGNAL
154 * flag. Once this function is called, threads that call event_wait()
155 * functions will once again need to wait until the event object
156 * is signaled.
157 *
158 * @param e Event object
159 *
160 * @return Returns NO_ERROR on success.
161 */
event_unsignal(event_t * e)162 status_t event_unsignal(event_t *e) {
163 DEBUG_ASSERT(e->magic == EVENT_MAGIC);
164
165 e->signaled = false;
166
167 return NO_ERROR;
168 }
169
170