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