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