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