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