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_set                                 PORTABLE C      */
38 /*                                                           6.1.11       */
39 /*  AUTHOR                                                                */
40 /*                                                                        */
41 /*    William E. Lamie, Microsoft Corporation                             */
42 /*                                                                        */
43 /*  DESCRIPTION                                                           */
44 /*                                                                        */
45 /*    This function sets the specified flags in the event group based on  */
46 /*    the set option specified.  All threads suspended on the group whose */
47 /*    get request can now be satisfied are resumed.                       */
48 /*                                                                        */
49 /*  INPUT                                                                 */
50 /*                                                                        */
51 /*    group_ptr                         Pointer to group control block    */
52 /*    flags_to_set                      Event flags to set                */
53 /*    set_option                        Specified either AND or OR        */
54 /*                                        operation on the event flags    */
55 /*                                                                        */
56 /*  OUTPUT                                                                */
57 /*                                                                        */
58 /*    TX_SUCCESS                        Always returns success            */
59 /*                                                                        */
60 /*  CALLS                                                                 */
61 /*                                                                        */
62 /*    _tx_thread_system_preempt_check   Check for preemption              */
63 /*    _tx_thread_system_resume          Resume thread service             */
64 /*    _tx_thread_system_ni_resume       Non-interruptable resume thread   */
65 /*                                                                        */
66 /*  CALLED BY                                                             */
67 /*                                                                        */
68 /*    Application Code                                                    */
69 /*                                                                        */
70 /*  RELEASE HISTORY                                                       */
71 /*                                                                        */
72 /*    DATE              NAME                      DESCRIPTION             */
73 /*                                                                        */
74 /*  05-19-2020      William E. Lamie        Initial Version 6.0           */
75 /*  09-30-2020      Yuxin Zhou              Modified comment(s),          */
76 /*                                            resulting in version 6.1    */
77 /*  04-25-2022      William E. Lamie        Modified comment(s), and      */
78 /*                                            added corrected preemption  */
79 /*                                            check logic, resulting in   */
80 /*                                            version 6.1.11              */
81 /*                                                                        */
82 /**************************************************************************/
_tx_event_flags_set(TX_EVENT_FLAGS_GROUP * group_ptr,ULONG flags_to_set,UINT set_option)83 UINT  _tx_event_flags_set(TX_EVENT_FLAGS_GROUP *group_ptr, ULONG flags_to_set, UINT set_option)
84 {
85 
86 TX_INTERRUPT_SAVE_AREA
87 
88 TX_THREAD       *thread_ptr;
89 TX_THREAD       *next_thread_ptr;
90 TX_THREAD       *next_thread;
91 TX_THREAD       *previous_thread;
92 TX_THREAD       *satisfied_list;
93 TX_THREAD       *last_satisfied;
94 TX_THREAD       *suspended_list;
95 UINT            suspended_count;
96 ULONG           current_event_flags;
97 ULONG           requested_flags;
98 ULONG           flags_satisfied;
99 ULONG           *suspend_info_ptr;
100 UINT            and_request;
101 UINT            get_option;
102 UINT            clear_request;
103 UINT            preempt_check;
104 #ifndef TX_NOT_INTERRUPTABLE
105 UINT            interrupted_set_request;
106 #endif
107 #ifndef TX_DISABLE_NOTIFY_CALLBACKS
108 VOID            (*events_set_notify)(struct TX_EVENT_FLAGS_GROUP_STRUCT *notify_group_ptr);
109 #endif
110 
111 
112     /* Disable interrupts to remove the semaphore from the created list.  */
113     TX_DISABLE
114 
115 #ifdef TX_EVENT_FLAGS_ENABLE_PERFORMANCE_INFO
116 
117     /* Increment the total event flags set counter.  */
118     _tx_event_flags_performance_set_count++;
119 
120     /* Increment the number of event flags sets on this semaphore.  */
121     group_ptr -> tx_event_flags_group_performance_set_count++;
122 #endif
123 
124     /* If trace is enabled, insert this event into the trace buffer.  */
125     TX_TRACE_IN_LINE_INSERT(TX_TRACE_EVENT_FLAGS_SET, group_ptr, flags_to_set, set_option, group_ptr -> tx_event_flags_group_suspended_count, TX_TRACE_EVENT_FLAGS_EVENTS)
126 
127     /* Log this kernel call.  */
128     TX_EL_EVENT_FLAGS_SET_INSERT
129 
130     /* Determine how to set this group's event flags.  */
131     if ((set_option & TX_EVENT_FLAGS_AND_MASK) == TX_AND)
132     {
133 
134 #ifndef TX_NOT_INTERRUPTABLE
135 
136         /* Set interrupted set request flag to false.  */
137         interrupted_set_request =  TX_FALSE;
138 
139         /* Determine if the suspension list is being processed by an interrupted
140            set request.  */
141         if (group_ptr -> tx_event_flags_group_suspended_count != TX_NO_SUSPENSIONS)
142         {
143 
144             if (group_ptr -> tx_event_flags_group_suspension_list == TX_NULL)
145             {
146 
147                 /* Set the interrupted set request flag.  */
148                 interrupted_set_request =  TX_TRUE;
149             }
150         }
151 
152         /* Was a set request interrupted?  */
153         if (interrupted_set_request == TX_TRUE)
154         {
155 
156             /* A previous set operation was interrupted, we need to defer the
157                event clearing until the set operation is complete.  */
158 
159             /* Remember the events to clear.  */
160             group_ptr -> tx_event_flags_group_delayed_clear =
161                                         group_ptr -> tx_event_flags_group_delayed_clear | ~flags_to_set;
162         }
163         else
164         {
165 #endif
166 
167             /* Previous set operation was not interrupted, simply clear the
168                specified flags by "ANDing" the flags into the current events
169                of the group.  */
170             group_ptr -> tx_event_flags_group_current =
171                 group_ptr -> tx_event_flags_group_current & flags_to_set;
172 
173 #ifndef TX_NOT_INTERRUPTABLE
174 
175         }
176 #endif
177 
178         /* Restore interrupts.  */
179         TX_RESTORE
180     }
181     else
182     {
183 
184 #ifndef TX_DISABLE_NOTIFY_CALLBACKS
185 
186         /* Pickup the notify callback routine for this event flag group.  */
187         events_set_notify =  group_ptr -> tx_event_flags_group_set_notify;
188 #endif
189 
190         /* "OR" the flags into the current events of the group.  */
191         group_ptr -> tx_event_flags_group_current =
192             group_ptr -> tx_event_flags_group_current | flags_to_set;
193 
194 #ifndef TX_NOT_INTERRUPTABLE
195 
196         /* Determine if there are any delayed flags to clear.  */
197         if (group_ptr -> tx_event_flags_group_delayed_clear != ((ULONG) 0))
198         {
199 
200             /* Yes, we need to neutralize the delayed clearing as well.  */
201             group_ptr -> tx_event_flags_group_delayed_clear =
202                                         group_ptr -> tx_event_flags_group_delayed_clear & ~flags_to_set;
203         }
204 #endif
205 
206         /* Clear the preempt check flag.  */
207         preempt_check =  TX_FALSE;
208 
209         /* Pickup the thread suspended count.  */
210         suspended_count =  group_ptr -> tx_event_flags_group_suspended_count;
211 
212         /* Determine if there are any threads suspended on the event flag group.  */
213         if (group_ptr -> tx_event_flags_group_suspension_list != TX_NULL)
214         {
215 
216             /* Determine if there is just a single thread waiting on the event
217                flag group.  */
218             if (suspended_count == ((UINT) 1))
219             {
220 
221                 /* Single thread waiting for event flags.  Bypass the multiple thread
222                    logic.  */
223 
224                 /* Setup thread pointer.  */
225                 thread_ptr =  group_ptr -> tx_event_flags_group_suspension_list;
226 
227                 /* Pickup the current event flags.  */
228                 current_event_flags =  group_ptr -> tx_event_flags_group_current;
229 
230                 /* Pickup the suspend information.  */
231                 requested_flags =  thread_ptr -> tx_thread_suspend_info;
232 
233                 /* Pickup the suspend option.  */
234                 get_option =  thread_ptr -> tx_thread_suspend_option;
235 
236                 /* Isolate the AND selection.  */
237                 and_request =  (get_option & TX_AND);
238 
239                 /* Check for AND condition. All flags must be present to satisfy request.  */
240                 if (and_request == TX_AND)
241                 {
242 
243                     /* AND request is present.  */
244 
245                     /* Calculate the flags present.  */
246                     flags_satisfied =  (current_event_flags & requested_flags);
247 
248                     /* Determine if they satisfy the AND request.  */
249                     if (flags_satisfied != requested_flags)
250                     {
251 
252                         /* No, not all the requested flags are present. Clear the flags present variable.  */
253                         flags_satisfied =  ((ULONG) 0);
254                     }
255                 }
256                 else
257                 {
258 
259                     /* OR request is present. Simply or the requested flags and the current flags.  */
260                     flags_satisfied =  (current_event_flags & requested_flags);
261                 }
262 
263                 /* Determine if the request is satisfied.  */
264                 if (flags_satisfied != ((ULONG) 0))
265                 {
266 
267                     /* Yes, resume the thread and apply any event flag
268                        clearing.  */
269 
270                     /* Return the actual event flags that satisfied the request.  */
271                     suspend_info_ptr =   TX_VOID_TO_ULONG_POINTER_CONVERT(thread_ptr -> tx_thread_additional_suspend_info);
272                     *suspend_info_ptr =  current_event_flags;
273 
274                     /* Pickup the clear bit.  */
275                     clear_request =  (get_option & TX_EVENT_FLAGS_CLEAR_MASK);
276 
277                     /* Determine whether or not clearing needs to take place.  */
278                     if (clear_request == TX_TRUE)
279                     {
280 
281                         /* Yes, clear the flags that satisfied this request.  */
282                         group_ptr -> tx_event_flags_group_current =  group_ptr -> tx_event_flags_group_current & (~requested_flags);
283                     }
284 
285                     /* Clear the suspension information in the event flag group.  */
286                     group_ptr -> tx_event_flags_group_suspension_list =  TX_NULL;
287                     group_ptr -> tx_event_flags_group_suspended_count =  TX_NO_SUSPENSIONS;
288 
289                     /* Clear cleanup routine to avoid timeout.  */
290                     thread_ptr -> tx_thread_suspend_cleanup =  TX_NULL;
291 
292                     /* Put return status into the thread control block.  */
293                     thread_ptr -> tx_thread_suspend_status =  TX_SUCCESS;
294 
295 #ifdef TX_NOT_INTERRUPTABLE
296 
297                     /* Resume the thread!  */
298                     _tx_thread_system_ni_resume(thread_ptr);
299 #else
300 
301                     /* Temporarily disable preemption.  */
302                     _tx_thread_preempt_disable++;
303 
304                     /* Restore interrupts.  */
305                     TX_RESTORE
306 
307                     /* Resume thread.  */
308                     _tx_thread_system_resume(thread_ptr);
309 
310                     /* Disable interrupts to remove the semaphore from the created list.  */
311                     TX_DISABLE
312 #endif
313                 }
314             }
315             else
316             {
317 
318                 /* Otherwise, the event flag requests of multiple threads must be
319                    examined.  */
320 
321                 /* Setup thread pointer, keep a local copy of the head pointer.  */
322                 suspended_list =  group_ptr -> tx_event_flags_group_suspension_list;
323                 thread_ptr =      suspended_list;
324 
325                 /* Clear the suspended list head pointer to thwart manipulation of
326                    the list in ISR's while we are processing here.  */
327                 group_ptr -> tx_event_flags_group_suspension_list =  TX_NULL;
328 
329                 /* Setup the satisfied thread pointers.  */
330                 satisfied_list =  TX_NULL;
331                 last_satisfied =  TX_NULL;
332 
333                 /* Pickup the current event flags.  */
334                 current_event_flags =  group_ptr -> tx_event_flags_group_current;
335 
336                 /* Disable preemption while we process the suspended list.  */
337                 _tx_thread_preempt_disable++;
338 
339                 /* Since we have temporarily disabled preemption globally, set the preempt
340                    check flag to check for any preemption condition - including from
341                    unrelated ISR processing.  */
342                 preempt_check =  TX_TRUE;
343 
344                 /* Loop to examine all of the suspended threads. */
345                 do
346                 {
347 
348 #ifndef TX_NOT_INTERRUPTABLE
349 
350                     /* Restore interrupts temporarily.  */
351                     TX_RESTORE
352 
353                     /* Disable interrupts again.  */
354                     TX_DISABLE
355 #endif
356 
357                     /* Determine if we need to reset the search.  */
358                     if (group_ptr -> tx_event_flags_group_reset_search != TX_FALSE)
359                     {
360 
361                         /* Clear the reset search flag.  */
362                         group_ptr -> tx_event_flags_group_reset_search =  TX_FALSE;
363 
364                         /* Move the thread pointer to the beginning of the search list.  */
365                         thread_ptr =  suspended_list;
366 
367                         /* Reset the suspended count.  */
368                         suspended_count =  group_ptr -> tx_event_flags_group_suspended_count;
369 
370                         /* Update the current events with any new ones that might
371                            have been set in a nested set events call from an ISR.  */
372                         current_event_flags =  current_event_flags | group_ptr -> tx_event_flags_group_current;
373                     }
374 
375                     /* Save next thread pointer.  */
376                     next_thread_ptr =  thread_ptr -> tx_thread_suspended_next;
377 
378                     /* Pickup the suspend information.  */
379                     requested_flags =  thread_ptr -> tx_thread_suspend_info;
380 
381                     /* Pickup this thread's suspension get option.  */
382                     get_option =  thread_ptr -> tx_thread_suspend_option;
383 
384                     /* Isolate the AND selection.  */
385                     and_request =  (get_option & TX_AND);
386 
387                     /* Check for AND condition. All flags must be present to satisfy request.  */
388                     if (and_request == TX_AND)
389                     {
390 
391                         /* AND request is present.  */
392 
393                         /* Calculate the flags present.  */
394                         flags_satisfied =  (current_event_flags & requested_flags);
395 
396                         /* Determine if they satisfy the AND request.  */
397                         if (flags_satisfied != requested_flags)
398                         {
399 
400                             /* No, not all the requested flags are present. Clear the flags present variable.  */
401                             flags_satisfied =  ((ULONG) 0);
402                         }
403                     }
404                     else
405                     {
406 
407                         /* OR request is present. Simply or the requested flags and the current flags.  */
408                         flags_satisfied =  (current_event_flags & requested_flags);
409                     }
410 
411                     /* Check to see if the thread had a timeout or wait abort during the event search processing.
412                        If so, just set the flags satisfied to ensure the processing here removes the thread from
413                        the suspension list.  */
414                     if (thread_ptr -> tx_thread_state != TX_EVENT_FLAG)
415                     {
416 
417                        /* Simply set the satisfied flags to 1 in order to remove the thread from the suspension list.  */
418                         flags_satisfied =  ((ULONG) 1);
419                     }
420 
421                     /* Determine if the request is satisfied.  */
422                     if (flags_satisfied != ((ULONG) 0))
423                     {
424 
425                         /* Yes, this request can be handled now.  */
426 
427                         /* Determine if the thread is still suspended on the event flag group. If not, a wait
428                            abort must have been done from an ISR.  */
429                         if (thread_ptr -> tx_thread_state == TX_EVENT_FLAG)
430                         {
431 
432                             /* Return the actual event flags that satisfied the request.  */
433                             suspend_info_ptr =   TX_VOID_TO_ULONG_POINTER_CONVERT(thread_ptr -> tx_thread_additional_suspend_info);
434                             *suspend_info_ptr =  current_event_flags;
435 
436                             /* Pickup the clear bit.  */
437                             clear_request =  (get_option & TX_EVENT_FLAGS_CLEAR_MASK);
438 
439                             /* Determine whether or not clearing needs to take place.  */
440                             if (clear_request == TX_TRUE)
441                             {
442 
443                                 /* Yes, clear the flags that satisfied this request.  */
444                                 group_ptr -> tx_event_flags_group_current =  group_ptr -> tx_event_flags_group_current & ~requested_flags;
445                             }
446 
447                             /* Prepare for resumption of the first thread.  */
448 
449                             /* Clear cleanup routine to avoid timeout.  */
450                             thread_ptr -> tx_thread_suspend_cleanup =  TX_NULL;
451 
452                             /* Put return status into the thread control block.  */
453                             thread_ptr -> tx_thread_suspend_status =  TX_SUCCESS;
454                         }
455 
456                         /* We need to remove the thread from the suspension list and place it in the
457                            expired list.  */
458 
459                         /* See if this is the only suspended thread on the list.  */
460                         if (thread_ptr == thread_ptr -> tx_thread_suspended_next)
461                         {
462 
463                             /* Yes, the only suspended thread.  */
464 
465                             /* Update the head pointer.  */
466                             suspended_list =  TX_NULL;
467                         }
468                         else
469                         {
470 
471                             /* At least one more thread is on the same expiration list.  */
472 
473                             /* Update the links of the adjacent threads.  */
474                             next_thread =                                  thread_ptr -> tx_thread_suspended_next;
475                             previous_thread =                              thread_ptr -> tx_thread_suspended_previous;
476                             next_thread -> tx_thread_suspended_previous =  previous_thread;
477                             previous_thread -> tx_thread_suspended_next =  next_thread;
478 
479                             /* Update the list head pointer, if removing the head of the
480                                list.  */
481                             if (suspended_list == thread_ptr)
482                             {
483 
484                                 /* Yes, head pointer needs to be updated.  */
485                                 suspended_list =  thread_ptr -> tx_thread_suspended_next;
486                             }
487                         }
488 
489                         /* Decrement the suspension count.  */
490                         group_ptr -> tx_event_flags_group_suspended_count--;
491 
492                         /* Place this thread on the expired list.  */
493                         if (satisfied_list == TX_NULL)
494                         {
495 
496                             /* First thread on the satisfied list.  */
497                             satisfied_list =  thread_ptr;
498                             last_satisfied =  thread_ptr;
499 
500                             /* Setup initial next pointer.  */
501                             thread_ptr -> tx_thread_suspended_next =  TX_NULL;
502                         }
503                         else
504                         {
505 
506                             /* Not the first thread on the satisfied list.  */
507 
508                             /* Link it up at the end.  */
509                             last_satisfied -> tx_thread_suspended_next =  thread_ptr;
510                             thread_ptr -> tx_thread_suspended_next =      TX_NULL;
511                             last_satisfied =                              thread_ptr;
512                         }
513                     }
514 
515                     /* Copy next thread pointer to working thread ptr.  */
516                     thread_ptr =  next_thread_ptr;
517 
518                     /* Decrement the suspension count.  */
519                     suspended_count--;
520 
521                 } while (suspended_count != TX_NO_SUSPENSIONS);
522 
523                 /* Setup the group's suspension list head again.  */
524                 group_ptr -> tx_event_flags_group_suspension_list =  suspended_list;
525 
526 #ifndef TX_NOT_INTERRUPTABLE
527 
528                 /* Determine if there is any delayed event clearing to perform.  */
529                 if (group_ptr -> tx_event_flags_group_delayed_clear != ((ULONG) 0))
530                 {
531 
532                     /* Perform the delayed event clearing.  */
533                     group_ptr -> tx_event_flags_group_current =
534                         group_ptr -> tx_event_flags_group_current & ~(group_ptr -> tx_event_flags_group_delayed_clear);
535 
536                     /* Clear the delayed event flag clear value.  */
537                     group_ptr -> tx_event_flags_group_delayed_clear =  ((ULONG) 0);
538                 }
539 #endif
540 
541                 /* Restore interrupts.  */
542                 TX_RESTORE
543 
544                 /* Walk through the satisfied list, setup initial thread pointer. */
545                 thread_ptr =  satisfied_list;
546                 while(thread_ptr != TX_NULL)
547                 {
548 
549                     /* Get next pointer first.  */
550                     next_thread_ptr =  thread_ptr -> tx_thread_suspended_next;
551 
552                     /* Disable interrupts.  */
553                     TX_DISABLE
554 
555 #ifdef TX_NOT_INTERRUPTABLE
556 
557                     /* Resume the thread!  */
558                     _tx_thread_system_ni_resume(thread_ptr);
559 
560                     /* Restore interrupts.  */
561                     TX_RESTORE
562 #else
563 
564                     /* Disable preemption again.  */
565                     _tx_thread_preempt_disable++;
566 
567                     /* Restore interrupt posture.  */
568                     TX_RESTORE
569 
570                     /* Resume the thread.  */
571                     _tx_thread_system_resume(thread_ptr);
572 #endif
573 
574                     /* Move next thread to current.  */
575                     thread_ptr =  next_thread_ptr;
576                 }
577 
578                 /* Disable interrupts.  */
579                 TX_DISABLE
580 
581                 /* Release thread preemption disable.  */
582                 _tx_thread_preempt_disable--;
583             }
584         }
585         else
586         {
587 
588             /* Determine if we need to set the reset search field.  */
589             if (group_ptr -> tx_event_flags_group_suspended_count != TX_NO_SUSPENSIONS)
590             {
591 
592                 /* We interrupted a search of an event flag group suspension
593                    list.  Make sure we reset the search.  */
594                 group_ptr -> tx_event_flags_group_reset_search =  TX_TRUE;
595             }
596         }
597 
598         /* Restore interrupts.  */
599         TX_RESTORE
600 
601 #ifndef TX_DISABLE_NOTIFY_CALLBACKS
602 
603         /* Determine if a notify callback is required.  */
604         if (events_set_notify != TX_NULL)
605         {
606 
607             /* Call application event flags set notification.  */
608             (events_set_notify)(group_ptr);
609         }
610 #endif
611 
612         /* Determine if a check for preemption is necessary.  */
613         if (preempt_check == TX_TRUE)
614         {
615 
616             /* Yes, one or more threads were resumed, check for preemption.  */
617             _tx_thread_system_preempt_check();
618         }
619     }
620 
621     /* Return completion status.  */
622     return(TX_SUCCESS);
623 }
624 
625