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