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 /** Timer */
18 /** */
19 /**************************************************************************/
20 /**************************************************************************/
21
22 #define TX_SOURCE_CODE
23 #define TX_THREAD_SMP_SOURCE_CODE
24
25
26 /* Include necessary system files. */
27
28 #include "tx_api.h"
29 #include "tx_timer.h"
30 #include "tx_thread.h"
31
32
33 /**************************************************************************/
34 /* */
35 /* FUNCTION RELEASE */
36 /* */
37 /* _tx_timer_thread_entry PORTABLE SMP */
38 /* 6.1 */
39 /* AUTHOR */
40 /* */
41 /* William E. Lamie, Microsoft Corporation */
42 /* */
43 /* DESCRIPTION */
44 /* */
45 /* This function manages thread and application timer expirations. */
46 /* Actually, from this thread's point of view, there is no difference. */
47 /* */
48 /* INPUT */
49 /* */
50 /* timer_thread_input Used just for verification */
51 /* */
52 /* OUTPUT */
53 /* */
54 /* None */
55 /* */
56 /* CALLS */
57 /* */
58 /* Timer Expiration Function */
59 /* _tx_thread_system_suspend Thread suspension */
60 /* _tx_thread_system_ni_suspend Non-interruptable suspend thread */
61 /* _tx_timer_system_activate Timer reactivate processing */
62 /* _tx_thread_smp_core_exclude Exclude core from timer execution */
63 /* */
64 /* CALLED BY */
65 /* */
66 /* ThreadX Scheduler */
67 /* */
68 /* RELEASE HISTORY */
69 /* */
70 /* DATE NAME DESCRIPTION */
71 /* */
72 /* 09-30-2020 William E. Lamie Initial Version 6.1 */
73 /* */
74 /**************************************************************************/
75 #ifndef TX_TIMER_PROCESS_IN_ISR
_tx_timer_thread_entry(ULONG timer_thread_input)76 VOID _tx_timer_thread_entry(ULONG timer_thread_input)
77 {
78
79 TX_INTERRUPT_SAVE_AREA
80
81 TX_TIMER_INTERNAL *expired_timers;
82 TX_TIMER_INTERNAL *reactivate_timer;
83 TX_TIMER_INTERNAL *next_timer;
84 TX_TIMER_INTERNAL *previous_timer;
85 TX_TIMER_INTERNAL *current_timer;
86 VOID (*timeout_function)(ULONG id);
87 ULONG timeout_param = ((ULONG) 0);
88 TX_THREAD *thread_ptr;
89 #ifdef TX_REACTIVATE_INLINE
90 TX_TIMER_INTERNAL **timer_list; /* Timer list pointer */
91 UINT expiration_time; /* Value used for pointer offset*/
92 ULONG delta;
93 #endif
94 #ifdef TX_TIMER_ENABLE_PERFORMANCE_INFO
95 UCHAR *working_ptr;
96 TX_TIMER *timer_ptr;
97 #endif
98 ULONG core_exclusion;
99 #ifdef TX_MISRA_ENABLE
100 UINT status;
101 #endif
102
103
104 /* Make sure the timer input is correct. This also gets rid of the
105 silly compiler warnings. */
106 if (timer_thread_input == TX_TIMER_ID)
107 {
108
109 /* Yes, valid thread entry, proceed... */
110
111 /* Now go into an infinite loop to process timer expirations. */
112 while (TX_LOOP_FOREVER)
113 {
114
115 /* First, move the current list pointer and clear the timer
116 expired value. This allows the interrupt handling portion
117 to continue looking for timer expirations. */
118 TX_DISABLE
119
120 /* Save the current timer expiration list pointer. */
121 expired_timers = *_tx_timer_current_ptr;
122
123 /* Modify the head pointer in the first timer in the list, if there
124 is one! */
125 if (expired_timers != TX_NULL)
126 {
127
128 expired_timers -> tx_timer_internal_list_head = &expired_timers;
129
130 /* Pickup the current core exclusion bit map for this timer. */
131 core_exclusion = expired_timers -> tx_timer_internal_smp_cores_excluded;
132 }
133 else
134 {
135
136 /* Pickup the current core exclusion bit map for the timer thread. */
137 core_exclusion = _tx_timer_thread.tx_thread_smp_cores_excluded;
138 }
139
140 /* Set the current list pointer to NULL. */
141 *_tx_timer_current_ptr = TX_NULL;
142
143 /* Move the current pointer up one timer entry wrap if we get to
144 the end of the list. */
145 _tx_timer_current_ptr = TX_TIMER_POINTER_ADD(_tx_timer_current_ptr, 1);
146 if (_tx_timer_current_ptr == _tx_timer_list_end)
147 {
148
149 _tx_timer_current_ptr = _tx_timer_list_start;
150 }
151
152 /* Clear the expired flag. */
153 _tx_timer_expired = TX_FALSE;
154
155 /* Restore interrupts temporarily. */
156 TX_RESTORE
157
158 /* Determine if we need to change the processor exclusion setting. */
159 if ((_tx_timer_thread.tx_thread_smp_cores_excluded & ((ULONG) TX_THREAD_SMP_CORE_MASK)) != core_exclusion)
160 {
161
162 #ifdef TX_MISRA_ENABLE
163 do
164 {
165
166 /* Setup the new core exclusion for execution this timer. */
167 status = _tx_thread_smp_core_exclude(&_tx_timer_thread, core_exclusion);
168 } while (status != TX_SUCCESS);
169 #else
170 /* Setup the new core exclusion for execution this timer. */
171 _tx_thread_smp_core_exclude(&_tx_timer_thread, core_exclusion);
172 #endif
173
174 /* When we get to this point, the system timer thread is executing on one of the allowed cores. */
175 }
176
177 /* Disable interrupts again. */
178 TX_DISABLE
179
180 /* Next, process the expiration of the associated timers at this
181 time slot. */
182 while (expired_timers != TX_NULL)
183 {
184
185 /* Something is on the list. Remove it and process the expiration. */
186 current_timer = expired_timers;
187
188 /* Pickup the next timer. */
189 next_timer = expired_timers -> tx_timer_internal_active_next;
190
191 /* Set the reactivate_timer to NULL. */
192 reactivate_timer = TX_NULL;
193
194 /* Determine if this is the only timer. */
195 if (current_timer == next_timer)
196 {
197
198 /* Yes, this is the only timer in the list. */
199
200 /* Set the head pointer to NULL. */
201 expired_timers = TX_NULL;
202
203 /* Pickup the excluded core(s) for the timer thread. */
204 core_exclusion = _tx_timer_thread.tx_thread_smp_cores_excluded;
205 }
206 else
207 {
208
209 /* No, not the only expired timer. */
210
211 /* Remove this timer from the expired list. */
212 previous_timer = current_timer -> tx_timer_internal_active_previous;
213 next_timer -> tx_timer_internal_active_previous = previous_timer;
214 previous_timer -> tx_timer_internal_active_next = next_timer;
215
216 /* Modify the next timer's list head to point at the current list head. */
217 next_timer -> tx_timer_internal_list_head = &expired_timers;
218
219 /* Set the list head pointer. */
220 expired_timers = next_timer;
221
222 /* Pickup the excluded core(s) for this timer. */
223 core_exclusion = ((expired_timers -> tx_timer_internal_smp_cores_excluded) & ((ULONG) TX_THREAD_SMP_CORE_MASK));
224 }
225
226 /* In any case, the timer is now off of the expired list. */
227
228 /* Determine if the timer has expired or if it is just a really
229 big timer that needs to be placed in the list again. */
230 if (current_timer -> tx_timer_internal_remaining_ticks > TX_TIMER_ENTRIES)
231 {
232
233 /* Timer is bigger than the timer entries and must be
234 rescheduled. */
235
236 #ifdef TX_TIMER_ENABLE_PERFORMANCE_INFO
237
238 /* Increment the total expiration adjustments counter. */
239 _tx_timer_performance__expiration_adjust_count++;
240
241 /* Determine if this is an application timer. */
242 if (current_timer -> tx_timer_internal_timeout_function != &_tx_thread_timeout)
243 {
244
245 /* Derive the application timer pointer. */
246
247 /* Pickup the application timer pointer. */
248 TX_USER_TIMER_POINTER_GET(current_timer, timer_ptr)
249
250 /* Increment the number of expiration adjustments on this timer. */
251 if (timer_ptr -> tx_timer_id == TX_TIMER_ID)
252 {
253
254 timer_ptr -> tx_timer_performance__expiration_adjust_count++;
255 }
256 }
257 #endif
258
259 /* Decrement the remaining ticks of the timer. */
260 current_timer -> tx_timer_internal_remaining_ticks =
261 current_timer -> tx_timer_internal_remaining_ticks - TX_TIMER_ENTRIES;
262
263 /* Set the timeout function to NULL in order to bypass the
264 expiration. */
265 timeout_function = TX_NULL;
266
267 /* Make the timer appear that it is still active while interrupts
268 are enabled. This will permit proper processing of a timer
269 deactivate from an ISR. */
270 current_timer -> tx_timer_internal_list_head = &reactivate_timer;
271 current_timer -> tx_timer_internal_active_next = current_timer;
272
273 /* Setup the temporary timer list head pointer. */
274 reactivate_timer = current_timer;
275 }
276 else
277 {
278
279 /* Timer did expire. */
280
281 #ifdef TX_TIMER_ENABLE_PERFORMANCE_INFO
282
283 /* Increment the total expirations counter. */
284 _tx_timer_performance_expiration_count++;
285
286 /* Determine if this is an application timer. */
287 if (current_timer -> tx_timer_internal_timeout_function != &_tx_thread_timeout)
288 {
289
290 /* Derive the application timer pointer. */
291
292 /* Pickup the application timer pointer. */
293 TX_USER_TIMER_POINTER_GET(current_timer, timer_ptr)
294
295 /* Increment the number of expirations on this timer. */
296 if (timer_ptr -> tx_timer_id == TX_TIMER_ID)
297 {
298
299 timer_ptr -> tx_timer_performance_expiration_count++;
300 }
301 }
302 #endif
303
304 /* Copy the calling function and ID into local variables before interrupts
305 are re-enabled. */
306 timeout_function = current_timer -> tx_timer_internal_timeout_function;
307 timeout_param = current_timer -> tx_timer_internal_timeout_param;
308
309 /* Copy the reinitialize ticks into the remaining ticks. */
310 current_timer -> tx_timer_internal_remaining_ticks = current_timer -> tx_timer_internal_re_initialize_ticks;
311
312 /* Determine if the timer should be reactivated. */
313 if (current_timer -> tx_timer_internal_remaining_ticks != ((ULONG) 0))
314 {
315
316 /* Make the timer appear that it is still active while processing
317 the expiration routine and with interrupts enabled. This will
318 permit proper processing of a timer deactivate from both the
319 expiration routine and an ISR. */
320 current_timer -> tx_timer_internal_list_head = &reactivate_timer;
321 current_timer -> tx_timer_internal_active_next = current_timer;
322
323 /* Setup the temporary timer list head pointer. */
324 reactivate_timer = current_timer;
325 }
326 else
327 {
328
329 /* Set the list pointer of this timer to NULL. This is used to indicate
330 the timer is no longer active. */
331 current_timer -> tx_timer_internal_list_head = TX_NULL;
332 }
333 }
334
335 /* Set pointer to indicate the expired timer that is currently being processed. */
336 _tx_timer_expired_timer_ptr = current_timer;
337
338 #ifndef TX_PROCESS_TIMER_WITH_PROTECTION
339
340 /* Restore interrupts for timer expiration call. */
341 TX_RESTORE
342 #endif
343
344 /* Call the timer-expiration function, if non-NULL. */
345 if (timeout_function != TX_NULL)
346 {
347
348 (timeout_function) (timeout_param);
349 }
350
351 #ifndef TX_PROCESS_TIMER_WITH_PROTECTION
352
353 /* Lockout interrupts again. */
354 TX_DISABLE
355 #endif
356
357 /* Clear expired timer pointer. */
358 _tx_timer_expired_timer_ptr = TX_NULL;
359
360 /* Determine if the timer needs to be reactivated. */
361 if (reactivate_timer == current_timer)
362 {
363
364 /* Reactivate the timer. */
365
366 #ifdef TX_TIMER_ENABLE_PERFORMANCE_INFO
367
368 /* Determine if this timer expired. */
369 if (timeout_function != TX_NULL)
370 {
371
372 /* Increment the total reactivations counter. */
373 _tx_timer_performance_reactivate_count++;
374
375 /* Determine if this is an application timer. */
376 if (current_timer -> tx_timer_internal_timeout_function != &_tx_thread_timeout)
377 {
378
379 /* Derive the application timer pointer. */
380
381 /* Pickup the application timer pointer. */
382 TX_USER_TIMER_POINTER_GET(current_timer, timer_ptr)
383
384 /* Increment the number of expirations on this timer. */
385 if (timer_ptr -> tx_timer_id == TX_TIMER_ID)
386 {
387
388 timer_ptr -> tx_timer_performance_reactivate_count++;
389 }
390 }
391 }
392 #endif
393
394 #ifdef TX_REACTIVATE_INLINE
395
396 /* Calculate the amount of time remaining for the timer. */
397 if (current_timer -> tx_timer_internal_remaining_ticks > TX_TIMER_ENTRIES)
398 {
399
400 /* Set expiration time to the maximum number of entries. */
401 expiration_time = TX_TIMER_ENTRIES - ((UINT) 1);
402 }
403 else
404 {
405
406 /* Timer value fits in the timer entries. */
407
408 /* Set the expiration time. */
409 expiration_time = ((UINT) current_timer -> tx_timer_internal_remaining_ticks) - ((UINT) 1);
410 }
411
412 /* At this point, we are ready to put the timer back on one of
413 the timer lists. */
414
415 /* Calculate the proper place for the timer. */
416 timer_list = TX_TIMER_POINTER_ADD(_tx_timer_current_ptr, expiration_time);
417 if (TX_TIMER_INDIRECT_TO_VOID_POINTER_CONVERT(timer_list) >= TX_TIMER_INDIRECT_TO_VOID_POINTER_CONVERT(_tx_timer_list_end))
418 {
419
420 /* Wrap from the beginning of the list. */
421 delta = TX_TIMER_POINTER_DIF(timer_list, _tx_timer_list_end);
422 timer_list = TX_TIMER_POINTER_ADD(_tx_timer_list_start, delta);
423 }
424
425 /* Now put the timer on this list. */
426 if ((*timer_list) == TX_NULL)
427 {
428
429 /* This list is NULL, just put the new timer on it. */
430
431 /* Setup the links in this timer. */
432 current_timer -> tx_timer_internal_active_next = current_timer;
433 current_timer -> tx_timer_internal_active_previous = current_timer;
434
435 /* Setup the list head pointer. */
436 *timer_list = current_timer;
437 }
438 else
439 {
440
441 /* This list is not NULL, add current timer to the end. */
442 next_timer = *timer_list;
443 previous_timer = next_timer -> tx_timer_internal_active_previous;
444 previous_timer -> tx_timer_internal_active_next = current_timer;
445 next_timer -> tx_timer_internal_active_previous = current_timer;
446 current_timer -> tx_timer_internal_active_next = next_timer;
447 current_timer -> tx_timer_internal_active_previous = previous_timer;
448 }
449
450 /* Setup list head pointer. */
451 current_timer -> tx_timer_internal_list_head = timer_list;
452 #else
453
454 /* Reactivate through the timer activate function. */
455
456 /* Clear the list head for the timer activate call. */
457 current_timer -> tx_timer_internal_list_head = TX_NULL;
458
459 /* Activate the current timer. */
460 _tx_timer_system_activate(current_timer);
461 #endif
462 }
463
464 /* Restore interrupts. */
465 TX_RESTORE
466
467 /* Determine if we need to change the core exclusion setting. */
468 if (_tx_timer_thread.tx_thread_smp_cores_excluded != core_exclusion)
469 {
470
471 #ifdef TX_MISRA_ENABLE
472 do
473 {
474
475 /* Setup the new core exclusion for execution this timer. */
476 status = _tx_thread_smp_core_exclude(&_tx_timer_thread, core_exclusion);
477 } while (status != TX_SUCCESS);
478 #else
479
480 /* Setup the new core exclusion for execution this timer. */
481 _tx_thread_smp_core_exclude(&_tx_timer_thread, core_exclusion);
482 #endif
483
484 /* When we get to this point, the system timer thread is executing on one of the allowed cores. */
485 }
486
487 /* Lockout interrupts again. */
488 TX_DISABLE
489 }
490
491 /* Finally, suspend this thread and wait for the next expiration. */
492
493 /* Determine if another expiration took place while we were in this
494 thread. If so, process another expiration. */
495 if (_tx_timer_expired == TX_FALSE)
496 {
497
498 /* Otherwise, no timer expiration, so suspend the thread. */
499
500 /* Build pointer to the timer thread. */
501 thread_ptr = &_tx_timer_thread;
502
503 /* Set the status to suspending, in order to indicate the
504 suspension is in progress. */
505 thread_ptr -> tx_thread_state = TX_SUSPENDED;
506
507 #ifdef TX_NOT_INTERRUPTABLE
508
509 /* Call actual non-interruptable thread suspension routine. */
510 _tx_thread_system_ni_suspend(thread_ptr, ((ULONG) 0));
511
512 /* Restore interrupts. */
513 TX_RESTORE
514 #else
515
516 /* Set the suspending flag. */
517 thread_ptr -> tx_thread_suspending = TX_TRUE;
518
519 /* Increment the preempt disable count prior to suspending. */
520 _tx_thread_preempt_disable++;
521
522 /* Restore interrupts. */
523 TX_RESTORE
524
525 /* Call actual thread suspension routine. */
526 _tx_thread_system_suspend(thread_ptr);
527 #endif
528 }
529 else
530 {
531
532 /* Restore interrupts. */
533 TX_RESTORE
534 }
535 }
536 }
537
538 #ifdef TX_SAFETY_CRITICAL
539
540 /* If we ever get here, raise safety critical exception. */
541 TX_SAFETY_CRITICAL_EXCEPTION(__FILE__, __LINE__, 0);
542 #endif
543
544 }
545 #endif
546
547