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 #define TX_THREAD_SMP_SOURCE_CODE
25 
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_thread_entry                             PORTABLE SMP     */
39 /*                                                           6.1          */
40 /*  AUTHOR                                                                */
41 /*                                                                        */
42 /*    William E. Lamie, Microsoft Corporation                             */
43 /*                                                                        */
44 /*  DESCRIPTION                                                           */
45 /*                                                                        */
46 /*    This function manages thread and application timer expirations.     */
47 /*    Actually, from this thread's point of view, there is no difference. */
48 /*                                                                        */
49 /*  INPUT                                                                 */
50 /*                                                                        */
51 /*    timer_thread_input                Used just for verification        */
52 /*                                                                        */
53 /*  OUTPUT                                                                */
54 /*                                                                        */
55 /*    None                                                                */
56 /*                                                                        */
57 /*  CALLS                                                                 */
58 /*                                                                        */
59 /*    Timer Expiration Function                                           */
60 /*    _tx_thread_system_suspend         Thread suspension                 */
61 /*    _tx_thread_system_ni_suspend      Non-interruptable suspend thread  */
62 /*    _tx_timer_system_activate         Timer reactivate processing       */
63 /*    _tx_thread_smp_core_exclude       Exclude core from timer execution */
64 /*                                                                        */
65 /*  CALLED BY                                                             */
66 /*                                                                        */
67 /*    ThreadX Scheduler                                                   */
68 /*                                                                        */
69 /*  RELEASE HISTORY                                                       */
70 /*                                                                        */
71 /*    DATE              NAME                      DESCRIPTION             */
72 /*                                                                        */
73 /*  09-30-2020     William E. Lamie         Initial 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 UCHAR                       *working_ptr;
97 TX_TIMER                    *timer_ptr;
98 #endif
99 ULONG                        core_exclusion;
100 #ifdef TX_MISRA_ENABLE
101 UINT                         status;
102 #endif
103 
104 
105     /* Make sure the timer input is correct.  This also gets rid of the
106        silly compiler warnings.  */
107     if (timer_thread_input == TX_TIMER_ID)
108     {
109 
110         /* Yes, valid thread entry, proceed...  */
111 
112         /* Now go into an infinite loop to process timer expirations.  */
113         while (TX_LOOP_FOREVER)
114         {
115 
116             /* First, move the current list pointer and clear the timer
117                expired value.  This allows the interrupt handling portion
118                to continue looking for timer expirations.  */
119             TX_DISABLE
120 
121             /* Save the current timer expiration list pointer.  */
122             expired_timers =  *_tx_timer_current_ptr;
123 
124             /* Modify the head pointer in the first timer in the list, if there
125                is one!  */
126             if (expired_timers != TX_NULL)
127             {
128 
129                 expired_timers -> tx_timer_internal_list_head =  &expired_timers;
130 
131                 /* Pickup the current core exclusion bit map for this timer.  */
132                 core_exclusion =  expired_timers -> tx_timer_internal_smp_cores_excluded;
133             }
134             else
135             {
136 
137                 /* Pickup the current core exclusion bit map for the timer thread.  */
138                 core_exclusion =  _tx_timer_thread.tx_thread_smp_cores_excluded;
139             }
140 
141             /* Set the current list pointer to NULL.  */
142             *_tx_timer_current_ptr =  TX_NULL;
143 
144             /* Move the current pointer up one timer entry wrap if we get to
145                the end of the list.  */
146             _tx_timer_current_ptr =  TX_TIMER_POINTER_ADD(_tx_timer_current_ptr, 1);
147             if (_tx_timer_current_ptr == _tx_timer_list_end)
148             {
149 
150                 _tx_timer_current_ptr =  _tx_timer_list_start;
151             }
152 
153             /* Clear the expired flag.  */
154             _tx_timer_expired =  TX_FALSE;
155 
156             /* Restore interrupts temporarily.  */
157             TX_RESTORE
158 
159             /* Determine if we need to change the processor exclusion setting.  */
160             if ((_tx_timer_thread.tx_thread_smp_cores_excluded & ((ULONG) TX_THREAD_SMP_CORE_MASK)) != core_exclusion)
161             {
162 
163 #ifdef TX_MISRA_ENABLE
164                 do
165                 {
166 
167                      /* Setup the new core exclusion for execution this timer.  */
168                      status =  _tx_thread_smp_core_exclude(&_tx_timer_thread, core_exclusion);
169                 } while (status != TX_SUCCESS);
170 #else
171                /* Setup the new core exclusion for execution this timer.  */
172                 _tx_thread_smp_core_exclude(&_tx_timer_thread, core_exclusion);
173 #endif
174 
175                 /* When we get to this point, the system timer thread is executing on one of the allowed cores.  */
176             }
177 
178             /* Disable interrupts again.  */
179             TX_DISABLE
180 
181             /* Next, process the expiration of the associated timers at this
182                time slot.  */
183             while (expired_timers != TX_NULL)
184             {
185 
186                 /* Something is on the list.  Remove it and process the expiration.  */
187                 current_timer =  expired_timers;
188 
189                 /* Pickup the next timer.  */
190                 next_timer =  expired_timers -> tx_timer_internal_active_next;
191 
192                 /* Set the reactivate_timer to NULL.  */
193                 reactivate_timer =  TX_NULL;
194 
195                 /* Determine if this is the only timer.  */
196                 if (current_timer == next_timer)
197                 {
198 
199                     /* Yes, this is the only timer in the list.  */
200 
201                     /* Set the head pointer to NULL.  */
202                     expired_timers =  TX_NULL;
203 
204                     /* Pickup the excluded core(s) for the timer thread.  */
205                     core_exclusion =  _tx_timer_thread.tx_thread_smp_cores_excluded;
206                 }
207                 else
208                 {
209 
210                     /* No, not the only expired timer.  */
211 
212                     /* Remove this timer from the expired list.  */
213                     previous_timer =                                   current_timer -> tx_timer_internal_active_previous;
214                     next_timer -> tx_timer_internal_active_previous =  previous_timer;
215                     previous_timer -> tx_timer_internal_active_next =  next_timer;
216 
217                     /* Modify the next timer's list head to point at the current list head.  */
218                     next_timer -> tx_timer_internal_list_head =  &expired_timers;
219 
220                     /* Set the list head pointer.  */
221                     expired_timers =  next_timer;
222 
223                     /* Pickup the excluded core(s) for this timer.  */
224                     core_exclusion = ((expired_timers -> tx_timer_internal_smp_cores_excluded) & ((ULONG) TX_THREAD_SMP_CORE_MASK));
225                 }
226 
227                 /* In any case, the timer is now off of the expired list.  */
228 
229                 /* Determine if the timer has expired or if it is just a really
230                    big timer that needs to be placed in the list again.  */
231                 if (current_timer -> tx_timer_internal_remaining_ticks > TX_TIMER_ENTRIES)
232                 {
233 
234                     /* Timer is bigger than the timer entries and must be
235                        rescheduled.  */
236 
237 #ifdef TX_TIMER_ENABLE_PERFORMANCE_INFO
238 
239                     /* Increment the total expiration adjustments counter.  */
240                     _tx_timer_performance__expiration_adjust_count++;
241 
242                     /* Determine if this is an application timer.  */
243                     if (current_timer -> tx_timer_internal_timeout_function != &_tx_thread_timeout)
244                     {
245 
246                         /* Derive the application timer pointer.  */
247 
248                         /* Pickup the application timer pointer.  */
249                         TX_USER_TIMER_POINTER_GET(current_timer, timer_ptr)
250 
251                         /* Increment the number of expiration adjustments on this timer.  */
252                         if (timer_ptr -> tx_timer_id == TX_TIMER_ID)
253                         {
254 
255                             timer_ptr -> tx_timer_performance__expiration_adjust_count++;
256                         }
257                     }
258 #endif
259 
260                     /* Decrement the remaining ticks of the timer.  */
261                     current_timer -> tx_timer_internal_remaining_ticks =
262                             current_timer -> tx_timer_internal_remaining_ticks - TX_TIMER_ENTRIES;
263 
264                     /* Set the timeout function to NULL in order to bypass the
265                        expiration.  */
266                     timeout_function =  TX_NULL;
267 
268                     /* Make the timer appear that it is still active while interrupts
269                        are enabled.  This will permit proper processing of a timer
270                        deactivate from an ISR.  */
271                     current_timer -> tx_timer_internal_list_head =    &reactivate_timer;
272                     current_timer -> tx_timer_internal_active_next =  current_timer;
273 
274                     /* Setup the temporary timer list head pointer.  */
275                     reactivate_timer =  current_timer;
276                 }
277                 else
278                 {
279 
280                     /* Timer did expire.  */
281 
282 #ifdef TX_TIMER_ENABLE_PERFORMANCE_INFO
283 
284                     /* Increment the total expirations counter.  */
285                     _tx_timer_performance_expiration_count++;
286 
287                     /* Determine if this is an application timer.  */
288                     if (current_timer -> tx_timer_internal_timeout_function != &_tx_thread_timeout)
289                     {
290 
291                         /* Derive the application timer pointer.  */
292 
293                         /* Pickup the application timer pointer.  */
294                         TX_USER_TIMER_POINTER_GET(current_timer, timer_ptr)
295 
296                         /* Increment the number of expirations on this timer.  */
297                         if (timer_ptr -> tx_timer_id == TX_TIMER_ID)
298                         {
299 
300                             timer_ptr -> tx_timer_performance_expiration_count++;
301                         }
302                     }
303 #endif
304 
305                     /* Copy the calling function and ID into local variables before interrupts
306                        are re-enabled.  */
307                     timeout_function =  current_timer -> tx_timer_internal_timeout_function;
308                     timeout_param =     current_timer -> tx_timer_internal_timeout_param;
309 
310                     /* Copy the reinitialize ticks into the remaining ticks.  */
311                     current_timer -> tx_timer_internal_remaining_ticks =  current_timer -> tx_timer_internal_re_initialize_ticks;
312 
313                     /* Determine if the timer should be reactivated.  */
314                     if (current_timer -> tx_timer_internal_remaining_ticks != ((ULONG) 0))
315                     {
316 
317                         /* Make the timer appear that it is still active while processing
318                            the expiration routine and with interrupts enabled.  This will
319                            permit proper processing of a timer deactivate from both the
320                            expiration routine and an ISR.  */
321                         current_timer -> tx_timer_internal_list_head =    &reactivate_timer;
322                         current_timer -> tx_timer_internal_active_next =  current_timer;
323 
324                         /* Setup the temporary timer list head pointer.  */
325                         reactivate_timer =  current_timer;
326                     }
327                     else
328                     {
329 
330                         /* Set the list pointer of this timer to NULL.  This is used to indicate
331                            the timer is no longer active.  */
332                         current_timer -> tx_timer_internal_list_head =  TX_NULL;
333                     }
334                 }
335 
336                 /* Set pointer to indicate the expired timer that is currently being processed.  */
337                 _tx_timer_expired_timer_ptr =  current_timer;
338 
339 #ifndef TX_PROCESS_TIMER_WITH_PROTECTION
340 
341                 /* Restore interrupts for timer expiration call.  */
342                 TX_RESTORE
343 #endif
344 
345                 /* Call the timer-expiration function, if non-NULL.  */
346                 if (timeout_function != TX_NULL)
347                 {
348 
349                     (timeout_function) (timeout_param);
350                 }
351 
352 #ifndef TX_PROCESS_TIMER_WITH_PROTECTION
353 
354                 /* Lockout interrupts again.  */
355                 TX_DISABLE
356 #endif
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 #ifdef TX_REACTIVATE_INLINE
396 
397                     /* Calculate the amount of time remaining for the timer.  */
398                     if (current_timer -> tx_timer_internal_remaining_ticks > TX_TIMER_ENTRIES)
399                     {
400 
401                         /* Set expiration time to the maximum number of entries.  */
402                         expiration_time =  TX_TIMER_ENTRIES - ((UINT) 1);
403                     }
404                     else
405                     {
406 
407                         /* Timer value fits in the timer entries.  */
408 
409                         /* Set the expiration time.  */
410                         expiration_time =  ((UINT) current_timer -> tx_timer_internal_remaining_ticks) - ((UINT) 1);
411                     }
412 
413                     /* At this point, we are ready to put the timer back on one of
414                        the timer lists.  */
415 
416                     /* Calculate the proper place for the timer.  */
417                     timer_list =  TX_TIMER_POINTER_ADD(_tx_timer_current_ptr, expiration_time);
418                     if (TX_TIMER_INDIRECT_TO_VOID_POINTER_CONVERT(timer_list) >= TX_TIMER_INDIRECT_TO_VOID_POINTER_CONVERT(_tx_timer_list_end))
419                     {
420 
421                         /* Wrap from the beginning of the list.  */
422                         delta =  TX_TIMER_POINTER_DIF(timer_list, _tx_timer_list_end);
423                         timer_list =  TX_TIMER_POINTER_ADD(_tx_timer_list_start, delta);
424                     }
425 
426                     /* Now put the timer on this list.  */
427                     if ((*timer_list) == TX_NULL)
428                     {
429 
430                         /* This list is NULL, just put the new timer on it.  */
431 
432                         /* Setup the links in this timer.  */
433                         current_timer -> tx_timer_internal_active_next =      current_timer;
434                         current_timer -> tx_timer_internal_active_previous =  current_timer;
435 
436                         /* Setup the list head pointer.  */
437                         *timer_list =  current_timer;
438                     }
439                     else
440                     {
441 
442                         /* This list is not NULL, add current timer to the end. */
443                         next_timer =                                          *timer_list;
444                         previous_timer =                                      next_timer -> tx_timer_internal_active_previous;
445                         previous_timer -> tx_timer_internal_active_next =     current_timer;
446                         next_timer -> tx_timer_internal_active_previous =     current_timer;
447                         current_timer -> tx_timer_internal_active_next =      next_timer;
448                         current_timer -> tx_timer_internal_active_previous =  previous_timer;
449                     }
450 
451                     /* Setup list head pointer.  */
452                     current_timer -> tx_timer_internal_list_head =  timer_list;
453 #else
454 
455                     /* Reactivate through the timer activate function.  */
456 
457                     /* Clear the list head for the timer activate call.  */
458                     current_timer -> tx_timer_internal_list_head = TX_NULL;
459 
460                     /* Activate the current timer.  */
461                     _tx_timer_system_activate(current_timer);
462 #endif
463                 }
464 
465                 /* Restore interrupts.  */
466                 TX_RESTORE
467 
468                 /* Determine if we need to change the core exclusion setting.  */
469                 if (_tx_timer_thread.tx_thread_smp_cores_excluded != core_exclusion)
470                 {
471 
472 #ifdef TX_MISRA_ENABLE
473                 do
474                 {
475 
476                     /* Setup the new core exclusion for execution this timer.  */
477                     status =  _tx_thread_smp_core_exclude(&_tx_timer_thread, core_exclusion);
478                 } while (status != TX_SUCCESS);
479 #else
480 
481                     /* Setup the new core exclusion for execution this timer.  */
482                     _tx_thread_smp_core_exclude(&_tx_timer_thread, core_exclusion);
483 #endif
484 
485                     /* When we get to this point, the system timer thread is executing on one of the allowed cores.  */
486                 }
487 
488                 /* Lockout interrupts again.  */
489                 TX_DISABLE
490             }
491 
492             /* Finally, suspend this thread and wait for the next expiration.  */
493 
494             /* Determine if another expiration took place while we were in this
495                thread.  If so, process another expiration.  */
496             if (_tx_timer_expired == TX_FALSE)
497             {
498 
499                 /* Otherwise, no timer expiration, so suspend the thread.  */
500 
501                 /* Build pointer to the timer thread.  */
502                 thread_ptr =  &_tx_timer_thread;
503 
504                 /* Set the status to suspending, in order to indicate the
505                    suspension is in progress.  */
506                 thread_ptr -> tx_thread_state =  TX_SUSPENDED;
507 
508 #ifdef TX_NOT_INTERRUPTABLE
509 
510                 /* Call actual non-interruptable thread suspension routine.  */
511                 _tx_thread_system_ni_suspend(thread_ptr, ((ULONG) 0));
512 
513                 /* Restore interrupts.  */
514                 TX_RESTORE
515 #else
516 
517                 /* Set the suspending flag. */
518                 thread_ptr -> tx_thread_suspending =  TX_TRUE;
519 
520                 /* Increment the preempt disable count prior to suspending.  */
521                 _tx_thread_preempt_disable++;
522 
523                 /* Restore interrupts.  */
524                 TX_RESTORE
525 
526                 /* Call actual thread suspension routine.  */
527                 _tx_thread_system_suspend(thread_ptr);
528 #endif
529             }
530             else
531             {
532 
533                 /* Restore interrupts.  */
534                 TX_RESTORE
535             }
536         }
537     }
538 
539 #ifdef TX_SAFETY_CRITICAL
540 
541     /* If we ever get here, raise safety critical exception.  */
542     TX_SAFETY_CRITICAL_EXCEPTION(__FILE__, __LINE__, 0);
543 #endif
544 
545 }
546 #endif
547 
548