1 /**************************************************************************/
2 /*                                                                        */
3 /*       Copyright (c) Microsoft Corporation. All rights reserved.        */
4 /*                                                                        */
5 /*       This software is licensed under the Microsoft Software License   */
6 /*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
7 /*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
8 /*       and in the root directory of this software.                      */
9 /*                                                                        */
10 /**************************************************************************/
11 
12 
13 /**************************************************************************/
14 /**************************************************************************/
15 /**                                                                       */
16 /** ThreadX Component                                                     */
17 /**                                                                       */
18 /**   Event Flags                                                         */
19 /**                                                                       */
20 /**************************************************************************/
21 /**************************************************************************/
22 
23 #define TX_SOURCE_CODE
24 
25 
26 /* Include necessary system files.  */
27 
28 #include "tx_api.h"
29 #include "tx_trace.h"
30 #include "tx_thread.h"
31 #include "tx_event_flags.h"
32 
33 
34 /**************************************************************************/
35 /*                                                                        */
36 /*  FUNCTION                                               RELEASE        */
37 /*                                                                        */
38 /*    _tx_event_flags_get                                 PORTABLE C      */
39 /*                                                           6.2.0        */
40 /*  AUTHOR                                                                */
41 /*                                                                        */
42 /*    William E. Lamie, Microsoft Corporation                             */
43 /*                                                                        */
44 /*  DESCRIPTION                                                           */
45 /*                                                                        */
46 /*    This function gets the specified event flags from the group,        */
47 /*    according to the get option.  The get option also specifies whether */
48 /*    or not the retrieved flags are cleared.                             */
49 /*                                                                        */
50 /*  INPUT                                                                 */
51 /*                                                                        */
52 /*    group_ptr                         Pointer to group control block    */
53 /*    requested_event_flags             Event flags requested             */
54 /*    get_option                        Specifies and/or and clear options*/
55 /*    actual_flags_ptr                  Pointer to place the actual flags */
56 /*                                        the service retrieved           */
57 /*    wait_option                       Suspension option                 */
58 /*                                                                        */
59 /*  OUTPUT                                                                */
60 /*                                                                        */
61 /*    status                            Completion status                 */
62 /*                                                                        */
63 /*  CALLS                                                                 */
64 /*                                                                        */
65 /*    _tx_thread_system_suspend         Suspend thread service            */
66 /*    _tx_thread_system_ni_suspend      Non-interruptable suspend thread  */
67 /*                                                                        */
68 /*  CALLED BY                                                             */
69 /*                                                                        */
70 /*    Application Code                                                    */
71 /*                                                                        */
72 /*  RELEASE HISTORY                                                       */
73 /*                                                                        */
74 /*    DATE              NAME                      DESCRIPTION             */
75 /*                                                                        */
76 /*  05-19-2020      William E. Lamie        Initial Version 6.0           */
77 /*  09-30-2020      Yuxin Zhou              Modified comment(s),          */
78 /*                                            resulting in version 6.1    */
79 /*  04-25-2022      Scott Larson            Modified comment(s),          */
80 /*                                            handle 0 flags case,        */
81 /*                                            resulting in version 6.1.11 */
82 /*  10-31-2022      Scott Larson            Modified comment(s), always   */
83 /*                                            return actual flags,        */
84 /*                                            resulting in version 6.2.0  */
85 /*                                                                        */
86 /**************************************************************************/
_tx_event_flags_get(TX_EVENT_FLAGS_GROUP * group_ptr,ULONG requested_flags,UINT get_option,ULONG * actual_flags_ptr,ULONG wait_option)87 UINT  _tx_event_flags_get(TX_EVENT_FLAGS_GROUP *group_ptr, ULONG requested_flags,
88                     UINT get_option, ULONG *actual_flags_ptr, ULONG wait_option)
89 {
90 
91 TX_INTERRUPT_SAVE_AREA
92 
93 UINT            status;
94 UINT            and_request;
95 UINT            clear_request;
96 ULONG           current_flags;
97 ULONG           flags_satisfied;
98 #ifndef TX_NOT_INTERRUPTABLE
99 ULONG           delayed_clear_flags;
100 #endif
101 UINT            suspended_count;
102 TX_THREAD       *thread_ptr;
103 TX_THREAD       *next_thread;
104 TX_THREAD       *previous_thread;
105 #ifndef TX_NOT_INTERRUPTABLE
106 UINT            interrupted_set_request;
107 #endif
108 
109 
110     /* Disable interrupts to examine the event flags group.  */
111     TX_DISABLE
112 
113 #ifdef TX_EVENT_FLAGS_ENABLE_PERFORMANCE_INFO
114 
115     /* Increment the total event flags get counter.  */
116     _tx_event_flags_performance_get_count++;
117 
118     /* Increment the number of event flags gets on this semaphore.  */
119     group_ptr -> tx_event_flags_group__performance_get_count++;
120 #endif
121 
122     /* If trace is enabled, insert this event into the trace buffer.  */
123     TX_TRACE_IN_LINE_INSERT(TX_TRACE_EVENT_FLAGS_GET, group_ptr, requested_flags, group_ptr -> tx_event_flags_group_current, get_option, TX_TRACE_EVENT_FLAGS_EVENTS)
124 
125     /* Log this kernel call.  */
126     TX_EL_EVENT_FLAGS_GET_INSERT
127 
128     /* Pickup current flags.  */
129     current_flags =  group_ptr -> tx_event_flags_group_current;
130 
131     /* Return the actual event flags and apply delayed clearing.  */
132     *actual_flags_ptr =  current_flags & ~group_ptr -> tx_event_flags_group_delayed_clear;
133 
134     /* Apply the event flag option mask.  */
135     and_request =  (get_option & TX_AND);
136 
137 #ifdef TX_NOT_INTERRUPTABLE
138 
139     /* Check for AND condition. All flags must be present to satisfy request.  */
140     if (and_request == TX_AND)
141     {
142 
143         /* AND request is present.  */
144 
145         /* Calculate the flags present.  */
146         flags_satisfied =  (current_flags & requested_flags);
147 
148         /* Determine if they satisfy the AND request.  */
149         if (flags_satisfied != requested_flags)
150         {
151 
152             /* No, not all the requested flags are present. Clear the flags present variable.  */
153             flags_satisfied =  ((ULONG) 0);
154         }
155     }
156     else
157     {
158 
159         /* OR request is present. Simply or the requested flags and the current flags.  */
160         flags_satisfied =  (current_flags & requested_flags);
161     }
162 
163     /* Determine if the request is satisfied.  */
164     if (flags_satisfied != ((ULONG) 0))
165     {
166 
167         /* Pickup the clear bit.  */
168         clear_request =  (get_option & TX_EVENT_FLAGS_CLEAR_MASK);
169 
170         /* Determine whether or not clearing needs to take place.  */
171         if (clear_request == TX_TRUE)
172         {
173 
174              /* Yes, clear the flags that satisfied this request.  */
175              group_ptr -> tx_event_flags_group_current =
176                                         group_ptr -> tx_event_flags_group_current & (~requested_flags);
177         }
178 
179         /* Return success.  */
180         status =  TX_SUCCESS;
181     }
182 
183 #else
184 
185     /* Pickup delayed clear flags.  */
186     delayed_clear_flags =  group_ptr -> tx_event_flags_group_delayed_clear;
187 
188     /* Determine if there are any delayed clear operations pending.  */
189     if (delayed_clear_flags != ((ULONG) 0))
190     {
191 
192         /* Yes, apply them to the current flags.  */
193         current_flags =  current_flags & (~delayed_clear_flags);
194     }
195 
196     /* Check for AND condition. All flags must be present to satisfy request.  */
197     if (and_request == TX_AND)
198     {
199 
200         /* AND request is present.  */
201 
202         /* Calculate the flags present.  */
203         flags_satisfied =  (current_flags & requested_flags);
204 
205         /* Determine if they satisfy the AND request.  */
206         if (flags_satisfied != requested_flags)
207         {
208 
209             /* No, not all the requested flags are present. Clear the flags present variable.  */
210             flags_satisfied =  ((ULONG) 0);
211         }
212     }
213     else
214     {
215 
216         /* OR request is present. Simply AND together the requested flags and the current flags
217            to see if any are present.  */
218         flags_satisfied =  (current_flags & requested_flags);
219     }
220 
221     /* Determine if the request is satisfied.  */
222     if (flags_satisfied != ((ULONG) 0))
223     {
224 
225         /* Yes, this request can be handled immediately.  */
226 
227         /* Pickup the clear bit.  */
228         clear_request =  (get_option & TX_EVENT_FLAGS_CLEAR_MASK);
229 
230         /* Determine whether or not clearing needs to take place.  */
231         if (clear_request == TX_TRUE)
232         {
233 
234             /* Set interrupted set request flag to false.  */
235             interrupted_set_request =  TX_FALSE;
236 
237             /* Determine if the suspension list is being processed by an interrupted
238                set request.  */
239             if (group_ptr -> tx_event_flags_group_suspended_count != TX_NO_SUSPENSIONS)
240             {
241 
242                 if (group_ptr -> tx_event_flags_group_suspension_list == TX_NULL)
243                 {
244 
245                     /* Set the interrupted set request flag.  */
246                     interrupted_set_request =  TX_TRUE;
247                 }
248             }
249 
250             /* Was a set request interrupted?  */
251             if (interrupted_set_request == TX_TRUE)
252             {
253 
254                 /* A previous set operation is was interrupted, we need to defer the
255                    event clearing until the set operation is complete.  */
256 
257                 /* Remember the events to clear.  */
258                 group_ptr -> tx_event_flags_group_delayed_clear =
259                                         group_ptr -> tx_event_flags_group_delayed_clear | requested_flags;
260             }
261             else
262             {
263 
264                 /* Yes, clear the flags that satisfied this request.  */
265                 group_ptr -> tx_event_flags_group_current =
266                                         group_ptr -> tx_event_flags_group_current & ~requested_flags;
267             }
268         }
269 
270         /* Set status to success.  */
271         status =  TX_SUCCESS;
272     }
273 
274 #endif
275     else
276     {
277         /* flags_satisfied is 0.  */
278         /* Determine if the request specifies suspension.  */
279         if (wait_option != TX_NO_WAIT)
280         {
281 
282             /* Determine if the preempt disable flag is non-zero OR the requested events is 0.  */
283             if ((_tx_thread_preempt_disable != ((UINT) 0)) || (requested_flags == (UINT) 0))
284             {
285 
286                 /* Suspension is not allowed if the preempt disable flag is non-zero at this point,
287                    or if requested_flags is 0, return error completion.  */
288                 status =  TX_NO_EVENTS;
289             }
290             else
291             {
292 
293                 /* Prepare for suspension of this thread.  */
294 
295 #ifdef TX_EVENT_FLAGS_ENABLE_PERFORMANCE_INFO
296 
297                 /* Increment the total event flags suspensions counter.  */
298                 _tx_event_flags_performance_suspension_count++;
299 
300                 /* Increment the number of event flags suspensions on this semaphore.  */
301                 group_ptr -> tx_event_flags_group___performance_suspension_count++;
302 #endif
303 
304                 /* Pickup thread pointer.  */
305                 TX_THREAD_GET_CURRENT(thread_ptr)
306 
307                 /* Setup cleanup routine pointer.  */
308                 thread_ptr -> tx_thread_suspend_cleanup =  &(_tx_event_flags_cleanup);
309 
310                 /* Remember which event flags we are looking for.  */
311                 thread_ptr -> tx_thread_suspend_info =  requested_flags;
312 
313                 /* Save the get option as well.  */
314                 thread_ptr -> tx_thread_suspend_option =  get_option;
315 
316                 /* Save the destination for the current events.  */
317                 thread_ptr -> tx_thread_additional_suspend_info =  (VOID *) actual_flags_ptr;
318 
319                 /* Setup cleanup information, i.e. this event flags group control
320                    block.  */
321                 thread_ptr -> tx_thread_suspend_control_block =  (VOID *) group_ptr;
322 
323 #ifndef TX_NOT_INTERRUPTABLE
324 
325                 /* Increment the suspension sequence number, which is used to identify
326                    this suspension event.  */
327                 thread_ptr -> tx_thread_suspension_sequence++;
328 #endif
329 
330                 /* Pickup the suspended count.  */
331                 suspended_count =  group_ptr -> tx_event_flags_group_suspended_count;
332 
333                 /* Setup suspension list.  */
334                 if (suspended_count == TX_NO_SUSPENSIONS)
335                 {
336 
337                     /* No other threads are suspended.  Setup the head pointer and
338                        just setup this threads pointers to itself.  */
339                     group_ptr -> tx_event_flags_group_suspension_list =   thread_ptr;
340                     thread_ptr -> tx_thread_suspended_next =              thread_ptr;
341                     thread_ptr -> tx_thread_suspended_previous =          thread_ptr;
342                 }
343                 else
344                 {
345 
346                     /* This list is not NULL, add current thread to the end. */
347                     next_thread =                                   group_ptr -> tx_event_flags_group_suspension_list;
348                     thread_ptr -> tx_thread_suspended_next =        next_thread;
349                     previous_thread =                               next_thread -> tx_thread_suspended_previous;
350                     thread_ptr -> tx_thread_suspended_previous =    previous_thread;
351                     previous_thread -> tx_thread_suspended_next =   thread_ptr;
352                     next_thread -> tx_thread_suspended_previous =   thread_ptr;
353                 }
354 
355                 /* Increment the number of threads suspended.  */
356                 group_ptr -> tx_event_flags_group_suspended_count++;
357 
358                 /* Set the state to suspended.  */
359                 thread_ptr -> tx_thread_state =    TX_EVENT_FLAG;
360 
361 #ifdef TX_NOT_INTERRUPTABLE
362 
363                 /* Call actual non-interruptable thread suspension routine.  */
364                 _tx_thread_system_ni_suspend(thread_ptr, wait_option);
365 
366                 /* Return the completion status.  */
367                 status =  thread_ptr -> tx_thread_suspend_status;
368 #else
369 
370                 /* Set the suspending flag.  */
371                 thread_ptr -> tx_thread_suspending =  TX_TRUE;
372 
373                 /* Setup the timeout period.  */
374                 thread_ptr -> tx_thread_timer.tx_timer_internal_remaining_ticks =  wait_option;
375 
376                 /* Temporarily disable preemption.  */
377                 _tx_thread_preempt_disable++;
378 
379                 /* Restore interrupts.  */
380                 TX_RESTORE
381 
382                 /* Call actual thread suspension routine.  */
383                 _tx_thread_system_suspend(thread_ptr);
384 
385                 /* Disable interrupts.  */
386                 TX_DISABLE
387 
388                 /* Return the completion status.  */
389                 status =  thread_ptr -> tx_thread_suspend_status;
390 #endif
391             }
392         }
393         else
394         {
395 
396             /* Immediate return, return error completion.  */
397             status =  TX_NO_EVENTS;
398         }
399     }
400 
401     /* Restore interrupts.  */
402     TX_RESTORE
403 
404     /* Return completion status.  */
405     return(status);
406 }
407 
408