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