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