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