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