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