1 /*
2 Copyright (c) 2012, Broadcom Europe Ltd
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12 * Neither the name of the copyright holder nor the
13 names of its contributors may be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 /*=============================================================================
29 VideoCore OS Abstraction Layer - event flags implemented via mutexes
30 =============================================================================*/
31
32 #include "interface/vcos/vcos.h"
33 #include "interface/vcos/generic/vcos_generic_event_flags.h"
34
35 #include <stddef.h>
36
37 /** A structure created by a thread that waits on the event flags
38 * for a particular combination of flags to arrive.
39 */
40 typedef struct VCOS_EVENT_WAITER_T
41 {
42 VCOS_UNSIGNED requested_events; /**< The events wanted */
43 VCOS_UNSIGNED actual_events; /**< Actual events found */
44 VCOS_UNSIGNED op; /**< The event operation to be used */
45 VCOS_STATUS_T return_status; /**< The return status the waiter should pass back */
46 VCOS_EVENT_FLAGS_T *flags; /**< Pointer to the original 'flags' structure */
47 VCOS_THREAD_T *thread; /**< Thread waiting */
48 struct VCOS_EVENT_WAITER_T *next;
49 } VCOS_EVENT_WAITER_T;
50
51 #ifndef NDEBUG
52 static int waiter_list_valid(VCOS_EVENT_FLAGS_T *flags);
53 #endif
54 static void event_flags_timer_expired(void *cxt);
55
vcos_generic_event_flags_create(VCOS_EVENT_FLAGS_T * flags,const char * name)56 VCOS_STATUS_T vcos_generic_event_flags_create(VCOS_EVENT_FLAGS_T *flags, const char *name)
57 {
58 VCOS_STATUS_T rc;
59 if ((rc=vcos_mutex_create(&flags->lock, name)) != VCOS_SUCCESS)
60 {
61 return rc;
62 }
63
64 flags->events = 0;
65 flags->waiters.head = flags->waiters.tail = 0;
66 return rc;
67 }
68
vcos_generic_event_flags_set(VCOS_EVENT_FLAGS_T * flags,VCOS_UNSIGNED bitmask,VCOS_OPTION op)69 void vcos_generic_event_flags_set(VCOS_EVENT_FLAGS_T *flags,
70 VCOS_UNSIGNED bitmask,
71 VCOS_OPTION op)
72 {
73 vcos_assert(flags);
74 vcos_mutex_lock(&flags->lock);
75 if (op == VCOS_OR)
76 {
77 flags->events |= bitmask;
78 }
79 else if (op == VCOS_AND)
80 {
81 flags->events &= bitmask;
82 }
83 else
84 {
85 vcos_assert(0);
86 }
87
88 /* Now wake up any threads that have now become signalled. */
89 if (flags->waiters.head != NULL)
90 {
91 VCOS_UNSIGNED consumed_events = 0;
92 VCOS_EVENT_WAITER_T **pcurrent_waiter = &flags->waiters.head;
93 VCOS_EVENT_WAITER_T *prev_waiter = NULL;
94
95 /* Walk the chain of tasks suspend on this event flag group to determine
96 * if any of their requests can be satisfied.
97 */
98 while ((*pcurrent_waiter) != NULL)
99 {
100 VCOS_EVENT_WAITER_T *curr_waiter = *pcurrent_waiter;
101
102 /* Determine if this request has been satisfied */
103
104 /* First, find the event flags in common. */
105 VCOS_UNSIGNED waiter_satisfied = flags->events & curr_waiter->requested_events;
106
107 /* Second, determine if all the event flags must match */
108 if (curr_waiter->op & VCOS_AND)
109 {
110 /* All requested events must be present */
111 waiter_satisfied = (waiter_satisfied == curr_waiter->requested_events);
112 }
113
114 /* Wake this one up? */
115 if (waiter_satisfied)
116 {
117
118 if (curr_waiter->op & VCOS_CONSUME)
119 {
120 consumed_events |= curr_waiter->requested_events;
121 }
122
123 /* remove this block from the list, taking care at the end */
124 *pcurrent_waiter = curr_waiter->next;
125 if (curr_waiter->next == NULL)
126 flags->waiters.tail = prev_waiter;
127
128 vcos_assert(waiter_list_valid(flags));
129
130 curr_waiter->return_status = VCOS_SUCCESS;
131 curr_waiter->actual_events = flags->events;
132
133 _vcos_thread_sem_post(curr_waiter->thread);
134 }
135 else
136 {
137 /* move to next element in the list */
138 prev_waiter = *pcurrent_waiter;
139 pcurrent_waiter = &(curr_waiter->next);
140 }
141 }
142
143 flags->events &= ~consumed_events;
144
145 }
146
147 vcos_mutex_unlock(&flags->lock);
148 }
149
vcos_generic_event_flags_delete(VCOS_EVENT_FLAGS_T * flags)150 void vcos_generic_event_flags_delete(VCOS_EVENT_FLAGS_T *flags)
151 {
152 vcos_mutex_delete(&flags->lock);
153 }
154
vcos_generic_event_flags_get(VCOS_EVENT_FLAGS_T * flags,VCOS_UNSIGNED bitmask,VCOS_OPTION op,VCOS_UNSIGNED suspend,VCOS_UNSIGNED * retrieved_bits)155 extern VCOS_STATUS_T vcos_generic_event_flags_get(VCOS_EVENT_FLAGS_T *flags,
156 VCOS_UNSIGNED bitmask,
157 VCOS_OPTION op,
158 VCOS_UNSIGNED suspend,
159 VCOS_UNSIGNED *retrieved_bits)
160 {
161 VCOS_EVENT_WAITER_T waitreq;
162 VCOS_STATUS_T rc = VCOS_EAGAIN;
163 int satisfied = 0;
164
165 vcos_assert(flags);
166
167 /* default retrieved bits to 0 */
168 *retrieved_bits = 0;
169
170 vcos_mutex_lock(&flags->lock);
171 switch (op & VCOS_EVENT_FLAG_OP_MASK)
172 {
173 case VCOS_AND:
174 if ((flags->events & bitmask) == bitmask)
175 {
176 *retrieved_bits = flags->events;
177 rc = VCOS_SUCCESS;
178 satisfied = 1;
179 if (op & VCOS_CONSUME)
180 flags->events &= ~bitmask;
181 }
182 break;
183
184 case VCOS_OR:
185 if (flags->events & bitmask)
186 {
187 *retrieved_bits = flags->events;
188 rc = VCOS_SUCCESS;
189 satisfied = 1;
190 if (op & VCOS_CONSUME)
191 flags->events &= ~bitmask;
192 }
193 break;
194
195 default:
196 vcos_assert(0);
197 rc = VCOS_EINVAL;
198 break;
199 }
200
201 if (!satisfied && suspend)
202 {
203 /* Have to go to sleep.
204 *
205 * Append to tail so we get FIFO ordering.
206 */
207 waitreq.requested_events = bitmask;
208 waitreq.op = op;
209 waitreq.return_status = VCOS_EAGAIN;
210 waitreq.flags = flags;
211 waitreq.actual_events = 0;
212 waitreq.thread = vcos_thread_current();
213 waitreq.next = 0;
214 vcos_assert(waitreq.thread != (VCOS_THREAD_T*)-1);
215 VCOS_QUEUE_APPEND_TAIL(&flags->waiters, &waitreq);
216
217 if (suspend != (VCOS_UNSIGNED)-1)
218 _vcos_task_timer_set(event_flags_timer_expired, &waitreq, suspend);
219
220 vcos_mutex_unlock(&flags->lock);
221 /* go to sleep and wait to be signalled or timeout */
222
223 _vcos_thread_sem_wait();
224
225 *retrieved_bits = waitreq.actual_events;
226 rc = waitreq.return_status;
227
228 /* cancel the timer - do not do this while holding the mutex as it
229 * might be waiting for the timeout function to complete, which will
230 * try to take the mutex.
231 */
232 if (suspend != (VCOS_UNSIGNED)-1)
233 _vcos_task_timer_cancel();
234 }
235 else
236 {
237 vcos_mutex_unlock(&flags->lock);
238 }
239
240 return rc;
241 }
242
243 /** Called when a get call times out. Remove this thread's
244 * entry from the waiting queue, then resume the thread.
245 */
event_flags_timer_expired(void * cxt)246 static void event_flags_timer_expired(void *cxt)
247 {
248 VCOS_EVENT_WAITER_T *waitreq = (VCOS_EVENT_WAITER_T *)cxt;
249 VCOS_EVENT_FLAGS_T *flags = waitreq->flags;
250 VCOS_EVENT_WAITER_T **plist;
251 VCOS_EVENT_WAITER_T *prev = NULL;
252 VCOS_THREAD_T *thread = 0;
253
254 vcos_assert(flags);
255
256 vcos_mutex_lock(&flags->lock);
257
258 /* walk the list of waiting threads on this event group, and remove
259 * the one that has expired.
260 *
261 * FIXME: could use doubly-linked list if lots of threads are found
262 * to be waiting on a single event flag instance.
263 */
264 plist = &flags->waiters.head;
265 while (*plist != NULL)
266 {
267 if (*plist == waitreq)
268 {
269 int at_end;
270 /* found it */
271 thread = (*plist)->thread;
272 at_end = ((*plist)->next == NULL);
273
274 /* link past */
275 *plist = (*plist)->next;
276 if (at_end)
277 flags->waiters.tail = prev;
278
279 break;
280 }
281 prev = *plist;
282 plist = &(*plist)->next;
283 }
284 vcos_assert(waiter_list_valid(flags));
285
286 vcos_mutex_unlock(&flags->lock);
287
288 if (thread)
289 {
290 _vcos_thread_sem_post(thread);
291 }
292 }
293
294 #ifndef NDEBUG
295
waiter_list_valid(VCOS_EVENT_FLAGS_T * flags)296 static int waiter_list_valid(VCOS_EVENT_FLAGS_T *flags)
297 {
298 int valid;
299 /* Either both head and tail are NULL, or neither are NULL */
300 if (flags->waiters.head == NULL)
301 {
302 valid = (flags->waiters.tail == NULL);
303 }
304 else
305 {
306 valid = (flags->waiters.tail != NULL);
307 }
308
309 /* If head and tail point at the same non-NULL element, then there
310 * is only one element in the list.
311 */
312 if (flags->waiters.head && (flags->waiters.head == flags->waiters.tail))
313 {
314 valid = (flags->waiters.head->next == NULL);
315 }
316 return valid;
317 }
318
319 #endif
320