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 #define TX_THREAD_SMP_SOURCE_CODE
24 
25 
26 /* Include necessary system files.  */
27 
28 #include "tx_api.h"
29 #include "tx_thread.h"
30 #include "tx_mutex.h"
31 
32 
33 /**************************************************************************/
34 /*                                                                        */
35 /*  FUNCTION                                               RELEASE        */
36 /*                                                                        */
37 /*    _tx_mutex_priority_change                          PORTABLE SMP     */
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 for the  */
46 /*    priority inheritance option of the mutex service.                   */
47 /*                                                                        */
48 /*  INPUT                                                                 */
49 /*                                                                        */
50 /*    thread_ptr                            Pointer to thread to suspend  */
51 /*    new_priority                          New thread priority           */
52 /*    new_threshold                         New preemption-threshold      */
53 /*                                                                        */
54 /*  OUTPUT                                                                */
55 /*                                                                        */
56 /*    None                                                                */
57 /*                                                                        */
58 /*  CALLS                                                                 */
59 /*                                                                        */
60 /*    _tx_thread_smp_rebalance_execute_list                               */
61 /*                                      Rebalance the execution list      */
62 /*    _tx_thread_smp_simple_priority_change                               */
63 /*                                      Change priority                   */
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 /*                                                                        */
69 /*  CALLED BY                                                             */
70 /*                                                                        */
71 /*    _tx_mutex_get                     Inherit priority                  */
72 /*    _tx_mutex_put                     Restore previous priority         */
73 /*                                                                        */
74 /*  RELEASE HISTORY                                                       */
75 /*                                                                        */
76 /*    DATE              NAME                      DESCRIPTION             */
77 /*                                                                        */
78 /*  09-30-2020     William E. Lamie         Initial Version 6.1           */
79 /*                                                                        */
80 /**************************************************************************/
_tx_mutex_priority_change(TX_THREAD * thread_ptr,UINT new_priority)81 VOID  _tx_mutex_priority_change(TX_THREAD *thread_ptr, UINT new_priority)
82 {
83 
84 #ifndef TX_NOT_INTERRUPTABLE
85 
86 TX_INTERRUPT_SAVE_AREA
87 #endif
88 
89 TX_THREAD       *execute_ptr;
90 UINT            core_index;
91 UINT            original_priority;
92 UINT            lowest_priority;
93 TX_THREAD       *original_pt_thread;
94 #ifndef TX_DISABLE_PREEMPTION_THRESHOLD
95 TX_THREAD       *new_pt_thread;
96 UINT            priority;
97 ULONG           priority_bit;
98 #if TX_MAX_PRIORITIES > 32
99 UINT            map_index;
100 #endif
101 #endif
102 UINT            finished;
103 
104 
105     /* Default finished to false.  */
106     finished =  TX_FALSE;
107 
108 #ifndef TX_NOT_INTERRUPTABLE
109 
110     /* Lockout interrupts while the thread is being suspended.  */
111     TX_DISABLE
112 #endif
113 
114     /* Determine if there is anything to do.  */
115     if (thread_ptr -> tx_thread_priority == new_priority)
116     {
117 
118         if (thread_ptr -> tx_thread_preempt_threshold == new_priority)
119         {
120 
121             /* Set the finished flag to true.  */
122             finished =  TX_TRUE;
123         }
124     }
125 
126     /* Determine if there is still more to do.  */
127     if (finished == TX_FALSE)
128     {
129 
130         /* Default the execute pointer to NULL.  */
131         execute_ptr =  TX_NULL;
132 
133         /* Determine if this thread is currently ready.  */
134         if (thread_ptr -> tx_thread_state != TX_READY)
135         {
136 
137             /* Change thread priority to the new mutex priority-inheritance priority.  */
138             thread_ptr -> tx_thread_priority =  new_priority;
139 
140             /* Determine how to setup the thread's preemption-threshold.  */
141             if (thread_ptr -> tx_thread_user_preempt_threshold < new_priority)
142             {
143 
144                 /* Change thread preemption-threshold to the user's preemption-threshold.  */
145                 thread_ptr -> tx_thread_preempt_threshold =  thread_ptr -> tx_thread_user_preempt_threshold;
146             }
147             else
148             {
149 
150                 /* Change the thread preemption-threshold to the new threshold.  */
151                 thread_ptr -> tx_thread_preempt_threshold =  new_priority;
152             }
153 
154         }
155         else
156         {
157 
158             /* Pickup the core index.  */
159             core_index =  thread_ptr -> tx_thread_smp_core_mapped;
160 
161             /* Save the original priority.  */
162             original_priority =  thread_ptr -> tx_thread_priority;
163 
164             /* Determine if this thread is the currently executing thread.  */
165             if (thread_ptr == _tx_thread_execute_ptr[core_index])
166             {
167 
168                 /* Yes, this thread is scheduled.  */
169 
170                 /* Remember this thread as the currently executing thread.  */
171                 execute_ptr =  thread_ptr;
172 
173                 /* Determine if the thread is being set to a higher-priority and it does't have
174                    preemption-threshold set.  */
175                 if (new_priority < thread_ptr -> tx_thread_priority)
176                 {
177 
178                     /* Check for preemption-threshold.  */
179                     if (thread_ptr -> tx_thread_user_priority == thread_ptr -> tx_thread_user_preempt_threshold)
180                     {
181 
182                         /* Simple case, remove the thread from the current priority list and place in
183                            the higher priority list.   */
184                         _tx_thread_smp_simple_priority_change(thread_ptr, new_priority);
185 
186                         /* Set the finished flag to true.  */
187                         finished =  TX_TRUE;
188                     }
189                 }
190             }
191             else
192             {
193 
194                 /* Thread is not currently executing, so it can just be moved to the lower priority in the list.  */
195 
196                 /* Determine if the thread is being set to a lower-priority and it does't have
197                    preemption-threshold set.  */
198                 if (new_priority > thread_ptr -> tx_thread_priority)
199                 {
200 
201                     /* Check for preemption-threshold.  */
202                     if (thread_ptr -> tx_thread_user_priority == thread_ptr -> tx_thread_user_preempt_threshold)
203                     {
204 
205                         /* Simple case, remove the thread from the current priority list and place in
206                            the lower priority list.   */
207                         if (new_priority < thread_ptr -> tx_thread_user_priority)
208                         {
209 
210                             /* Use the new priority.  */
211                             _tx_thread_smp_simple_priority_change(thread_ptr, new_priority);
212                         }
213                         else
214                         {
215 
216                             /* Use the user priority.  */
217                             _tx_thread_smp_simple_priority_change(thread_ptr, thread_ptr -> tx_thread_user_priority);
218                         }
219 
220                         /* Set the finished flag to true.  */
221                         finished =  TX_TRUE;
222                     }
223                 }
224             }
225 
226             /* Now determine if we are finished.  */
227             if (finished == TX_FALSE)
228             {
229 
230                 /* Save the original preemption-threshold thread.  */
231                 original_pt_thread =  _tx_thread_preemption__threshold_scheduled;
232 
233 #ifdef TX_NOT_INTERRUPTABLE
234 
235                 /* Increment the preempt disable flag.  */
236                 _tx_thread_preempt_disable++;
237 
238                 /* Set the state to priority change.  */
239                 thread_ptr -> tx_thread_state =    TX_PRIORITY_CHANGE;
240 
241                 /* Call actual non-interruptable thread suspension routine.  */
242                 _tx_thread_system_ni_suspend(thread_ptr, ((ULONG) 0));
243 
244                 /* At this point, the preempt disable flag is still set, so we still have
245                    protection against all preemption.  */
246 
247                 /* Determine how to setup the thread's priority.  */
248                 if (thread_ptr -> tx_thread_user_priority < new_priority)
249                 {
250 
251                     /* Change thread priority to the user's priority.  */
252                     thread_ptr -> tx_thread_priority =  thread_ptr -> tx_thread_user_priority;
253                 }
254                 else
255                 {
256 
257                     /* Change thread priority to the new mutex priority-inheritance priority.  */
258                     thread_ptr -> tx_thread_priority =  new_priority;
259                 }
260 
261                 /* Determine how to setup the thread's preemption-threshold.  */
262                 if (thread_ptr -> tx_thread_user_preempt_threshold < new_priority)
263                 {
264 
265                     /* Change thread preemption-threshold to the user's preemption-threshold.  */
266                     thread_ptr -> tx_thread_preempt_threshold =  thread_ptr -> tx_thread_user_preempt_threshold;
267                 }
268                 else
269                 {
270 
271                     /* Change the thread preemption-threshold to the new threshold.  */
272                     thread_ptr -> tx_thread_preempt_threshold =  new_priority;
273                 }
274 
275                 /* Resume the thread with the new priority.  */
276                 _tx_thread_system_ni_resume(thread_ptr);
277 
278                 /* Decrement the preempt disable flag.  */
279                 _tx_thread_preempt_disable--;
280 #else
281 
282                 /* Increment the preempt disable flag.  */
283                 _tx_thread_preempt_disable =  _tx_thread_preempt_disable + ((UINT) 2);
284 
285                 /* Set the state to priority change.  */
286                 thread_ptr -> tx_thread_state =    TX_PRIORITY_CHANGE;
287 
288                 /* Set the suspending flag. */
289                 thread_ptr -> tx_thread_suspending =  TX_TRUE;
290 
291                 /* Setup the timeout period.  */
292                 thread_ptr -> tx_thread_timer.tx_timer_internal_remaining_ticks =  ((UINT) 0);
293 
294                 /* Restore interrupts.  */
295                 TX_RESTORE
296 
297                 /* The thread is ready and must first be removed from the list.  Call the
298                    system suspend function to accomplish this.  */
299                 _tx_thread_system_suspend(thread_ptr);
300 
301                 /* Lockout interrupts again.  */
302                 TX_DISABLE
303 
304                 /* At this point, the preempt disable flag is still set, so we still have
305                    protection against all preemption.  */
306 
307                 /* Determine how to setup the thread's priority.  */
308                 if (thread_ptr -> tx_thread_user_priority < new_priority)
309                 {
310 
311                     /* Change thread priority to the user's priority.  */
312                     thread_ptr -> tx_thread_priority =  thread_ptr -> tx_thread_user_priority;
313                 }
314                 else
315                 {
316 
317                     /* Change thread priority to the new mutex priority-inheritance priority.  */
318                     thread_ptr -> tx_thread_priority =  new_priority;
319                 }
320 
321                 /* Determine how to setup the thread's preemption-threshold.  */
322                 if (thread_ptr -> tx_thread_user_preempt_threshold < new_priority)
323                 {
324 
325                     /* Change thread preemption-threshold to the user's preemption-threshold.  */
326                     thread_ptr -> tx_thread_preempt_threshold =  thread_ptr -> tx_thread_user_preempt_threshold;
327                 }
328                 else
329                 {
330 
331                     /* Change the thread preemption-threshold to the new threshold.  */
332                     thread_ptr -> tx_thread_preempt_threshold =  new_priority;
333                 }
334 
335                 /* Restore interrupts.  */
336                 TX_RESTORE
337 
338                 /* Resume the thread with the new priority.  */
339                 _tx_thread_system_resume(thread_ptr);
340 #endif
341 
342                 /* Optional processing extension.  */
343                 TX_MUTEX_PRIORITY_CHANGE_EXTENSION
344 
345 #ifndef TX_NOT_INTERRUPTABLE
346 
347                 /* Disable interrupts.  */
348                 TX_DISABLE
349 #endif
350 
351                 /* Determine if the thread was previously executing.  */
352                 if (thread_ptr == execute_ptr)
353                 {
354 
355                     /* Make sure the thread is still ready.  */
356                     if (thread_ptr -> tx_thread_state == TX_READY)
357                     {
358 
359 #ifndef TX_DISABLE_PREEMPTION_THRESHOLD
360                         /* Determine if preemption-threshold is in force at the new priority level.  */
361                         if (_tx_thread_preemption_threshold_list[thread_ptr -> tx_thread_priority] == TX_NULL)
362                         {
363 
364                             /* Ensure that this thread is placed at the front of the priority list.  */
365                             _tx_thread_priority_list[thread_ptr -> tx_thread_priority] =  thread_ptr;
366                         }
367 #else
368 
369                         /* Ensure that this thread is placed at the front of the priority list.  */
370                         _tx_thread_priority_list[thread_ptr -> tx_thread_priority] =  thread_ptr;
371 #endif
372                     }
373                 }
374 
375                 /* Pickup the core index.  */
376                 core_index =  thread_ptr -> tx_thread_smp_core_mapped;
377 
378 #ifndef TX_THREAD_SMP_DYNAMIC_CORE_MAX
379 
380                 /* Pickup the next thread to execute.   */
381                 if (core_index < ((UINT) TX_THREAD_SMP_MAX_CORES))
382 #else
383 
384                 /* Pickup the next thread to execute.   */
385                 if (core_index < _tx_thread_smp_max_cores)
386 #endif
387                 {
388 
389                     /* Determine if this thread is not the next thread to execute.  */
390                     if (thread_ptr != _tx_thread_execute_ptr[core_index])
391                     {
392 
393                         /* Now determine if this thread was previously executing thread.  */
394                         if (thread_ptr == execute_ptr)
395                         {
396 
397                             /* Determine if we moved to a lower priority. If so, move the thread to the front of the priority list.  */
398                             if (original_priority < new_priority)
399                             {
400 
401                                 /* Make sure the thread is still ready.  */
402                                 if (thread_ptr -> tx_thread_state == TX_READY)
403                                 {
404 
405                                     /* Determine the lowest priority scheduled thread.  */
406                                     lowest_priority =  _tx_thread_smp_lowest_priority_get();
407 
408                                     /* Determine if this thread has a higher or same priority as the lowest priority
409                                        in the list.  */
410                                     if (thread_ptr -> tx_thread_priority <= lowest_priority)
411                                     {
412 
413                                         /* Yes, we need to rebalance to make it possible for this thread to execute.  */
414 
415                                         /* Determine if the thread with preemption-threshold thread has changed... and is
416                                            not the scheduled thread.   */
417                                         if ((original_pt_thread != _tx_thread_preemption__threshold_scheduled) &&
418                                             (original_pt_thread != thread_ptr))
419                                         {
420 
421                                             /* Yes, preemption-threshold has changed.  Determine if it can or should
422                                                be reversed.  */
423 
424 #ifndef TX_DISABLE_PREEMPTION_THRESHOLD
425 
426                                             /* Pickup the preemption-threshold thread.  */
427                                             new_pt_thread =  _tx_thread_preemption__threshold_scheduled;
428 #endif
429 
430                                             /* Restore the original preemption-threshold thread.  */
431                                             _tx_thread_preemption__threshold_scheduled =  original_pt_thread;
432 
433 
434 #ifndef TX_DISABLE_PREEMPTION_THRESHOLD
435 
436                                             /* Determine if there is a new preemption-threshold thread to reverse.  */
437                                             if (new_pt_thread != TX_NULL)
438                                             {
439 
440                                                 /* Clear the information associated with the new preemption-threshold thread.  */
441 
442                                                 /* Pickup the priority.  */
443                                                 priority =  new_pt_thread -> tx_thread_priority;
444 
445                                                 /* Clear the preempted list entry.  */
446                                                 _tx_thread_preemption_threshold_list[priority] =  TX_NULL;
447 
448 #if TX_MAX_PRIORITIES > 32
449                                                 /* Calculate the bit map array index.  */
450                                                 map_index =  new_priority/((UINT) 32);
451 #endif
452                                                 /* Ensure that this thread's priority is clear in the preempt map.  */
453                                                 TX_MOD32_BIT_SET(priority, priority_bit)
454                                                 _tx_thread_preempted_maps[MAP_INDEX] =  _tx_thread_preempted_maps[MAP_INDEX] & (~(priority_bit));
455 #if TX_MAX_PRIORITIES > 32
456 
457                                                 /* Determine if there are any other bits set in this preempt map.  */
458                                                 if (_tx_thread_preempted_maps[MAP_INDEX] == ((ULONG) 0))
459                                                 {
460 
461                                                     /* No, clear the active bit to signify this preempted map has nothing set.  */
462                                                     TX_DIV32_BIT_SET(priority, priority_bit)
463                                                     _tx_thread_preempted_map_active =  _tx_thread_preempted_map_active & (~(priority_bit));
464                                                 }
465 #endif
466                                             }
467 #endif
468                                         }
469 
470                                         /* Pickup the index.  */
471                                         core_index =  TX_SMP_CORE_ID;
472 
473                                         /* Call the rebalance routine. This routine maps cores and ready threads.  */
474                                         _tx_thread_smp_rebalance_execute_list(core_index);
475                                     }
476                                 }
477                             }
478                         }
479                     }
480                 }
481             }
482         }
483     }
484 
485 #ifndef TX_NOT_INTERRUPTABLE
486 
487     /* Restore interrupts.  */
488     TX_RESTORE
489 #endif
490 }
491 
492