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