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 | |
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 | |
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 | |
150 | void vcos_generic_event_flags_delete(VCOS_EVENT_FLAGS_T *flags) |
151 | { |
152 | vcos_mutex_delete(&flags->lock); |
153 | } |
154 | |
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 | |
244 | /** Called when a get call times out. Remove this thread's |
245 | * entry from the waiting queue, then resume the thread. |
246 | */ |
247 | static void event_flags_timer_expired(void *cxt) |
248 | { |
249 | VCOS_EVENT_WAITER_T *waitreq = (VCOS_EVENT_WAITER_T *)cxt; |
250 | VCOS_EVENT_FLAGS_T *flags = waitreq->flags; |
251 | VCOS_EVENT_WAITER_T **plist; |
252 | VCOS_EVENT_WAITER_T *prev = NULL; |
253 | VCOS_THREAD_T *thread = 0; |
254 | |
255 | vcos_assert(flags); |
256 | |
257 | vcos_mutex_lock(&flags->lock); |
258 | |
259 | /* walk the list of waiting threads on this event group, and remove |
260 | * the one that has expired. |
261 | * |
262 | * FIXME: could use doubly-linked list if lots of threads are found |
263 | * to be waiting on a single event flag instance. |
264 | */ |
265 | plist = &flags->waiters.head; |
266 | while (*plist != NULL) |
267 | { |
268 | if (*plist == waitreq) |
269 | { |
270 | int at_end; |
271 | /* found it */ |
272 | thread = (*plist)->thread; |
273 | at_end = ((*plist)->next == NULL); |
274 | |
275 | /* link past */ |
276 | *plist = (*plist)->next; |
277 | if (at_end) |
278 | flags->waiters.tail = prev; |
279 | |
280 | break; |
281 | } |
282 | prev = *plist; |
283 | plist = &(*plist)->next; |
284 | } |
285 | vcos_assert(waiter_list_valid(flags)); |
286 | |
287 | vcos_mutex_unlock(&flags->lock); |
288 | |
289 | if (thread) |
290 | { |
291 | _vcos_thread_sem_post(thread); |
292 | } |
293 | } |
294 | |
295 | #ifndef NDEBUG |
296 | |
297 | static int waiter_list_valid(VCOS_EVENT_FLAGS_T *flags) |
298 | { |
299 | int valid; |
300 | /* Either both head and tail are NULL, or neither are NULL */ |
301 | if (flags->waiters.head == NULL) |
302 | { |
303 | valid = (flags->waiters.tail == NULL); |
304 | } |
305 | else |
306 | { |
307 | valid = (flags->waiters.tail != NULL); |
308 | } |
309 | |
310 | /* If head and tail point at the same non-NULL element, then there |
311 | * is only one element in the list. |
312 | */ |
313 | if (flags->waiters.head && (flags->waiters.head == flags->waiters.tail)) |
314 | { |
315 | valid = (flags->waiters.head->next == NULL); |
316 | } |
317 | return valid; |
318 | } |
319 | |
320 | #endif |
321 | |