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