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