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