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