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_trace.h"
29 #include "tx_thread.h"
30 #include "tx_mutex.h"
31 
32 
33 /**************************************************************************/
34 /*                                                                        */
35 /*  FUNCTION                                               RELEASE        */
36 /*                                                                        */
37 /*    _tx_mutex_get                                       PORTABLE C      */
38 /*                                                           6.1          */
39 /*  AUTHOR                                                                */
40 /*                                                                        */
41 /*    William E. Lamie, Microsoft Corporation                             */
42 /*                                                                        */
43 /*  DESCRIPTION                                                           */
44 /*                                                                        */
45 /*    This function gets the specified mutex.  If the calling thread      */
46 /*    already owns the mutex, an ownership count is simply increased.     */
47 /*                                                                        */
48 /*  INPUT                                                                 */
49 /*                                                                        */
50 /*    mutex_ptr                         Pointer to mutex control block    */
51 /*    wait_option                       Suspension option                 */
52 /*                                                                        */
53 /*  OUTPUT                                                                */
54 /*                                                                        */
55 /*    status                            Completion status                 */
56 /*                                                                        */
57 /*  CALLS                                                                 */
58 /*                                                                        */
59 /*    _tx_thread_system_suspend         Suspend thread service            */
60 /*    _tx_thread_system_ni_suspend      Non-interruptable suspend thread  */
61 /*    _tx_mutex_priority_change         Inherit thread priority           */
62 /*                                                                        */
63 /*  CALLED BY                                                             */
64 /*                                                                        */
65 /*    Application Code                                                    */
66 /*                                                                        */
67 /*  RELEASE HISTORY                                                       */
68 /*                                                                        */
69 /*    DATE              NAME                      DESCRIPTION             */
70 /*                                                                        */
71 /*  05-19-2020     William E. Lamie         Initial Version 6.0           */
72 /*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
73 /*                                            resulting in version 6.1    */
74 /*                                                                        */
75 /**************************************************************************/
_tx_mutex_get(TX_MUTEX * mutex_ptr,ULONG wait_option)76 UINT  _tx_mutex_get(TX_MUTEX *mutex_ptr, ULONG wait_option)
77 {
78 
79 TX_INTERRUPT_SAVE_AREA
80 
81 TX_THREAD       *thread_ptr;
82 TX_MUTEX        *next_mutex;
83 TX_MUTEX        *previous_mutex;
84 TX_THREAD       *mutex_owner;
85 TX_THREAD       *next_thread;
86 TX_THREAD       *previous_thread;
87 UINT            status;
88 
89 
90     /* Disable interrupts to get an instance from the mutex.  */
91     TX_DISABLE
92 
93 #ifdef TX_MUTEX_ENABLE_PERFORMANCE_INFO
94 
95     /* Increment the total mutex get counter.  */
96     _tx_mutex_performance_get_count++;
97 
98     /* Increment the number of attempts to get this mutex.  */
99     mutex_ptr -> tx_mutex_performance_get_count++;
100 #endif
101 
102     /* If trace is enabled, insert this event into the trace buffer.  */
103     TX_TRACE_IN_LINE_INSERT(TX_TRACE_MUTEX_GET, mutex_ptr, wait_option, TX_POINTER_TO_ULONG_CONVERT(mutex_ptr -> tx_mutex_owner), mutex_ptr -> tx_mutex_ownership_count, TX_TRACE_MUTEX_EVENTS)
104 
105     /* Log this kernel call.  */
106     TX_EL_MUTEX_GET_INSERT
107 
108     /* Pickup thread pointer.  */
109     TX_THREAD_GET_CURRENT(thread_ptr)
110 
111     /* Determine if this mutex is available.  */
112     if (mutex_ptr -> tx_mutex_ownership_count == ((UINT) 0))
113     {
114 
115         /* Set the ownership count to 1.  */
116         mutex_ptr -> tx_mutex_ownership_count =  ((UINT) 1);
117 
118         /* Remember that the calling thread owns the mutex.  */
119         mutex_ptr -> tx_mutex_owner =  thread_ptr;
120 
121         /* Determine if the thread pointer is valid.  */
122         if (thread_ptr != TX_NULL)
123         {
124 
125             /* Determine if priority inheritance is required.  */
126             if (mutex_ptr -> tx_mutex_inherit == TX_TRUE)
127             {
128 
129                 /* Remember the current priority of thread.  */
130                 mutex_ptr -> tx_mutex_original_priority =   thread_ptr -> tx_thread_priority;
131 
132                 /* Setup the highest priority waiting thread.  */
133                 mutex_ptr -> tx_mutex_highest_priority_waiting =  ((UINT) TX_MAX_PRIORITIES);
134             }
135 
136             /* Pickup next mutex pointer, which is the head of the list.  */
137             next_mutex =  thread_ptr -> tx_thread_owned_mutex_list;
138 
139             /* Determine if this thread owns any other mutexes that have priority inheritance.  */
140             if (next_mutex != TX_NULL)
141             {
142 
143                 /* Non-empty list. Link up the mutex.  */
144 
145                 /* Pickup the next and previous mutex pointer.  */
146                 previous_mutex =  next_mutex -> tx_mutex_owned_previous;
147 
148                 /* Place the owned mutex in the list.  */
149                 next_mutex -> tx_mutex_owned_previous =  mutex_ptr;
150                 previous_mutex -> tx_mutex_owned_next =  mutex_ptr;
151 
152                 /* Setup this mutex's next and previous created links.  */
153                 mutex_ptr -> tx_mutex_owned_previous =  previous_mutex;
154                 mutex_ptr -> tx_mutex_owned_next =      next_mutex;
155             }
156             else
157             {
158 
159                 /* The owned mutex list is empty.  Add mutex to empty list.  */
160                 thread_ptr -> tx_thread_owned_mutex_list =     mutex_ptr;
161                 mutex_ptr -> tx_mutex_owned_next =             mutex_ptr;
162                 mutex_ptr -> tx_mutex_owned_previous =         mutex_ptr;
163             }
164 
165             /* Increment the number of mutexes owned counter.  */
166             thread_ptr -> tx_thread_owned_mutex_count++;
167         }
168 
169         /* Restore interrupts.  */
170         TX_RESTORE
171 
172         /* Return success.  */
173         status =  TX_SUCCESS;
174     }
175 
176     /* Otherwise, see if the owning thread is trying to obtain the same mutex.  */
177     else if (mutex_ptr -> tx_mutex_owner == thread_ptr)
178     {
179 
180         /* The owning thread is requesting the mutex again, just
181            increment the ownership count.  */
182         mutex_ptr -> tx_mutex_ownership_count++;
183 
184         /* Restore interrupts.  */
185         TX_RESTORE
186 
187         /* Return success.  */
188         status =  TX_SUCCESS;
189     }
190     else
191     {
192 
193         /* Determine if the request specifies suspension.  */
194         if (wait_option != TX_NO_WAIT)
195         {
196 
197             /* Determine if the preempt disable flag is non-zero.  */
198             if (_tx_thread_preempt_disable != ((UINT) 0))
199             {
200 
201                 /* Restore interrupts.  */
202                 TX_RESTORE
203 
204                 /* Suspension is not allowed if the preempt disable flag is non-zero at this point - return error completion.  */
205                 status =  TX_NOT_AVAILABLE;
206             }
207             else
208             {
209 
210                 /* Prepare for suspension of this thread.  */
211 
212                 /* Pickup the mutex owner.  */
213                 mutex_owner =  mutex_ptr -> tx_mutex_owner;
214 
215 #ifdef TX_MUTEX_ENABLE_PERFORMANCE_INFO
216 
217                 /* Increment the total mutex suspension counter.  */
218                 _tx_mutex_performance_suspension_count++;
219 
220                 /* Increment the number of suspensions on this mutex.  */
221                 mutex_ptr -> tx_mutex_performance_suspension_count++;
222 
223                 /* Determine if a priority inversion is present.  */
224                 if (thread_ptr -> tx_thread_priority < mutex_owner -> tx_thread_priority)
225                 {
226 
227                     /* Yes, priority inversion is present!  */
228 
229                     /* Increment the total mutex priority inversions counter.  */
230                     _tx_mutex_performance_priority_inversion_count++;
231 
232                     /* Increment the number of priority inversions on this mutex.  */
233                     mutex_ptr -> tx_mutex_performance_priority_inversion_count++;
234 
235 #ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
236 
237                     /* Increment the number of total thread priority inversions.  */
238                     _tx_thread_performance_priority_inversion_count++;
239 
240                     /* Increment the number of priority inversions for this thread.  */
241                     thread_ptr -> tx_thread_performance_priority_inversion_count++;
242 #endif
243                 }
244 #endif
245 
246                 /* Setup cleanup routine pointer.  */
247                 thread_ptr -> tx_thread_suspend_cleanup =  &(_tx_mutex_cleanup);
248 
249                 /* Setup cleanup information, i.e. this mutex control
250                    block.  */
251                 thread_ptr -> tx_thread_suspend_control_block =  (VOID *) mutex_ptr;
252 
253 #ifndef TX_NOT_INTERRUPTABLE
254 
255                 /* Increment the suspension sequence number, which is used to identify
256                    this suspension event.  */
257                 thread_ptr -> tx_thread_suspension_sequence++;
258 #endif
259 
260                 /* Setup suspension list.  */
261                 if (mutex_ptr -> tx_mutex_suspended_count == TX_NO_SUSPENSIONS)
262                 {
263 
264                     /* No other threads are suspended.  Setup the head pointer and
265                        just setup this threads pointers to itself.  */
266                     mutex_ptr -> tx_mutex_suspension_list =         thread_ptr;
267                     thread_ptr -> tx_thread_suspended_next =        thread_ptr;
268                     thread_ptr -> tx_thread_suspended_previous =    thread_ptr;
269                 }
270                 else
271                 {
272 
273                     /* This list is not NULL, add current thread to the end. */
274                     next_thread =                                   mutex_ptr -> tx_mutex_suspension_list;
275                     thread_ptr -> tx_thread_suspended_next =        next_thread;
276                     previous_thread =                               next_thread -> tx_thread_suspended_previous;
277                     thread_ptr -> tx_thread_suspended_previous =    previous_thread;
278                     previous_thread -> tx_thread_suspended_next =   thread_ptr;
279                     next_thread -> tx_thread_suspended_previous =   thread_ptr;
280                 }
281 
282                 /* Increment the suspension count.  */
283                 mutex_ptr -> tx_mutex_suspended_count++;
284 
285                 /* Set the state to suspended.  */
286                 thread_ptr -> tx_thread_state =    TX_MUTEX_SUSP;
287 
288 #ifdef TX_NOT_INTERRUPTABLE
289 
290                 /* Determine if we need to raise the priority of the thread
291                    owning the mutex.  */
292                 if (mutex_ptr -> tx_mutex_inherit == TX_TRUE)
293                 {
294 
295                     /* Determine if this is the highest priority to raise for this mutex.  */
296                     if (mutex_ptr -> tx_mutex_highest_priority_waiting > thread_ptr -> tx_thread_priority)
297                     {
298 
299                         /* Remember this priority.  */
300                         mutex_ptr -> tx_mutex_highest_priority_waiting =  thread_ptr -> tx_thread_priority;
301                     }
302 
303                     /* Determine if we have to update inherit priority level of the mutex owner.  */
304                     if (thread_ptr -> tx_thread_priority < mutex_owner -> tx_thread_inherit_priority)
305                     {
306 
307                         /* Remember the new priority inheritance priority.  */
308                         mutex_owner -> tx_thread_inherit_priority =  thread_ptr -> tx_thread_priority;
309                     }
310 
311                     /* Priority inheritance is requested, check to see if the thread that owns the mutex is lower priority.  */
312                     if (mutex_owner -> tx_thread_priority > thread_ptr -> tx_thread_priority)
313                     {
314 
315                         /* Yes, raise the suspended, owning thread's priority to that
316                            of the current thread.  */
317                         _tx_mutex_priority_change(mutex_owner, thread_ptr -> tx_thread_priority);
318 
319 #ifdef TX_MUTEX_ENABLE_PERFORMANCE_INFO
320 
321                         /* Increment the total mutex priority inheritance counter.  */
322                         _tx_mutex_performance__priority_inheritance_count++;
323 
324                         /* Increment the number of priority inheritance situations on this mutex.  */
325                         mutex_ptr -> tx_mutex_performance__priority_inheritance_count++;
326 #endif
327                     }
328                 }
329 
330                 /* Call actual non-interruptable thread suspension routine.  */
331                 _tx_thread_system_ni_suspend(thread_ptr, wait_option);
332 
333                 /* Restore interrupts.  */
334                 TX_RESTORE
335 #else
336 
337                 /* Set the suspending flag.  */
338                 thread_ptr -> tx_thread_suspending =  TX_TRUE;
339 
340                 /* Setup the timeout period.  */
341                 thread_ptr -> tx_thread_timer.tx_timer_internal_remaining_ticks =  wait_option;
342 
343                 /* Temporarily disable preemption.  */
344                 _tx_thread_preempt_disable++;
345 
346                 /* Restore interrupts.  */
347                 TX_RESTORE
348 
349                 /* Determine if we need to raise the priority of the thread
350                    owning the mutex.  */
351                 if (mutex_ptr -> tx_mutex_inherit == TX_TRUE)
352                 {
353 
354                     /* Determine if this is the highest priority to raise for this mutex.  */
355                     if (mutex_ptr -> tx_mutex_highest_priority_waiting > thread_ptr -> tx_thread_priority)
356                     {
357 
358                         /* Remember this priority.  */
359                         mutex_ptr -> tx_mutex_highest_priority_waiting =  thread_ptr -> tx_thread_priority;
360                     }
361 
362                     /* Determine if we have to update inherit priority level of the mutex owner.  */
363                     if (thread_ptr -> tx_thread_priority < mutex_owner -> tx_thread_inherit_priority)
364                     {
365 
366                         /* Remember the new priority inheritance priority.  */
367                         mutex_owner -> tx_thread_inherit_priority =  thread_ptr -> tx_thread_priority;
368                     }
369 
370                     /* Priority inheritance is requested, check to see if the thread that owns the mutex is lower priority.  */
371                     if (mutex_owner -> tx_thread_priority > thread_ptr -> tx_thread_priority)
372                     {
373 
374                         /* Yes, raise the suspended, owning thread's priority to that
375                            of the current thread.  */
376                         _tx_mutex_priority_change(mutex_owner, thread_ptr -> tx_thread_priority);
377 
378 #ifdef TX_MUTEX_ENABLE_PERFORMANCE_INFO
379 
380                         /* Increment the total mutex priority inheritance counter.  */
381                         _tx_mutex_performance__priority_inheritance_count++;
382 
383                         /* Increment the number of priority inheritance situations on this mutex.  */
384                         mutex_ptr -> tx_mutex_performance__priority_inheritance_count++;
385 #endif
386                     }
387                 }
388 
389                 /* Call actual thread suspension routine.  */
390                 _tx_thread_system_suspend(thread_ptr);
391 #endif
392                 /* Return the completion status.  */
393                 status =  thread_ptr -> tx_thread_suspend_status;
394             }
395         }
396         else
397         {
398 
399             /* Restore interrupts.  */
400             TX_RESTORE
401 
402             /* Immediate return, return error completion.  */
403             status =  TX_NOT_AVAILABLE;
404         }
405     }
406 
407     /* Return completion status.  */
408     return(status);
409 }
410 
411