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_thread.h"
30 
31 
32 /**************************************************************************/
33 /*                                                                        */
34 /*  FUNCTION                                               RELEASE        */
35 /*                                                                        */
36 /*    _tx_thread_priority_change                          PORTABLE C      */
37 /*                                                           6.1          */
38 /*  AUTHOR                                                                */
39 /*                                                                        */
40 /*    William E. Lamie, Microsoft Corporation                             */
41 /*                                                                        */
42 /*  DESCRIPTION                                                           */
43 /*                                                                        */
44 /*    This function changes the priority of the specified thread.  It     */
45 /*    also returns the old priority and handles preemption if the calling */
46 /*    thread is currently executing and the priority change results in a  */
47 /*    higher priority thread ready for execution.                         */
48 /*                                                                        */
49 /*    Note: the preemption threshold is automatically changed to the new  */
50 /*    priority.                                                           */
51 /*                                                                        */
52 /*  INPUT                                                                 */
53 /*                                                                        */
54 /*    thread_ptr                            Pointer to thread to suspend  */
55 /*    new_priority                          New thread priority           */
56 /*    old_priority                          Old thread priority           */
57 /*                                                                        */
58 /*  OUTPUT                                                                */
59 /*                                                                        */
60 /*    status                                Completion status             */
61 /*                                                                        */
62 /*  CALLS                                                                 */
63 /*                                                                        */
64 /*    _tx_thread_system_resume          Resume thread                     */
65 /*    _tx_thread_system_ni_resume       Non-interruptable resume thread   */
66 /*    _tx_thread_system_suspend         Suspend thread                    */
67 /*    _tx_thread_system_ni_suspend      Non-interruptable suspend thread  */
68 /*    _tx_thread_system_preempt_check   Check for preemption              */
69 /*                                                                        */
70 /*  CALLED BY                                                             */
71 /*                                                                        */
72 /*    Application Code                                                    */
73 /*                                                                        */
74 /*  RELEASE HISTORY                                                       */
75 /*                                                                        */
76 /*    DATE              NAME                      DESCRIPTION             */
77 /*                                                                        */
78 /*  05-19-2020     William E. Lamie         Initial Version 6.0           */
79 /*  09-30-2020     William E. Lamie         Modified comment(s), and      */
80 /*                                            change thread state from    */
81 /*                                            TX_SUSPENDED to             */
82 /*                                            TX_PRIORITY_CHANGE before   */
83 /*                                            calling                     */
84 /*                                            _tx_thread_system_suspend,  */
85 /*                                            resulting in version 6.1    */
86 /*                                                                        */
87 /**************************************************************************/
_tx_thread_priority_change(TX_THREAD * thread_ptr,UINT new_priority,UINT * old_priority)88 UINT  _tx_thread_priority_change(TX_THREAD *thread_ptr, UINT new_priority, UINT *old_priority)
89 {
90 
91 TX_INTERRUPT_SAVE_AREA
92 
93 TX_THREAD       *execute_ptr;
94 TX_THREAD       *next_execute_ptr;
95 UINT            original_priority;
96 
97 
98     /* Lockout interrupts while the thread is being suspended.  */
99     TX_DISABLE
100 
101     /* Save the previous priority.  */
102     *old_priority =  thread_ptr -> tx_thread_user_priority;
103 
104     /* If trace is enabled, insert this event into the trace buffer.  */
105     TX_TRACE_IN_LINE_INSERT(TX_TRACE_THREAD_PRIORITY_CHANGE, thread_ptr, new_priority, thread_ptr -> tx_thread_priority, thread_ptr -> tx_thread_state, TX_TRACE_THREAD_EVENTS)
106 
107     /* Log this kernel call.  */
108     TX_EL_THREAD_PRIORITY_CHANGE_INSERT
109 
110     /* Determine if this thread is currently ready.  */
111     if (thread_ptr -> tx_thread_state != TX_READY)
112     {
113 
114         /* Setup the user priority and threshold in the thread's control
115            block.  */
116         thread_ptr -> tx_thread_user_priority =               new_priority;
117         thread_ptr -> tx_thread_user_preempt_threshold =      new_priority;
118 
119         /* Determine if the actual thread priority should be setup, which is the
120            case if the new priority is higher than the priority inheritance.  */
121         if (new_priority < thread_ptr -> tx_thread_inherit_priority)
122         {
123 
124             /* Change thread priority to the new user's priority.  */
125             thread_ptr -> tx_thread_priority =           new_priority;
126             thread_ptr -> tx_thread_preempt_threshold =  new_priority;
127         }
128         else
129         {
130 
131             /* Change thread priority to the priority inheritance.  */
132             thread_ptr -> tx_thread_priority =           thread_ptr -> tx_thread_inherit_priority;
133             thread_ptr -> tx_thread_preempt_threshold =  thread_ptr -> tx_thread_inherit_priority;
134         }
135 
136         /* Restore interrupts.  */
137         TX_RESTORE
138     }
139     else
140     {
141 
142         /* Set the state to priority change.  */
143         thread_ptr -> tx_thread_state =    TX_PRIORITY_CHANGE;
144 
145         /* Pickup the next thread to execute.  */
146         execute_ptr =  _tx_thread_execute_ptr;
147 
148         /* Save the original priority.  */
149         original_priority =  thread_ptr -> tx_thread_priority;
150 
151 #ifdef TX_NOT_INTERRUPTABLE
152 
153         /* Increment the preempt disable flag.  */
154         _tx_thread_preempt_disable++;
155 
156         /* Call actual non-interruptable thread suspension routine.  */
157         _tx_thread_system_ni_suspend(thread_ptr, ((ULONG) 0));
158 
159         /* At this point, the preempt disable flag is still set, so we still have
160            protection against all preemption.  */
161 
162         /* Setup the new priority for this thread.  */
163         thread_ptr -> tx_thread_user_priority =           new_priority;
164         thread_ptr -> tx_thread_user_preempt_threshold =  new_priority;
165 
166         /* Determine if the actual thread priority should be setup, which is the
167            case if the new priority is higher than the priority inheritance.  */
168         if (new_priority < thread_ptr -> tx_thread_inherit_priority)
169         {
170 
171             /* Change thread priority to the new user's priority.  */
172             thread_ptr -> tx_thread_priority =           new_priority;
173             thread_ptr -> tx_thread_preempt_threshold =  new_priority;
174         }
175         else
176         {
177 
178             /* Change thread priority to the priority inheritance.  */
179             thread_ptr -> tx_thread_priority =           thread_ptr -> tx_thread_inherit_priority;
180             thread_ptr -> tx_thread_preempt_threshold =  thread_ptr -> tx_thread_inherit_priority;
181         }
182 
183         /* Resume the thread with the new priority.  */
184         _tx_thread_system_ni_resume(thread_ptr);
185 
186 #else
187 
188         /* Increment the preempt disable flag by 2 to prevent system suspend from
189            returning to the system.  */
190         _tx_thread_preempt_disable =  _tx_thread_preempt_disable + ((UINT) 3);
191 
192         /* Set the suspending flag. */
193         thread_ptr -> tx_thread_suspending =  TX_TRUE;
194 
195         /* Setup the timeout period.  */
196         thread_ptr -> tx_thread_timer.tx_timer_internal_remaining_ticks =  ((ULONG) 0);
197 
198         /* Restore interrupts.  */
199         TX_RESTORE
200 
201         /* The thread is ready and must first be removed from the list.  Call the
202            system suspend function to accomplish this.  */
203         _tx_thread_system_suspend(thread_ptr);
204 
205         /* At this point, the preempt disable flag is still set, so we still have
206            protection against all preemption.  */
207 
208         /* Setup the new priority for this thread.  */
209         thread_ptr -> tx_thread_user_priority =           new_priority;
210         thread_ptr -> tx_thread_user_preempt_threshold =  new_priority;
211 
212         /* Determine if the actual thread priority should be setup, which is the
213            case if the new priority is higher than the priority inheritance.  */
214         if (new_priority < thread_ptr -> tx_thread_inherit_priority)
215         {
216 
217             /* Change thread priority to the new user's priority.  */
218             thread_ptr -> tx_thread_priority =           new_priority;
219             thread_ptr -> tx_thread_preempt_threshold =  new_priority;
220         }
221         else
222         {
223 
224             /* Change thread priority to the priority inheritance.  */
225             thread_ptr -> tx_thread_priority =           thread_ptr -> tx_thread_inherit_priority;
226             thread_ptr -> tx_thread_preempt_threshold =  thread_ptr -> tx_thread_inherit_priority;
227         }
228 
229         /* Resume the thread with the new priority.  */
230         _tx_thread_system_resume(thread_ptr);
231 
232         /* Disable interrupts.  */
233         TX_DISABLE
234 #endif
235 
236         /* Decrement the preempt disable flag.  */
237         _tx_thread_preempt_disable--;
238 
239         /* Pickup the next thread to execute.  */
240         next_execute_ptr =  _tx_thread_execute_ptr;
241 
242         /* Determine if this thread is not the next thread to execute.  */
243         if (thread_ptr != next_execute_ptr)
244         {
245 
246             /* Make sure the thread is still ready.  */
247             if (thread_ptr -> tx_thread_state == TX_READY)
248             {
249 
250                 /* Now check and see if this thread has an equal or higher priority.  */
251                 if (thread_ptr -> tx_thread_priority <= next_execute_ptr -> tx_thread_priority)
252                 {
253 
254                     /* Now determine if this thread was the previously executing thread.  */
255                     if (thread_ptr == execute_ptr)
256                     {
257 
258                         /* Yes, this thread was previously executing before we temporarily suspended and resumed
259                            it in order to change the priority. A lower or same priority thread cannot be the next thread
260                            to execute in this case since this thread really didn't suspend.  Simply reset the execute
261                            pointer to this thread.  */
262                         _tx_thread_execute_ptr =  thread_ptr;
263 
264                         /* Determine if we moved to a lower priority. If so, move the thread to the front of its priority list.  */
265                         if (original_priority < new_priority)
266                         {
267 
268                             /* Ensure that this thread is placed at the front of the priority list.  */
269                             _tx_thread_priority_list[thread_ptr -> tx_thread_priority] =  thread_ptr;
270                         }
271                     }
272                 }
273             }
274         }
275 
276         /* Restore interrupts.  */
277         TX_RESTORE
278 
279         /* Check for preemption.  */
280         _tx_thread_system_preempt_check();
281     }
282 
283     /* Return success if we get here!  */
284     return(TX_SUCCESS);
285 }
286 
287