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