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 
24 #ifndef TX_NO_TIMER
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_expiration_process                        PORTABLE C      */
38 /*                                                           6.1          */
39 /*  AUTHOR                                                                */
40 /*                                                                        */
41 /*    William E. Lamie, Microsoft Corporation                             */
42 /*                                                                        */
43 /*  DESCRIPTION                                                           */
44 /*                                                                        */
45 /*    This function processes thread and application timer expirations.   */
46 /*    It is called from the _tx_timer_interrupt handler and either        */
47 /*    processes the timer expiration in the ISR or defers to the system   */
48 /*    timer thread. The actual processing is determined during            */
49 /*    compilation.                                                        */
50 /*                                                                        */
51 /*  INPUT                                                                 */
52 /*                                                                        */
53 /*    None                                                                */
54 /*                                                                        */
55 /*  OUTPUT                                                                */
56 /*                                                                        */
57 /*    None                                                                */
58 /*                                                                        */
59 /*  CALLS                                                                 */
60 /*                                                                        */
61 /*    _tx_thread_system_resume          Thread resume processing          */
62 /*    _tx_thread_system_ni_resume       Non-interruptable resume thread   */
63 /*    _tx_timer_system_activate         Timer reactivate processing       */
64 /*    Timer Expiration Function                                           */
65 /*                                                                        */
66 /*  CALLED BY                                                             */
67 /*                                                                        */
68 /*    _tx_timer_interrupt               Timer interrupt handler           */
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     Scott Larson             Modified comment(s), and      */
76 /*                                            opt out of function when    */
77 /*                                            TX_NO_TIMER is defined,     */
78 /*                                            resulting in version 6.1    */
79 /*                                                                        */
80 /**************************************************************************/
_tx_timer_expiration_process(VOID)81 VOID  _tx_timer_expiration_process(VOID)
82 {
83 
84 TX_INTERRUPT_SAVE_AREA
85 
86 #ifdef TX_TIMER_PROCESS_IN_ISR
87 
88 TX_TIMER_INTERNAL           *expired_timers;
89 TX_TIMER_INTERNAL           *reactivate_timer;
90 TX_TIMER_INTERNAL           *next_timer;
91 TX_TIMER_INTERNAL           *previous_timer;
92 #ifdef TX_REACTIVATE_INLINE
93 TX_TIMER_INTERNAL           **timer_list;               /* Timer list pointer           */
94 UINT                        expiration_time;            /* Value used for pointer offset*/
95 ULONG                       delta;
96 #endif
97 TX_TIMER_INTERNAL           *current_timer;
98 VOID                        (*timeout_function)(ULONG id);
99 ULONG                       timeout_param =  ((ULONG) 0);
100 #ifdef TX_TIMER_ENABLE_PERFORMANCE_INFO
101 TX_TIMER                    *timer_ptr;
102 #endif
103 #endif
104 
105 #ifndef TX_TIMER_PROCESS_IN_ISR
106 
107     /* Don't process in the ISR, wakeup the system timer thread to process the
108        timer expiration.  */
109 
110     /* Disable interrupts.  */
111     TX_DISABLE
112 
113 #ifdef TX_NOT_INTERRUPTABLE
114 
115     /* Resume the thread!  */
116     _tx_thread_system_ni_resume(&_tx_timer_thread);
117 
118     /* Restore interrupts.  */
119     TX_RESTORE
120 #else
121 
122     /* Increment the preempt disable flag.  */
123     _tx_thread_preempt_disable++;
124 
125     /* Restore interrupts.  */
126     TX_RESTORE
127 
128     /* Call the system resume function to activate the timer thread.  */
129     _tx_thread_system_resume(&_tx_timer_thread);
130 #endif
131 
132 #else
133 
134     /* Process the timer expiration directly in the ISR. This increases the interrupt
135        processing, however, it eliminates the need for a system timer thread and associated
136        resources.  */
137 
138     /* Disable interrupts.  */
139     TX_DISABLE
140 
141     /* Determine if the timer processing is already active.  This needs to be checked outside
142        of the processing loop because it remains set throughout nested timer interrupt conditions.  */
143     if (_tx_timer_processing_active == TX_FALSE)
144     {
145 
146         /* Timer processing is not nested.  */
147 
148         /* Determine if the timer expiration has already been cleared.  */
149         if (_tx_timer_expired != ((UINT) 0))
150         {
151 
152             /* Proceed with timer processing.  */
153 
154             /* Set the timer interrupt processing active flag.  */
155             _tx_timer_processing_active =  TX_TRUE;
156 
157             /* Now go into an infinite loop to process timer expirations.  */
158             do
159             {
160 
161                 /* First, move the current list pointer and clear the timer
162                    expired value.  This allows the interrupt handling portion
163                    to continue looking for timer expirations.  */
164 
165                 /* Save the current timer expiration list pointer.  */
166                 expired_timers =  *_tx_timer_current_ptr;
167 
168                 /* Modify the head pointer in the first timer in the list, if there
169                    is one!  */
170                 if (expired_timers != TX_NULL)
171                 {
172 
173                     expired_timers -> tx_timer_internal_list_head =  &expired_timers;
174                 }
175 
176                 /* Set the current list pointer to NULL.  */
177                 *_tx_timer_current_ptr =  TX_NULL;
178 
179                 /* Move the current pointer up one timer entry wrap if we get to
180                    the end of the list.  */
181                 _tx_timer_current_ptr =  TX_TIMER_POINTER_ADD(_tx_timer_current_ptr, 1);
182                 if (_tx_timer_current_ptr == _tx_timer_list_end)
183                 {
184 
185                     _tx_timer_current_ptr =  _tx_timer_list_start;
186                 }
187 
188                 /* Clear the expired flag.  */
189                 _tx_timer_expired =  TX_FALSE;
190 
191                 /* Restore interrupts temporarily.  */
192                 TX_RESTORE
193 
194                 /* Disable interrupts again.  */
195                 TX_DISABLE
196 
197                 /* Next, process the expiration of the associated timers at this
198                    time slot.  */
199                 while (expired_timers != TX_NULL)
200                 {
201 
202                     /* Something is on the list.  Remove it and process the expiration.  */
203                     current_timer =  expired_timers;
204 
205                     /* Pickup the next timer.  */
206                     next_timer =  expired_timers -> tx_timer_internal_active_next;
207 
208                     /* Set the reactivate timer to NULL.  */
209                     reactivate_timer =  TX_NULL;
210 
211                     /* Determine if this is the only timer.  */
212                     if (current_timer == next_timer)
213                     {
214 
215                         /* Yes, this is the only timer in the list.  */
216 
217                         /* Set the head pointer to NULL.  */
218                         expired_timers =  TX_NULL;
219                     }
220                     else
221                     {
222 
223                         /* No, not the only expired timer.  */
224 
225                         /* Remove this timer from the expired list.  */
226                         previous_timer =                                   current_timer -> tx_timer_internal_active_previous;
227                         next_timer -> tx_timer_internal_active_previous =  previous_timer;
228                         previous_timer -> tx_timer_internal_active_next =  next_timer;
229 
230                         /* Modify the next timer's list head to point at the current list head.  */
231                         next_timer -> tx_timer_internal_list_head =  &expired_timers;
232 
233                         /* Set the list head pointer.  */
234                         expired_timers =  next_timer;
235                     }
236 
237                     /* In any case, the timer is now off of the expired list.  */
238 
239                     /* Determine if the timer has expired or if it is just a really
240                        big timer that needs to be placed in the list again.  */
241                     if (current_timer -> tx_timer_internal_remaining_ticks > TX_TIMER_ENTRIES)
242                     {
243 
244                         /* Timer is bigger than the timer entries and must be
245                            rescheduled.  */
246 
247 #ifdef TX_TIMER_ENABLE_PERFORMANCE_INFO
248 
249                         /* Increment the total expiration adjustments counter.  */
250                         _tx_timer_performance__expiration_adjust_count++;
251 
252                         /* Determine if this is an application timer.  */
253                         if (current_timer -> tx_timer_internal_timeout_function != &_tx_thread_timeout)
254                         {
255 
256                             /* Derive the application timer pointer.  */
257 
258                             /* Pickup the application timer pointer.  */
259                             TX_USER_TIMER_POINTER_GET(current_timer, timer_ptr)
260 
261                             /* Increment the number of expiration adjustments on this timer.  */
262                             if (timer_ptr -> tx_timer_id == TX_TIMER_ID)
263                             {
264 
265                                 timer_ptr -> tx_timer_performance__expiration_adjust_count++;
266                             }
267                         }
268 #endif
269 
270                         /* Decrement the remaining ticks of the timer.  */
271                         current_timer -> tx_timer_internal_remaining_ticks =
272                                 current_timer -> tx_timer_internal_remaining_ticks - TX_TIMER_ENTRIES;
273 
274                         /* Set the timeout function to NULL in order to bypass the
275                            expiration.  */
276                         timeout_function =  TX_NULL;
277 
278                         /* Make the timer appear that it is still active while interrupts
279                            are enabled.  This will permit proper processing of a timer
280                            deactivate from an ISR.  */
281                         current_timer -> tx_timer_internal_list_head =    &reactivate_timer;
282                         current_timer -> tx_timer_internal_active_next =  current_timer;
283 
284                         /* Setup the temporary timer list head pointer.  */
285                         reactivate_timer =  current_timer;
286                     }
287                     else
288                     {
289 
290                         /* Timer did expire.  */
291 
292 #ifdef TX_TIMER_ENABLE_PERFORMANCE_INFO
293 
294                         /* Increment the total expirations counter.  */
295                         _tx_timer_performance_expiration_count++;
296 
297                         /* Determine if this is an application timer.  */
298                         if (current_timer -> tx_timer_internal_timeout_function != &_tx_thread_timeout)
299                         {
300 
301                             /* Derive the application timer pointer.  */
302 
303                             /* Pickup the application timer pointer.  */
304                             TX_USER_TIMER_POINTER_GET(current_timer, timer_ptr)
305 
306                             /* Increment the number of expirations on this timer.  */
307                             if (timer_ptr -> tx_timer_id == TX_TIMER_ID)
308                             {
309 
310                                 timer_ptr -> tx_timer_performance_expiration_count++;
311                             }
312                         }
313 #endif
314 
315                         /* Copy the calling function and ID into local variables before interrupts
316                            are re-enabled.  */
317                         timeout_function =  current_timer -> tx_timer_internal_timeout_function;
318                         timeout_param =     current_timer -> tx_timer_internal_timeout_param;
319 
320                         /* Copy the reinitialize ticks into the remaining ticks.  */
321                         current_timer -> tx_timer_internal_remaining_ticks =  current_timer -> tx_timer_internal_re_initialize_ticks;
322 
323                         /* Determine if the timer should be reactivated.  */
324                         if (current_timer -> tx_timer_internal_remaining_ticks != ((ULONG) 0))
325                         {
326 
327                             /* Make the timer appear that it is still active while processing
328                                the expiration routine and with interrupts enabled.  This will
329                                permit proper processing of a timer deactivate from both the
330                                expiration routine and an ISR.  */
331                             current_timer -> tx_timer_internal_list_head =    &reactivate_timer;
332                             current_timer -> tx_timer_internal_active_next =  current_timer;
333 
334                             /* Setup the temporary timer list head pointer.  */
335                             reactivate_timer =  current_timer;
336                         }
337                         else
338                         {
339 
340                             /* Set the list pointer of this timer to NULL.  This is used to indicate
341                                the timer is no longer active.  */
342                             current_timer -> tx_timer_internal_list_head =  TX_NULL;
343                         }
344                     }
345 
346                     /* Set pointer to indicate the expired timer that is currently being processed.  */
347                     _tx_timer_expired_timer_ptr =  current_timer;
348 
349                     /* Restore interrupts for timer expiration call.  */
350                     TX_RESTORE
351 
352                     /* Call the timer-expiration function, if non-NULL.  */
353                     if (timeout_function != TX_NULL)
354                     {
355 
356                         (timeout_function) (timeout_param);
357                     }
358 
359                     /* Lockout interrupts again.  */
360                     TX_DISABLE
361 
362                     /* Clear expired timer pointer.  */
363                     _tx_timer_expired_timer_ptr =  TX_NULL;
364 
365                     /* Determine if the timer needs to be reactivated.  */
366                     if (reactivate_timer == current_timer)
367                     {
368 
369                         /* Reactivate the timer.  */
370 
371 #ifdef TX_TIMER_ENABLE_PERFORMANCE_INFO
372 
373                         /* Determine if this timer expired.  */
374                         if (timeout_function != TX_NULL)
375                         {
376 
377                             /* Increment the total reactivations counter.  */
378                             _tx_timer_performance_reactivate_count++;
379 
380                             /* Determine if this is an application timer.  */
381                             if (current_timer -> tx_timer_internal_timeout_function != &_tx_thread_timeout)
382                             {
383 
384                                 /* Derive the application timer pointer.  */
385 
386                                 /* Pickup the application timer pointer.  */
387                                 TX_USER_TIMER_POINTER_GET(current_timer, timer_ptr)
388 
389                                 /* Increment the number of expirations on this timer.  */
390                                 if (timer_ptr -> tx_timer_id == TX_TIMER_ID)
391                                 {
392 
393                                     timer_ptr -> tx_timer_performance_reactivate_count++;
394                                 }
395                             }
396                         }
397 #endif
398 
399 
400 #ifdef TX_REACTIVATE_INLINE
401 
402                         /* Calculate the amount of time remaining for the timer.  */
403                         if (current_timer -> tx_timer_internal_remaining_ticks > TX_TIMER_ENTRIES)
404                         {
405 
406                             /* Set expiration time to the maximum number of entries.  */
407                             expiration_time =  TX_TIMER_ENTRIES - ((UINT) 1);
408                         }
409                         else
410                         {
411 
412                             /* Timer value fits in the timer entries.  */
413 
414                             /* Set the expiration time.  */
415                             expiration_time =  ((UINT) current_timer -> tx_timer_internal_remaining_ticks) - ((UINT) 1);
416                         }
417 
418                         /* At this point, we are ready to put the timer back on one of
419                            the timer lists.  */
420 
421                         /* Calculate the proper place for the timer.  */
422                         timer_list =  TX_TIMER_POINTER_ADD(_tx_timer_current_ptr, expiration_time);
423                         if (TX_TIMER_INDIRECT_TO_VOID_POINTER_CONVERT(timer_list) >= TX_TIMER_INDIRECT_TO_VOID_POINTER_CONVERT(_tx_timer_list_end))
424                         {
425 
426                             /* Wrap from the beginning of the list.  */
427                             delta =  TX_TIMER_POINTER_DIF(timer_list, _tx_timer_list_end);
428                             timer_list =  TX_TIMER_POINTER_ADD(_tx_timer_list_start, delta);
429                         }
430 
431                         /* Now put the timer on this list.  */
432                         if ((*timer_list) == TX_NULL)
433                         {
434 
435                             /* This list is NULL, just put the new timer on it.  */
436 
437                             /* Setup the links in this timer.  */
438                             current_timer -> tx_timer_internal_active_next =      current_timer;
439                             current_timer -> tx_timer_internal_active_previous =  current_timer;
440 
441                             /* Setup the list head pointer.  */
442                             *timer_list =  current_timer;
443                         }
444                         else
445                         {
446 
447                             /* This list is not NULL, add current timer to the end. */
448                             next_timer =                                          *timer_list;
449                             previous_timer =                                      next_timer -> tx_timer_internal_active_previous;
450                             previous_timer -> tx_timer_internal_active_next =     current_timer;
451                             next_timer -> tx_timer_internal_active_previous =     current_timer;
452                             current_timer -> tx_timer_internal_active_next =      next_timer;
453                             current_timer -> tx_timer_internal_active_previous =  previous_timer;
454                         }
455 
456                         /* Setup list head pointer.  */
457                         current_timer -> tx_timer_internal_list_head =  timer_list;
458 #else
459 
460                         /* Reactivate through the timer activate function.  */
461 
462                         /* Clear the list head for the timer activate call.  */
463                         current_timer -> tx_timer_internal_list_head = TX_NULL;
464 
465                         /* Activate the current timer.  */
466                         _tx_timer_system_activate(current_timer);
467 #endif
468                     }
469                 }
470             } while (_tx_timer_expired != TX_FALSE);
471 
472             /* Clear the timer interrupt processing active flag.  */
473             _tx_timer_processing_active =  TX_FALSE;
474         }
475     }
476 
477     /* Restore interrupts.  */
478     TX_RESTORE
479 #endif
480 }
481 
482 #endif
483