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