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