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 /** Mutex */
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_thread.h"
29 #include "tx_mutex.h"
30
31
32 /**************************************************************************/
33 /* */
34 /* FUNCTION RELEASE */
35 /* */
36 /* _tx_mutex_priority_change PORTABLE C */
37 /* 6.1.6 */
38 /* AUTHOR */
39 /* */
40 /* William E. Lamie, Microsoft Corporation */
41 /* */
42 /* DESCRIPTION */
43 /* */
44 /* This function changes the priority of the specified thread for the */
45 /* priority inheritance option of the mutex service. */
46 /* */
47 /* INPUT */
48 /* */
49 /* thread_ptr Pointer to thread to suspend */
50 /* new_priority New thread priority */
51 /* */
52 /* OUTPUT */
53 /* */
54 /* None */
55 /* */
56 /* CALLS */
57 /* */
58 /* _tx_thread_system_resume Resume thread */
59 /* _tx_thread_system_ni_resume Non-interruptable resume thread */
60 /* _tx_thread_system_suspend Suspend thread */
61 /* _tx_thread_system_ni_suspend Non-interruptable suspend thread */
62 /* */
63 /* CALLED BY */
64 /* */
65 /* _tx_mutex_get Inherit priority */
66 /* _tx_mutex_put Restore previous priority */
67 /* */
68 /* RELEASE HISTORY */
69 /* */
70 /* DATE NAME DESCRIPTION */
71 /* */
72 /* 05-19-2020 William E. Lamie Initial Version 6.0 */
73 /* 09-30-2020 William E. Lamie Modified comment(s), and */
74 /* change thread state from */
75 /* TX_SUSPENDED to */
76 /* TX_PRIORITY_CHANGE before */
77 /* calling */
78 /* _tx_thread_system_suspend, */
79 /* resulting in version 6.1 */
80 /* 04-02-2021 Scott Larson Modified comments, fixed */
81 /* mapping current thread's */
82 /* priority rather than next, */
83 /* resulting in version 6.1.6 */
84 /* */
85 /**************************************************************************/
_tx_mutex_priority_change(TX_THREAD * thread_ptr,UINT new_priority)86 VOID _tx_mutex_priority_change(TX_THREAD *thread_ptr, UINT new_priority)
87 {
88
89 #ifndef TX_NOT_INTERRUPTABLE
90
91 TX_INTERRUPT_SAVE_AREA
92 #endif
93
94 TX_THREAD *execute_ptr;
95 TX_THREAD *next_execute_ptr;
96 UINT original_priority;
97 #ifndef TX_DISABLE_PREEMPTION_THRESHOLD
98 ULONG priority_bit;
99 #if TX_MAX_PRIORITIES > 32
100 UINT map_index;
101 #endif
102 #endif
103
104
105
106 #ifndef TX_NOT_INTERRUPTABLE
107
108 /* Lockout interrupts while the thread is being suspended. */
109 TX_DISABLE
110 #endif
111
112 /* Determine if this thread is currently ready. */
113 if (thread_ptr -> tx_thread_state != TX_READY)
114 {
115
116 /* Change thread priority to the new mutex priority-inheritance priority. */
117 thread_ptr -> tx_thread_priority = new_priority;
118
119 /* Determine how to setup the thread's preemption-threshold. */
120 if (thread_ptr -> tx_thread_user_preempt_threshold < new_priority)
121 {
122
123 /* Change thread preemption-threshold to the user's preemption-threshold. */
124 thread_ptr -> tx_thread_preempt_threshold = thread_ptr -> tx_thread_user_preempt_threshold;
125 }
126 else
127 {
128
129 /* Change the thread preemption-threshold to the new threshold. */
130 thread_ptr -> tx_thread_preempt_threshold = new_priority;
131 }
132
133 #ifndef TX_NOT_INTERRUPTABLE
134 /* Restore interrupts. */
135 TX_RESTORE
136 #endif
137 }
138 else
139 {
140
141 /* Pickup the next thread to execute. */
142 execute_ptr = _tx_thread_execute_ptr;
143
144 /* Save the original priority. */
145 original_priority = thread_ptr -> tx_thread_priority;
146
147 #ifdef TX_NOT_INTERRUPTABLE
148
149 /* Increment the preempt disable flag. */
150 _tx_thread_preempt_disable++;
151
152 /* Set the state to priority change. */
153 thread_ptr -> tx_thread_state = TX_PRIORITY_CHANGE;
154
155 /* Call actual non-interruptable thread suspension routine. */
156 _tx_thread_system_ni_suspend(thread_ptr, ((ULONG) 0));
157
158 /* At this point, the preempt disable flag is still set, so we still have
159 protection against all preemption. */
160
161 /* Change thread priority to the new mutex priority-inheritance priority. */
162 thread_ptr -> tx_thread_priority = new_priority;
163
164 /* Determine how to setup the thread's preemption-threshold. */
165 if (thread_ptr -> tx_thread_user_preempt_threshold < new_priority)
166 {
167
168 /* Change thread preemption-threshold to the user's preemption-threshold. */
169 thread_ptr -> tx_thread_preempt_threshold = thread_ptr -> tx_thread_user_preempt_threshold;
170 }
171 else
172 {
173
174 /* Change the thread preemption-threshold to the new threshold. */
175 thread_ptr -> tx_thread_preempt_threshold = new_priority;
176 }
177
178 /* Resume the thread with the new priority. */
179 _tx_thread_system_ni_resume(thread_ptr);
180
181 /* Decrement the preempt disable flag. */
182 _tx_thread_preempt_disable--;
183 #else
184
185 /* Increment the preempt disable flag. */
186 _tx_thread_preempt_disable = _tx_thread_preempt_disable + ((UINT) 2);
187
188 /* Set the state to priority change. */
189 thread_ptr -> tx_thread_state = TX_PRIORITY_CHANGE;
190
191 /* Set the suspending flag. */
192 thread_ptr -> tx_thread_suspending = TX_TRUE;
193
194 /* Setup the timeout period. */
195 thread_ptr -> tx_thread_timer.tx_timer_internal_remaining_ticks = ((ULONG) 0);
196
197 /* Restore interrupts. */
198 TX_RESTORE
199
200 /* The thread is ready and must first be removed from the list. Call the
201 system suspend function to accomplish this. */
202 _tx_thread_system_suspend(thread_ptr);
203
204 /* Disable interrupts. */
205 TX_DISABLE
206
207 /* At this point, the preempt disable flag is still set, so we still have
208 protection against all preemption. */
209
210 /* Change thread priority to the new mutex priority-inheritance priority. */
211 thread_ptr -> tx_thread_priority = new_priority;
212
213 /* Determine how to setup the thread's preemption-threshold. */
214 if (thread_ptr -> tx_thread_user_preempt_threshold < new_priority)
215 {
216
217 /* Change thread preemption-threshold to the user's preemption-threshold. */
218 thread_ptr -> tx_thread_preempt_threshold = thread_ptr -> tx_thread_user_preempt_threshold;
219 }
220 else
221 {
222
223 /* Change the thread preemption-threshold to the new threshold. */
224 thread_ptr -> tx_thread_preempt_threshold = new_priority;
225 }
226
227 /* Restore interrupts. */
228 TX_RESTORE
229
230 /* Resume the thread with the new priority. */
231 _tx_thread_system_resume(thread_ptr);
232 #endif
233
234 /* Optional processing extension. */
235 TX_MUTEX_PRIORITY_CHANGE_EXTENSION
236
237 #ifndef TX_NOT_INTERRUPTABLE
238
239 /* Disable interrupts. */
240 TX_DISABLE
241 #endif
242
243 /* Pickup the next thread to execute. */
244 next_execute_ptr = _tx_thread_execute_ptr;
245
246 /* Determine if this thread is not the next thread to execute. */
247 if (thread_ptr != next_execute_ptr)
248 {
249
250 /* Make sure the thread is still ready. */
251 if (thread_ptr -> tx_thread_state == TX_READY)
252 {
253
254 /* Now check and see if this thread has an equal or higher priority. */
255 if (thread_ptr -> tx_thread_priority <= next_execute_ptr -> tx_thread_priority)
256 {
257
258 /* Now determine if this thread was the previously executing thread. */
259 if (thread_ptr == execute_ptr)
260 {
261
262 /* Yes, this thread was previously executing before we temporarily suspended and resumed
263 it in order to change the priority. A lower or same priority thread cannot be the next thread
264 to execute in this case since this thread really didn't suspend. Simply reset the execute
265 pointer to this thread. */
266 _tx_thread_execute_ptr = thread_ptr;
267
268 /* Determine if we moved to a lower priority. If so, move the thread to the front of its priority list. */
269 if (original_priority < new_priority)
270 {
271
272 /* Ensure that this thread is placed at the front of the priority list. */
273 _tx_thread_priority_list[thread_ptr -> tx_thread_priority] = thread_ptr;
274 }
275 }
276 }
277 else
278 {
279
280 /* Now determine if this thread's preemption-threshold needs to be enforced. */
281 if (thread_ptr -> tx_thread_preempt_threshold < thread_ptr -> tx_thread_priority)
282 {
283
284 /* Yes, preemption-threshold is in force for this thread. */
285
286 /* Compare the next thread to execute thread's priority against the thread's preemption-threshold. */
287 if (thread_ptr -> tx_thread_preempt_threshold <= next_execute_ptr -> tx_thread_priority)
288 {
289
290 /* We must swap execute pointers to enforce the preemption-threshold of a thread coming out of
291 priority inheritance. */
292 _tx_thread_execute_ptr = thread_ptr;
293
294 /* Determine if we moved to a lower priority. If so, move the thread to the front of its priority list. */
295 if (original_priority < new_priority)
296 {
297
298 /* Ensure that this thread is placed at the front of the priority list. */
299 _tx_thread_priority_list[thread_ptr -> tx_thread_priority] = thread_ptr;
300 }
301 }
302
303 #ifndef TX_DISABLE_PREEMPTION_THRESHOLD
304
305 else
306 {
307
308 /* In this case, we need to mark the preempted map to indicate a thread executed above the
309 preemption-threshold. */
310
311 #if TX_MAX_PRIORITIES > 32
312
313 /* Calculate the index into the bit map array. */
314 map_index = (thread_ptr -> tx_thread_priority)/ ((UINT) 32);
315
316 /* Set the active bit to remember that the preempt map has something set. */
317 TX_DIV32_BIT_SET(thread_ptr -> tx_thread_priority, priority_bit)
318 _tx_thread_preempted_map_active = _tx_thread_preempted_map_active | priority_bit;
319 #endif
320
321 /* Remember that this thread was preempted by a thread above the thread's threshold. */
322 TX_MOD32_BIT_SET(thread_ptr -> tx_thread_priority, priority_bit)
323 _tx_thread_preempted_maps[MAP_INDEX] = _tx_thread_preempted_maps[MAP_INDEX] | priority_bit;
324 }
325 #endif
326 }
327 }
328 }
329 }
330
331 #ifndef TX_NOT_INTERRUPTABLE
332
333 /* Restore interrupts. */
334 TX_RESTORE
335 #endif
336 }
337 }
338
339