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