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