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