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