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 /**   Thread                                                              */
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_trace.h"
29 #include "tx_timer.h"
30 #include "tx_thread.h"
31 
32 
33 /**************************************************************************/
34 /*                                                                        */
35 /*  FUNCTION                                               RELEASE        */
36 /*                                                                        */
37 /*    _tx_thread_system_suspend                           PORTABLE C      */
38 /*                                                           6.1          */
39 /*                                                                        */
40 /*  AUTHOR                                                                */
41 /*                                                                        */
42 /*    William E. Lamie, Microsoft Corporation                             */
43 /*                                                                        */
44 /*  DESCRIPTION                                                           */
45 /*                                                                        */
46 /*    This function suspends the specified thread and changes the thread  */
47 /*    state to the value specified.  Note: delayed suspension processing  */
48 /*    is handled outside of this routine.                                 */
49 /*                                                                        */
50 /*  INPUT                                                                 */
51 /*                                                                        */
52 /*    thread_ptr                            Pointer to thread to suspend  */
53 /*                                                                        */
54 /*  OUTPUT                                                                */
55 /*                                                                        */
56 /*    None                                                                */
57 /*                                                                        */
58 /*  CALLS                                                                 */
59 /*                                                                        */
60 /*    _tx_thread_system_return              Return to system              */
61 /*    _tx_thread_system_preempt_check       System preemption check       */
62 /*    _tx_timer_system_activate             Activate timer for timeout    */
63 /*                                                                        */
64 /*  CALLED BY                                                             */
65 /*                                                                        */
66 /*    _tx_thread_priority_change            Thread priority change        */
67 /*    _tx_thread_shell_entry                Thread shell function         */
68 /*    _tx_thread_sleep                      Thread sleep                  */
69 /*    _tx_thread_suspend                    Application thread suspend    */
70 /*    _tx_thread_terminate                  Thread terminate              */
71 /*    Other ThreadX Components                                            */
72 /*                                                                        */
73 /*  RELEASE HISTORY                                                       */
74 /*                                                                        */
75 /*    DATE              NAME                      DESCRIPTION             */
76 /*                                                                        */
77 /*  05-19-2020     William E. Lamie         Initial Version 6.0           */
78 /*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
79 /*                                            resulting in version 6.1    */
80 /*                                                                        */
81 /**************************************************************************/
_tx_thread_system_suspend(TX_THREAD * thread_ptr)82 VOID  _tx_thread_system_suspend(TX_THREAD *thread_ptr)
83 #ifndef TX_NOT_INTERRUPTABLE
84 {
85 
86 TX_INTERRUPT_SAVE_AREA
87 
88 UINT            priority;
89 UINT            base_priority;
90 ULONG           priority_map;
91 ULONG           priority_bit;
92 ULONG           combined_flags;
93 TX_THREAD       *ready_next;
94 TX_THREAD       *ready_previous;
95 TX_THREAD       *current_thread;
96 
97 #if TX_MAX_PRIORITIES > 32
98 UINT            map_index;
99 #endif
100 
101 #ifndef TX_NO_TIMER
102 ULONG           timeout;
103 #endif
104 
105 #ifdef TX_ENABLE_EVENT_TRACE
106 TX_TRACE_BUFFER_ENTRY       *entry_ptr;
107 ULONG                       time_stamp =  ((ULONG) 0);
108 #endif
109 
110     /* Pickup thread pointer.  */
111     TX_THREAD_GET_CURRENT(current_thread)
112 
113 #ifdef TX_ENABLE_STACK_CHECKING
114 
115     /* Check this thread's stack.  */
116     TX_THREAD_STACK_CHECK(thread_ptr)
117 #endif
118 
119     /* Lockout interrupts while the thread is being suspended.  */
120     TX_DISABLE
121 
122 #ifndef TX_NO_TIMER
123 
124     /* Is the current thread suspending?  */
125     if (thread_ptr == current_thread)
126     {
127 
128         /* Pickup the wait option.  */
129         timeout =  thread_ptr -> tx_thread_timer.tx_timer_internal_remaining_ticks;
130 
131         /* Determine if an activation is needed.  */
132         if (timeout != TX_NO_WAIT)
133         {
134 
135             /* Make sure the suspension is not a wait-forever.  */
136             if (timeout != TX_WAIT_FOREVER)
137             {
138 
139                 /* Activate the thread timer with the timeout value setup in the caller.  */
140                 _tx_timer_system_activate(&(thread_ptr -> tx_thread_timer));
141             }
142         }
143 
144         /* Yes, reset time slice for current thread.  */
145         _tx_timer_time_slice =  thread_ptr -> tx_thread_new_time_slice;
146     }
147 #endif
148 
149     /* Decrease the preempt disabled count.  */
150     _tx_thread_preempt_disable--;
151 
152 #ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
153 
154     /* Increment the thread's suspend count.  */
155     thread_ptr -> tx_thread_performance_suspend_count++;
156 
157     /* Increment the total number of thread suspensions.  */
158     _tx_thread_performance_suspend_count++;
159 #endif
160 
161     /* Check to make sure the thread suspending flag is still set.  If not, it
162        has already been resumed.  */
163     if (thread_ptr -> tx_thread_suspending == TX_TRUE)
164     {
165 
166         /* Thread state change.  */
167         TX_THREAD_STATE_CHANGE(thread_ptr, thread_ptr -> tx_thread_state)
168 
169         /* Log the thread status change.  */
170         TX_EL_THREAD_STATUS_CHANGE_INSERT(thread_ptr, thread_ptr -> tx_thread_state)
171 
172 #ifdef TX_ENABLE_EVENT_TRACE
173 
174         /* If trace is enabled, save the current event pointer.  */
175         entry_ptr =  _tx_trace_buffer_current_ptr;
176 #endif
177 
178         /* Log the thread status change.  */
179         TX_TRACE_IN_LINE_INSERT(TX_TRACE_THREAD_SUSPEND, thread_ptr, thread_ptr -> tx_thread_state, TX_POINTER_TO_ULONG_CONVERT(&priority), TX_POINTER_TO_ULONG_CONVERT(_tx_thread_execute_ptr), TX_TRACE_INTERNAL_EVENTS)
180 
181 #ifdef TX_ENABLE_EVENT_TRACE
182 
183         /* Save the time stamp for later comparison to verify that
184            the event hasn't been overwritten by the time we have
185            computed the next thread to execute.  */
186         if (entry_ptr != TX_NULL)
187         {
188 
189             /* Save time stamp.  */
190             time_stamp =  entry_ptr -> tx_trace_buffer_entry_time_stamp;
191         }
192 #endif
193 
194         /* Actually suspend this thread.  But first, clear the suspending flag.  */
195         thread_ptr -> tx_thread_suspending =  TX_FALSE;
196 
197         /* Pickup priority of thread.  */
198         priority =  thread_ptr -> tx_thread_priority;
199 
200         /* Pickup the next ready thread pointer.  */
201         ready_next =      thread_ptr -> tx_thread_ready_next;
202 
203         /* Determine if there are other threads at this priority that are
204            ready.  */
205         if (ready_next != thread_ptr)
206         {
207 
208             /* Yes, there are other threads at this priority ready.  */
209 
210             /* Pickup the previous ready thread pointer.  */
211             ready_previous =  thread_ptr -> tx_thread_ready_previous;
212 
213             /* Just remove this thread from the priority list.  */
214             ready_next -> tx_thread_ready_previous =    ready_previous;
215             ready_previous -> tx_thread_ready_next =    ready_next;
216 
217             /* Determine if this is the head of the priority list.  */
218             if (_tx_thread_priority_list[priority] == thread_ptr)
219             {
220 
221                 /* Update the head pointer of this priority list.  */
222                 _tx_thread_priority_list[priority] =  ready_next;
223 
224 #ifndef TX_DISABLE_PREEMPTION_THRESHOLD
225 
226 #if TX_MAX_PRIORITIES > 32
227 
228                 /* Calculate the index into the bit map array.  */
229                 map_index =  priority/((UINT) 32);
230 #endif
231 
232                 /* Check for a thread preempted that had preemption threshold set.  */
233                 if (_tx_thread_preempted_maps[MAP_INDEX] != ((ULONG) 0))
234                 {
235 
236                     /* Ensure that this thread's priority is clear in the preempt map.  */
237                     TX_MOD32_BIT_SET(priority, priority_bit)
238                     _tx_thread_preempted_maps[MAP_INDEX] =  _tx_thread_preempted_maps[MAP_INDEX] & (~(priority_bit));
239 
240 #if TX_MAX_PRIORITIES > 32
241 
242                     /* Determine if there are any other bits set in this preempt map.  */
243                     if (_tx_thread_preempted_maps[MAP_INDEX] == ((ULONG) 0))
244                     {
245 
246                         /* No, clear the active bit to signify this preempt map has nothing set.  */
247                         TX_DIV32_BIT_SET(priority, priority_bit)
248                         _tx_thread_preempted_map_active =  _tx_thread_preempted_map_active & (~(priority_bit));
249                     }
250 #endif
251                 }
252 #endif
253             }
254         }
255         else
256         {
257 
258             /* This is the only thread at this priority ready to run.  Set the head
259                pointer to NULL.  */
260             _tx_thread_priority_list[priority] =    TX_NULL;
261 
262 #if TX_MAX_PRIORITIES > 32
263 
264             /* Calculate the index into the bit map array.  */
265             map_index =  priority/((UINT) 32);
266 #endif
267 
268             /* Clear this priority bit in the ready priority bit map.  */
269             TX_MOD32_BIT_SET(priority, priority_bit)
270             _tx_thread_priority_maps[MAP_INDEX] =  _tx_thread_priority_maps[MAP_INDEX] & (~(priority_bit));
271 
272 #if TX_MAX_PRIORITIES > 32
273 
274             /* Determine if there are any other bits set in this priority map.  */
275             if (_tx_thread_priority_maps[MAP_INDEX] == ((ULONG) 0))
276             {
277 
278                 /* No, clear the active bit to signify this priority map has nothing set.  */
279                 TX_DIV32_BIT_SET(priority, priority_bit)
280                 _tx_thread_priority_map_active =  _tx_thread_priority_map_active & (~(priority_bit));
281             }
282 #endif
283 
284 #ifndef TX_DISABLE_PREEMPTION_THRESHOLD
285 
286             /* Check for a thread preempted that had preemption-threshold set.  */
287             if (_tx_thread_preempted_maps[MAP_INDEX] != ((ULONG) 0))
288             {
289 
290                 /* Ensure that this thread's priority is clear in the preempt map.  */
291                 TX_MOD32_BIT_SET(priority, priority_bit)
292                 _tx_thread_preempted_maps[MAP_INDEX] =  _tx_thread_preempted_maps[MAP_INDEX] & (~(priority_bit));
293 
294 #if TX_MAX_PRIORITIES > 32
295 
296                 /* Determine if there are any other bits set in this preempt map.  */
297                 if (_tx_thread_preempted_maps[MAP_INDEX] == ((ULONG) 0))
298                 {
299 
300                     /* No, clear the active bit to signify this preempted map has nothing set.  */
301                     TX_DIV32_BIT_SET(priority, priority_bit)
302                     _tx_thread_preempted_map_active =  _tx_thread_preempted_map_active & (~(priority_bit));
303                 }
304 #endif
305             }
306 #endif
307 
308 #if TX_MAX_PRIORITIES > 32
309 
310             /* Calculate the index to find the next highest priority thread ready for execution.  */
311             priority_map =    _tx_thread_priority_map_active;
312 
313             /* Determine if there is anything.   */
314             if (priority_map != ((ULONG) 0))
315             {
316 
317                 /* Calculate the lowest bit set in the priority map. */
318                 TX_LOWEST_SET_BIT_CALCULATE(priority_map, map_index)
319             }
320 
321             /* Calculate the base priority as well.  */
322             base_priority =  map_index * ((UINT) 32);
323 #else
324 
325             /* Setup the base priority to zero.  */
326             base_priority =   ((UINT) 0);
327 #endif
328 
329             /* Setup working variable for the priority map.  */
330             priority_map =    _tx_thread_priority_maps[MAP_INDEX];
331 
332             /* Make a quick check for no other threads ready for execution.  */
333             if (priority_map == ((ULONG) 0))
334             {
335 
336                 /* Nothing else is ready.  Set highest priority and execute thread
337                    accordingly.  */
338                 _tx_thread_highest_priority =  ((UINT) TX_MAX_PRIORITIES);
339                 _tx_thread_execute_ptr =       TX_NULL;
340 
341 #ifndef TX_MISRA_ENABLE
342 
343 #ifdef TX_ENABLE_EVENT_TRACE
344 
345                 /* Check that the event time stamp is unchanged.  A different
346                    timestamp means that a later event wrote over the thread
347                    suspend event. In that case, do nothing here.  */
348                 if (entry_ptr != TX_NULL)
349                 {
350 
351                     /* Is the timestamp the same?  */
352                     if (time_stamp == entry_ptr -> tx_trace_buffer_entry_time_stamp)
353                     {
354 
355                         /* Timestamp is the same, set the "next thread pointer" to the new value of the
356                            next thread to execute. This can be used by the trace analysis tool to keep
357                            track of next thread execution.  */
358                         entry_ptr -> tx_trace_buffer_entry_information_field_4 =  0;
359                     }
360                 }
361 #endif
362 
363                 /* Restore interrupts.  */
364                 TX_RESTORE
365 
366                 /* Determine if preemption should take place. This is only possible if the current thread pointer is
367                    not the same as the execute thread pointer AND the system state and preempt disable flags are clear.  */
368                 TX_THREAD_SYSTEM_RETURN_CHECK(combined_flags)
369                 if (combined_flags == ((ULONG) 0))
370                 {
371 
372 #ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
373 
374                     /* Yes, increment the return to idle return count.  */
375                     _tx_thread_performance_idle_return_count++;
376 #endif
377 
378                     /* Preemption is needed - return to the system!  */
379                     _tx_thread_system_return();
380                 }
381 
382                 /* Return to caller.  */
383                 return;
384 #endif
385             }
386             else
387             {
388 
389                 /* Other threads at different priority levels are ready to run.  */
390 
391                 /* Calculate the lowest bit set in the priority map. */
392                 TX_LOWEST_SET_BIT_CALCULATE(priority_map, priority_bit)
393 
394                 /* Setup the next highest priority variable.  */
395                 _tx_thread_highest_priority =  base_priority + ((UINT) priority_bit);
396             }
397         }
398 
399         /* Determine if the suspending thread is the thread designated to execute.  */
400         if (thread_ptr == _tx_thread_execute_ptr)
401         {
402 
403             /* Pickup the highest priority thread to execute.  */
404             _tx_thread_execute_ptr =  _tx_thread_priority_list[_tx_thread_highest_priority];
405 
406 #ifndef TX_DISABLE_PREEMPTION_THRESHOLD
407 
408             /* Determine if a previous thread with preemption-threshold was preempted.  */
409 #if TX_MAX_PRIORITIES > 32
410             if (_tx_thread_preempted_map_active != ((ULONG) 0))
411 #else
412             if (_tx_thread_preempted_maps[MAP_INDEX] != ((ULONG) 0))
413 #endif
414             {
415 
416                 /* Yes, there was a thread preempted when it was using preemption-threshold.  */
417 
418                 /* Disable preemption.  */
419                 _tx_thread_preempt_disable++;
420 
421                 /* Restore interrupts.  */
422                 TX_RESTORE
423 
424                 /* Interrupts are enabled briefly here to keep the interrupt
425                    lockout time deterministic.  */
426 
427                 /* Disable interrupts again.  */
428                 TX_DISABLE
429 
430                 /* Decrement the preemption disable variable.  */
431                 _tx_thread_preempt_disable--;
432 
433                 /* Calculate the thread with preemption threshold set that
434                    was interrupted by a thread above the preemption level.  */
435 
436 #if TX_MAX_PRIORITIES > 32
437 
438                 /* Calculate the index to find the next highest priority thread ready for execution.  */
439                 priority_map =    _tx_thread_preempted_map_active;
440 
441                 /* Calculate the lowest bit set in the priority map. */
442                 TX_LOWEST_SET_BIT_CALCULATE(priority_map, map_index)
443 
444                 /* Calculate the base priority as well.  */
445                 base_priority =  map_index * ((UINT) 32);
446 #else
447 
448                 /* Setup the base priority to zero.  */
449                 base_priority =   ((UINT) 0);
450 #endif
451 
452                 /* Setup temporary preempted map.  */
453                 priority_map =  _tx_thread_preempted_maps[MAP_INDEX];
454 
455                 /* Calculate the lowest bit set in the priority map. */
456                 TX_LOWEST_SET_BIT_CALCULATE(priority_map, priority_bit)
457 
458                 /* Setup the highest priority preempted thread.  */
459                 priority =  base_priority + ((UINT) priority_bit);
460 
461                 /* Determine if the next highest priority thread is above the highest priority threshold value.  */
462                 if (_tx_thread_highest_priority >= (_tx_thread_priority_list[priority] -> tx_thread_preempt_threshold))
463                 {
464 
465                     /* Thread not allowed to execute until earlier preempted thread finishes or lowers its
466                        preemption-threshold.  */
467                     _tx_thread_execute_ptr =  _tx_thread_priority_list[priority];
468 
469                     /* Clear the corresponding bit in the preempted map, since the preemption has been restored.  */
470                     TX_MOD32_BIT_SET(priority, priority_bit)
471                     _tx_thread_preempted_maps[MAP_INDEX] =  _tx_thread_preempted_maps[MAP_INDEX] & (~(priority_bit));
472 
473 #if TX_MAX_PRIORITIES > 32
474 
475                     /* Determine if there are any other bits set in this preempt map.  */
476                     if (_tx_thread_preempted_maps[MAP_INDEX] == ((ULONG) 0))
477                     {
478 
479                         /* No, clear the active bit to signify this preempt map has nothing set.  */
480                         TX_DIV32_BIT_SET(priority, priority_bit)
481                         _tx_thread_preempted_map_active =  _tx_thread_preempted_map_active & (~(priority_bit));
482                     }
483 #endif
484                 }
485             }
486 #endif
487 
488 #ifndef TX_MISRA_ENABLE
489 
490 #ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
491 
492             /* Is the execute pointer different?  */
493             if (_tx_thread_performance_execute_log[_tx_thread_performance__execute_log_index] != _tx_thread_execute_ptr)
494             {
495 
496                 /* Move to next entry.  */
497                 _tx_thread_performance__execute_log_index++;
498 
499                 /* Check for wrap condition.  */
500                 if (_tx_thread_performance__execute_log_index >= TX_THREAD_EXECUTE_LOG_SIZE)
501                 {
502 
503                     /* Set the index to the beginning.  */
504                     _tx_thread_performance__execute_log_index =  ((UINT) 0);
505                 }
506 
507                 /* Log the new execute pointer.  */
508                 _tx_thread_performance_execute_log[_tx_thread_performance__execute_log_index] =  _tx_thread_execute_ptr;
509             }
510 #endif
511 
512 #ifdef TX_ENABLE_EVENT_TRACE
513 
514             /* Check that the event time stamp is unchanged.  A different
515                timestamp means that a later event wrote over the thread
516                suspend event. In that case, do nothing here.  */
517             if (entry_ptr != TX_NULL)
518             {
519 
520                 /* Is the timestamp the same?  */
521                 if (time_stamp == entry_ptr -> tx_trace_buffer_entry_time_stamp)
522                 {
523 
524                     /* Timestamp is the same, set the "next thread pointer" to the new value of the
525                        next thread to execute. This can be used by the trace analysis tool to keep
526                        track of next thread execution.  */
527                     entry_ptr -> tx_trace_buffer_entry_information_field_4 =  TX_POINTER_TO_ULONG_CONVERT(_tx_thread_execute_ptr);
528                 }
529             }
530 #endif
531 
532             /* Restore interrupts.  */
533             TX_RESTORE
534 
535             /* Determine if preemption should take place. This is only possible if the current thread pointer is
536                not the same as the execute thread pointer AND the system state and preempt disable flags are clear.  */
537             TX_THREAD_SYSTEM_RETURN_CHECK(combined_flags)
538             if (combined_flags == ((ULONG) 0))
539             {
540 
541 #ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
542 
543                 /* No, there is another thread ready to run and will be scheduled upon return.  */
544                 _tx_thread_performance_non_idle_return_count++;
545 #endif
546 
547                 /* Preemption is needed - return to the system!  */
548                 _tx_thread_system_return();
549             }
550 
551             /* Return to caller.  */
552             return;
553 #endif
554         }
555 
556 #ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
557 
558         /* Is the execute pointer different?  */
559         if (_tx_thread_performance_execute_log[_tx_thread_performance__execute_log_index] != _tx_thread_execute_ptr)
560         {
561 
562             /* Move to next entry.  */
563             _tx_thread_performance__execute_log_index++;
564 
565             /* Check for wrap condition.  */
566             if (_tx_thread_performance__execute_log_index >= TX_THREAD_EXECUTE_LOG_SIZE)
567             {
568 
569                 /* Set the index to the beginning.  */
570                 _tx_thread_performance__execute_log_index =  ((UINT) 0);
571             }
572 
573             /* Log the new execute pointer.  */
574             _tx_thread_performance_execute_log[_tx_thread_performance__execute_log_index] =  _tx_thread_execute_ptr;
575         }
576 #endif
577 
578 #ifdef TX_ENABLE_EVENT_TRACE
579 
580          /* Check that the event time stamp is unchanged.  A different
581             timestamp means that a later event wrote over the thread
582             suspend event. In that case, do nothing here.  */
583          if (entry_ptr != TX_NULL)
584          {
585 
586             /* Is the timestamp the same?  */
587             if (time_stamp == entry_ptr -> tx_trace_buffer_entry_time_stamp)
588             {
589 
590                 /* Timestamp is the same, set the "next thread pointer" to the new value of the
591                    next thread to execute. This can be used by the trace analysis tool to keep
592                    track of next thread execution.  */
593 #ifdef TX_MISRA_ENABLE
594                 entry_ptr -> tx_trace_buffer_entry_info_4 =  TX_POINTER_TO_ULONG_CONVERT(_tx_thread_execute_ptr);
595 #else
596                 entry_ptr -> tx_trace_buffer_entry_information_field_4 =  TX_POINTER_TO_ULONG_CONVERT(_tx_thread_execute_ptr);
597 #endif
598             }
599         }
600 #endif
601     }
602 
603     /* Restore interrupts.  */
604     TX_RESTORE
605 
606     /* Determine if a preemption condition is present.  */
607     if (current_thread != _tx_thread_execute_ptr)
608     {
609 
610 #ifdef TX_ENABLE_STACK_CHECKING
611 
612         /* Pickup the next execute pointer.  */
613         thread_ptr =  _tx_thread_execute_ptr;
614 
615         /* Check this thread's stack.  */
616         TX_THREAD_STACK_CHECK(thread_ptr)
617 #endif
618 
619         /* Determine if preemption should take place. This is only possible if the current thread pointer is
620            not the same as the execute thread pointer AND the system state and preempt disable flags are clear.  */
621         TX_THREAD_SYSTEM_RETURN_CHECK(combined_flags)
622         if (combined_flags == ((ULONG) 0))
623         {
624 
625 #ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
626 
627             /* Determine if an idle system return is present.  */
628             if (_tx_thread_execute_ptr == TX_NULL)
629             {
630 
631                 /* Yes, increment the return to idle return count.  */
632                 _tx_thread_performance_idle_return_count++;
633             }
634             else
635             {
636 
637                 /* No, there is another thread ready to run and will be scheduled upon return.  */
638                 _tx_thread_performance_non_idle_return_count++;
639             }
640 #endif
641 
642             /* Preemption is needed - return to the system!  */
643             _tx_thread_system_return();
644         }
645     }
646 
647     /* Return to caller.  */
648     return;
649 }
650 #else
651 /* Define the entry function for modules assuming the interruptable version of system suspend.  */
652 {
653 
654 TX_INTERRUPT_SAVE_AREA
655 
656 ULONG       wait_option;
657 
658     /* Disable interrupts.  */
659     TX_DISABLE
660 
661     /* Determine if the thread is still suspending.  */
662     if (thread_ptr -> tx_thread_suspending == TX_TRUE)
663     {
664 
665         /* Yes, prepare to call the non-interruptable system suspend function.  */
666 
667         /* Clear the thread suspending flag.  */
668         thread_ptr -> tx_thread_suspending =  TX_FALSE;
669 
670         /* Pickup the wait option.  */
671         wait_option =  thread_ptr -> tx_thread_timer.tx_timer_internal_remaining_ticks;
672 
673         /* Decrement the preempt disable count.  */
674         _tx_thread_preempt_disable--;
675 
676         /* Call actual non-interruptable thread suspension routine.  */
677         _tx_thread_system_ni_suspend(thread_ptr, wait_option);
678     }
679 
680     /* Restore interrupts.  */
681     TX_RESTORE
682 
683     /* Check for preemption.  */
684     _tx_thread_system_preempt_check();
685 }
686 
687 /* Define the system suspend function that is not interruptable, i.e., it is assumed that
688    interrupts are disabled upon calling this function.  */
689 
690 VOID  _tx_thread_system_ni_suspend(TX_THREAD *thread_ptr, ULONG wait_option)
691 {
692 
693 UINT            priority;
694 UINT            base_priority;
695 ULONG           priority_map;
696 ULONG           priority_bit;
697 ULONG           combined_flags;
698 TX_THREAD       *ready_next;
699 TX_THREAD       *ready_previous;
700 TX_THREAD       *current_thread;
701 
702 #if TX_MAX_PRIORITIES > 32
703 UINT            map_index;
704 #endif
705 
706 #ifdef TX_ENABLE_EVENT_TRACE
707 TX_TRACE_BUFFER_ENTRY       *entry_ptr;
708 ULONG                       time_stamp =  ((ULONG) 0);
709 #endif
710 
711 
712     /* Pickup thread pointer.  */
713     TX_THREAD_GET_CURRENT(current_thread)
714 
715 #ifndef TX_NO_TIMER
716 
717 
718     /* Determine if a timeout needs to be activated.  */
719     if (thread_ptr == current_thread)
720     {
721 
722         /* Is there a wait option?  */
723         if (wait_option != TX_NO_WAIT)
724         {
725 
726             /* Make sure it is not a wait-forever option.  */
727             if (wait_option != TX_WAIT_FOREVER)
728             {
729 
730                 /* Setup the wait option.  */
731                 thread_ptr -> tx_thread_timer.tx_timer_internal_remaining_ticks =  wait_option;
732 
733                 /* Activate the thread timer with the timeout value setup in the caller.  */
734                 _tx_timer_system_activate(&(thread_ptr -> tx_thread_timer));
735             }
736         }
737 
738         /* Reset time slice for current thread.  */
739         _tx_timer_time_slice =  thread_ptr -> tx_thread_new_time_slice;
740     }
741 #endif
742 
743 #ifdef TX_ENABLE_STACK_CHECKING
744 
745     /* Check this thread's stack.  */
746     TX_THREAD_STACK_CHECK(thread_ptr)
747 #endif
748 
749 #ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
750 
751     /* Increment the thread's suspend count.  */
752     thread_ptr -> tx_thread_performance_suspend_count++;
753 
754     /* Increment the total number of thread suspensions.  */
755     _tx_thread_performance_suspend_count++;
756 #endif
757 
758     /* Thread state change.  */
759     TX_THREAD_STATE_CHANGE(thread_ptr, thread_ptr -> tx_thread_state)
760 
761     /* Log the thread status change.  */
762     TX_EL_THREAD_STATUS_CHANGE_INSERT(thread_ptr, thread_ptr -> tx_thread_state)
763 
764 #ifdef TX_ENABLE_EVENT_TRACE
765 
766     /* If trace is enabled, save the current event pointer.  */
767     entry_ptr =  _tx_trace_buffer_current_ptr;
768 #endif
769 
770     /* Log the thread status change.  */
771     TX_TRACE_IN_LINE_INSERT(TX_TRACE_THREAD_SUSPEND, thread_ptr, thread_ptr -> tx_thread_state, TX_POINTER_TO_ULONG_CONVERT(&priority), TX_POINTER_TO_ULONG_CONVERT(_tx_thread_execute_ptr), TX_TRACE_INTERNAL_EVENTS)
772 
773 #ifdef TX_ENABLE_EVENT_TRACE
774 
775     /* Save the time stamp for later comparison to verify that
776        the event hasn't been overwritten by the time we have
777        computed the next thread to execute.  */
778     if (entry_ptr != TX_NULL)
779     {
780 
781         /* Save time stamp.  */
782         time_stamp =  entry_ptr -> tx_trace_buffer_entry_time_stamp;
783     }
784 #endif
785 
786     /* Pickup priority of thread.  */
787     priority =  thread_ptr -> tx_thread_priority;
788 
789     /* Pickup the next ready thread pointer.  */
790     ready_next =      thread_ptr -> tx_thread_ready_next;
791 
792     /* Determine if there are other threads at this priority that are
793        ready.  */
794     if (ready_next != thread_ptr)
795     {
796 
797         /* Yes, there are other threads at this priority ready.  */
798 
799         /* Pickup the previous ready thread pointer.  */
800         ready_previous =  thread_ptr -> tx_thread_ready_previous;
801 
802         /* Just remove this thread from the priority list.  */
803         ready_next -> tx_thread_ready_previous =    ready_previous;
804         ready_previous -> tx_thread_ready_next =    ready_next;
805 
806         /* Determine if this is the head of the priority list.  */
807         if (_tx_thread_priority_list[priority] == thread_ptr)
808         {
809 
810             /* Update the head pointer of this priority list.  */
811             _tx_thread_priority_list[priority] =  ready_next;
812 
813 #ifndef TX_DISABLE_PREEMPTION_THRESHOLD
814 
815 #if TX_MAX_PRIORITIES > 32
816 
817             /* Calculate the index into the bit map array.  */
818             map_index =  priority/((UINT) 32);
819 #endif
820 
821             /* Check for a thread preempted that had preemption threshold set.  */
822             if (_tx_thread_preempted_maps[MAP_INDEX] != ((ULONG) 0))
823             {
824 
825                 /* Ensure that this thread's priority is clear in the preempt map.  */
826                 TX_MOD32_BIT_SET(priority, priority_bit)
827                 _tx_thread_preempted_maps[MAP_INDEX] =  _tx_thread_preempted_maps[MAP_INDEX] & (~(priority_bit));
828 
829 #if TX_MAX_PRIORITIES > 32
830 
831                 /* Determine if there are any other bits set in this preempt map.  */
832                 if (_tx_thread_preempted_maps[MAP_INDEX] == ((ULONG) 0))
833                 {
834 
835                     /* No, clear the active bit to signify this preempt map has nothing set.  */
836                     TX_DIV32_BIT_SET(priority, priority_bit)
837                     _tx_thread_preempted_map_active =  _tx_thread_preempted_map_active & (~(priority_bit));
838                 }
839 #endif
840             }
841 #endif
842         }
843     }
844     else
845     {
846 
847         /* This is the only thread at this priority ready to run.  Set the head
848            pointer to NULL.  */
849         _tx_thread_priority_list[priority] =    TX_NULL;
850 
851 #if TX_MAX_PRIORITIES > 32
852 
853         /* Calculate the index into the bit map array.  */
854         map_index =  priority/((UINT) 32);
855 #endif
856 
857         /* Clear this priority bit in the ready priority bit map.  */
858         TX_MOD32_BIT_SET(priority, priority_bit)
859         _tx_thread_priority_maps[MAP_INDEX] =  _tx_thread_priority_maps[MAP_INDEX] & (~(priority_bit));
860 
861 #if TX_MAX_PRIORITIES > 32
862 
863         /* Determine if there are any other bits set in this priority map.  */
864         if (_tx_thread_priority_maps[MAP_INDEX] == ((ULONG) 0))
865         {
866 
867             /* No, clear the active bit to signify this priority map has nothing set.  */
868             TX_DIV32_BIT_SET(priority, priority_bit)
869             _tx_thread_priority_map_active =  _tx_thread_priority_map_active & (~(priority_bit));
870         }
871 #endif
872 
873 #ifndef TX_DISABLE_PREEMPTION_THRESHOLD
874 
875         /* Check for a thread preempted that had preemption-threshold set.  */
876         if (_tx_thread_preempted_maps[MAP_INDEX] != ((ULONG) 0))
877         {
878 
879             /* Ensure that this thread's priority is clear in the preempt map.  */
880             TX_MOD32_BIT_SET(priority, priority_bit)
881             _tx_thread_preempted_maps[MAP_INDEX] =  _tx_thread_preempted_maps[MAP_INDEX] & (~(priority_bit));
882 
883 #if TX_MAX_PRIORITIES > 32
884 
885             /* Determine if there are any other bits set in this preempt map.  */
886             if (_tx_thread_preempted_maps[MAP_INDEX] == ((ULONG) 0))
887             {
888 
889                 /* No, clear the active bit to signify this preempted map has nothing set.  */
890                 TX_DIV32_BIT_SET(priority, priority_bit)
891                 _tx_thread_preempted_map_active =  _tx_thread_preempted_map_active & (~(priority_bit));
892             }
893 #endif
894         }
895 #endif
896 
897 #if TX_MAX_PRIORITIES > 32
898 
899         /* Calculate the index to find the next highest priority thread ready for execution.  */
900         priority_map =    _tx_thread_priority_map_active;
901 
902         /* Determine if there is anything.   */
903         if (priority_map != ((ULONG) 0))
904         {
905 
906             /* Calculate the lowest bit set in the priority map. */
907             TX_LOWEST_SET_BIT_CALCULATE(priority_map, map_index)
908         }
909 
910         /* Calculate the base priority as well.  */
911         base_priority =  map_index * ((UINT) 32);
912 #else
913 
914         /* Setup the base priority to zero.  */
915         base_priority =   ((UINT) 0);
916 #endif
917 
918         /* Setup working variable for the priority map.  */
919         priority_map =    _tx_thread_priority_maps[MAP_INDEX];
920 
921         /* Make a quick check for no other threads ready for execution.  */
922         if (priority_map == ((ULONG) 0))
923         {
924 
925             /* Nothing else is ready.  Set highest priority and execute thread
926                accordingly.  */
927             _tx_thread_highest_priority =  ((UINT) TX_MAX_PRIORITIES);
928             _tx_thread_execute_ptr =       TX_NULL;
929 
930 #ifndef TX_MISRA_ENABLE
931 
932 #ifdef TX_ENABLE_EVENT_TRACE
933 
934             /* Check that the event time stamp is unchanged.  A different
935                timestamp means that a later event wrote over the thread
936                suspend event. In that case, do nothing here.  */
937             if (entry_ptr != TX_NULL)
938             {
939 
940                 /* Is the timestamp the same?  */
941                 if (time_stamp == entry_ptr -> tx_trace_buffer_entry_time_stamp)
942                 {
943 
944                     /* Timestamp is the same, set the "next thread pointer" to the new value of the
945                        next thread to execute. This can be used by the trace analysis tool to keep
946                        track of next thread execution.  */
947                     entry_ptr -> tx_trace_buffer_entry_information_field_4 =  0;
948                 }
949             }
950 #endif
951 
952             /* Determine if preemption should take place. This is only possible if the current thread pointer is
953                not the same as the execute thread pointer AND the system state and preempt disable flags are clear.  */
954             TX_THREAD_SYSTEM_RETURN_CHECK(combined_flags)
955             if (combined_flags == ((ULONG) 0))
956             {
957 
958 #ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
959 
960                 /* Yes, increment the return to idle return count.  */
961                 _tx_thread_performance_idle_return_count++;
962 #endif
963 
964                 /* Preemption is needed - return to the system!  */
965                 _tx_thread_system_return();
966             }
967 
968             /* Return to caller.  */
969             return;
970 #endif
971         }
972         else
973         {
974 
975             /* Calculate the lowest bit set in the priority map. */
976             TX_LOWEST_SET_BIT_CALCULATE(priority_map, priority_bit)
977 
978             /* Setup the next highest priority variable.  */
979             _tx_thread_highest_priority =  base_priority + ((UINT) priority_bit);
980         }
981     }
982 
983     /* Determine if the suspending thread is the thread designated to execute.  */
984     if (thread_ptr == _tx_thread_execute_ptr)
985     {
986 
987         /* Pickup the highest priority thread to execute.  */
988         _tx_thread_execute_ptr =  _tx_thread_priority_list[_tx_thread_highest_priority];
989 
990 #ifndef TX_DISABLE_PREEMPTION_THRESHOLD
991 
992         /* Determine if a previous thread with preemption-threshold was preempted.  */
993 #if TX_MAX_PRIORITIES > 32
994         if (_tx_thread_preempted_map_active != ((ULONG) 0))
995 #else
996         if (_tx_thread_preempted_maps[MAP_INDEX] != ((ULONG) 0))
997 #endif
998         {
999 
1000             /* Yes, there was a thread preempted when it was using preemption-threshold.  */
1001 
1002             /* Disable preemption.  */
1003             _tx_thread_preempt_disable++;
1004 
1005             /* Decrement the preemption disable variable.  */
1006             _tx_thread_preempt_disable--;
1007 
1008             /* Calculate the thread with preemption threshold set that
1009                was interrupted by a thread above the preemption level.  */
1010 
1011 #if TX_MAX_PRIORITIES > 32
1012 
1013             /* Calculate the index to find the next highest priority thread ready for execution.  */
1014             priority_map =    _tx_thread_preempted_map_active;
1015 
1016             /* Calculate the lowest bit set in the priority map. */
1017             TX_LOWEST_SET_BIT_CALCULATE(priority_map, map_index)
1018 
1019             /* Calculate the base priority as well.  */
1020             base_priority =  map_index * ((UINT) 32);
1021 #else
1022 
1023             /* Setup the base priority to zero.  */
1024             base_priority =   ((UINT) 0);
1025 #endif
1026 
1027             /* Setup temporary preempted map.  */
1028             priority_map =  _tx_thread_preempted_maps[MAP_INDEX];
1029 
1030             /* Calculate the lowest bit set in the priority map. */
1031             TX_LOWEST_SET_BIT_CALCULATE(priority_map, priority_bit)
1032 
1033             /* Setup the highest priority preempted thread.  */
1034             priority =  base_priority + ((UINT) priority_bit);
1035 
1036             /* Determine if the next highest priority thread is above the highest priority threshold value.  */
1037             if (_tx_thread_highest_priority >= (_tx_thread_priority_list[priority] -> tx_thread_preempt_threshold))
1038             {
1039 
1040                 /* Thread not allowed to execute until earlier preempted thread finishes or lowers its
1041                    preemption-threshold.  */
1042                 _tx_thread_execute_ptr =  _tx_thread_priority_list[priority];
1043 
1044                 /* Clear the corresponding bit in the preempted map, since the preemption has been restored.  */
1045                 TX_MOD32_BIT_SET(priority, priority_bit)
1046                 _tx_thread_preempted_maps[MAP_INDEX] =  _tx_thread_preempted_maps[MAP_INDEX] & (~(priority_bit));
1047 
1048 #if TX_MAX_PRIORITIES > 32
1049 
1050                 /* Determine if there are any other bits set in this preempt map.  */
1051                 if (_tx_thread_preempted_maps[MAP_INDEX] == ((ULONG) 0))
1052                 {
1053 
1054                     /* No, clear the active bit to signify this preempt map has nothing set.  */
1055                     TX_DIV32_BIT_SET(priority, priority_bit)
1056                     _tx_thread_preempted_map_active =  _tx_thread_preempted_map_active & (~(priority_bit));
1057                 }
1058 #endif
1059             }
1060         }
1061 #endif
1062 
1063 #ifndef TX_MISRA_ENABLE
1064 
1065 #ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
1066 
1067         /* Is the execute pointer different?  */
1068         if (_tx_thread_performance_execute_log[_tx_thread_performance__execute_log_index] != _tx_thread_execute_ptr)
1069         {
1070 
1071             /* Move to next entry.  */
1072             _tx_thread_performance__execute_log_index++;
1073 
1074             /* Check for wrap condition.  */
1075             if (_tx_thread_performance__execute_log_index >= TX_THREAD_EXECUTE_LOG_SIZE)
1076             {
1077 
1078                 /* Set the index to the beginning.  */
1079                 _tx_thread_performance__execute_log_index =  ((UINT) 0);
1080             }
1081 
1082             /* Log the new execute pointer.  */
1083             _tx_thread_performance_execute_log[_tx_thread_performance__execute_log_index] =  _tx_thread_execute_ptr;
1084         }
1085 #endif
1086 
1087 #ifdef TX_ENABLE_EVENT_TRACE
1088 
1089         /* Check that the event time stamp is unchanged.  A different
1090            timestamp means that a later event wrote over the thread
1091            suspend event. In that case, do nothing here.  */
1092         if (entry_ptr != TX_NULL)
1093         {
1094 
1095             /* Is the timestamp the same?  */
1096             if (time_stamp == entry_ptr -> tx_trace_buffer_entry_time_stamp)
1097             {
1098 
1099                 /* Timestamp is the same, set the "next thread pointer" to the new value of the
1100                    next thread to execute. This can be used by the trace analysis tool to keep
1101                    track of next thread execution.  */
1102                 entry_ptr -> tx_trace_buffer_entry_information_field_4 =  TX_POINTER_TO_ULONG_CONVERT(_tx_thread_execute_ptr);
1103             }
1104         }
1105 #endif
1106 
1107         /* Determine if preemption should take place. This is only possible if the current thread pointer is
1108            not the same as the execute thread pointer AND the system state and preempt disable flags are clear.  */
1109         TX_THREAD_SYSTEM_RETURN_CHECK(combined_flags)
1110         if (combined_flags == ((ULONG) 0))
1111         {
1112 
1113 #ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
1114 
1115             /* No, there is another thread ready to run and will be scheduled upon return.  */
1116             _tx_thread_performance_non_idle_return_count++;
1117 #endif
1118 
1119             /* Preemption is needed - return to the system!  */
1120             _tx_thread_system_return();
1121         }
1122 
1123         /* Return to caller.  */
1124         return;
1125 #endif
1126     }
1127 
1128 #ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
1129 
1130     /* Is the execute pointer different?  */
1131     if (_tx_thread_performance_execute_log[_tx_thread_performance__execute_log_index] != _tx_thread_execute_ptr)
1132     {
1133 
1134         /* Move to next entry.  */
1135         _tx_thread_performance__execute_log_index++;
1136 
1137         /* Check for wrap condition.  */
1138         if (_tx_thread_performance__execute_log_index >= TX_THREAD_EXECUTE_LOG_SIZE)
1139         {
1140 
1141             /* Set the index to the beginning.  */
1142             _tx_thread_performance__execute_log_index =  ((UINT) 0);
1143         }
1144 
1145         /* Log the new execute pointer.  */
1146         _tx_thread_performance_execute_log[_tx_thread_performance__execute_log_index] =  _tx_thread_execute_ptr;
1147     }
1148 #endif
1149 
1150 #ifdef TX_ENABLE_EVENT_TRACE
1151 
1152     /* Check that the event time stamp is unchanged.  A different
1153        timestamp means that a later event wrote over the thread
1154        suspend event. In that case, do nothing here.  */
1155     if (entry_ptr != TX_NULL)
1156     {
1157 
1158         /* Is the timestamp the same?  */
1159         if (time_stamp == entry_ptr -> tx_trace_buffer_entry_time_stamp)
1160         {
1161 
1162             /* Timestamp is the same, set the "next thread pointer" to the new value of the
1163                next thread to execute. This can be used by the trace analysis tool to keep
1164                track of next thread execution.  */
1165 #ifdef TX_MISRA_ENABLE
1166             entry_ptr -> tx_trace_buffer_entry_info_4 =  TX_POINTER_TO_ULONG_CONVERT(_tx_thread_execute_ptr);
1167 #else
1168             entry_ptr -> tx_trace_buffer_entry_information_field_4 =  TX_POINTER_TO_ULONG_CONVERT(_tx_thread_execute_ptr);
1169 #endif
1170         }
1171     }
1172 #endif
1173 
1174     /* Determine if a preemption condition is present.  */
1175     if (current_thread != _tx_thread_execute_ptr)
1176     {
1177 
1178 #ifdef TX_ENABLE_STACK_CHECKING
1179 
1180         /* Pickup the next execute pointer.  */
1181         thread_ptr =  _tx_thread_execute_ptr;
1182 
1183         /* Check this thread's stack.  */
1184         TX_THREAD_STACK_CHECK(thread_ptr)
1185 #endif
1186 
1187         /* Determine if preemption should take place. This is only possible if the current thread pointer is
1188            not the same as the execute thread pointer AND the system state and preempt disable flags are clear.  */
1189         TX_THREAD_SYSTEM_RETURN_CHECK(combined_flags)
1190         if (combined_flags == ((ULONG) 0))
1191         {
1192 
1193 #ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
1194 
1195             /* Determine if an idle system return is present.  */
1196             if (_tx_thread_execute_ptr == TX_NULL)
1197             {
1198 
1199                 /* Yes, increment the return to idle return count.  */
1200                 _tx_thread_performance_idle_return_count++;
1201             }
1202             else
1203             {
1204 
1205                 /* No, there is another thread ready to run and will be scheduled upon return.  */
1206                 _tx_thread_performance_non_idle_return_count++;
1207             }
1208 #endif
1209 
1210             /* Preemption is needed - return to the system!  */
1211             _tx_thread_system_return();
1212         }
1213     }
1214 
1215     /* Return to caller.  */
1216     return;
1217 }
1218 #endif
1219 
1220