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