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_put PORTABLE C */
38 /* 6.1 */
39 /* AUTHOR */
40 /* */
41 /* William E. Lamie, Microsoft Corporation */
42 /* */
43 /* DESCRIPTION */
44 /* */
45 /* This function puts back an instance of the specified mutex. */
46 /* */
47 /* INPUT */
48 /* */
49 /* mutex_ptr Pointer to mutex control block */
50 /* */
51 /* OUTPUT */
52 /* */
53 /* TX_SUCCESS Success completion status */
54 /* */
55 /* CALLS */
56 /* */
57 /* _tx_thread_system_preempt_check Check for preemption */
58 /* _tx_thread_system_resume Resume thread service */
59 /* _tx_thread_system_ni_resume Non-interruptable resume thread */
60 /* _tx_mutex_priority_change Restore previous thread priority */
61 /* _tx_mutex_prioritize Prioritize the mutex suspension */
62 /* _tx_mutex_thread_release Release all thread's mutexes */
63 /* _tx_mutex_delete Release ownership upon mutex */
64 /* deletion */
65 /* */
66 /* CALLED BY */
67 /* */
68 /* Application Code */
69 /* */
70 /* RELEASE HISTORY */
71 /* */
72 /* DATE NAME DESCRIPTION */
73 /* */
74 /* 05-19-2020 William E. Lamie Initial Version 6.0 */
75 /* 09-30-2020 Yuxin Zhou Modified comment(s), */
76 /* resulting in version 6.1 */
77 /* */
78 /**************************************************************************/
_tx_mutex_put(TX_MUTEX * mutex_ptr)79 UINT _tx_mutex_put(TX_MUTEX *mutex_ptr)
80 {
81
82 TX_INTERRUPT_SAVE_AREA
83
84 TX_THREAD *thread_ptr;
85 TX_THREAD *old_owner;
86 UINT old_priority;
87 UINT status;
88 TX_MUTEX *next_mutex;
89 TX_MUTEX *previous_mutex;
90 UINT owned_count;
91 UINT suspended_count;
92 TX_THREAD *current_thread;
93 TX_THREAD *next_thread;
94 TX_THREAD *previous_thread;
95 TX_THREAD *suspended_thread;
96 UINT inheritance_priority;
97
98
99 /* Setup status to indicate the processing is not complete. */
100 status = TX_NOT_DONE;
101
102 /* Disable interrupts to put an instance back to the mutex. */
103 TX_DISABLE
104
105 #ifdef TX_MUTEX_ENABLE_PERFORMANCE_INFO
106
107 /* Increment the total mutex put counter. */
108 _tx_mutex_performance_put_count++;
109
110 /* Increment the number of attempts to put this mutex. */
111 mutex_ptr -> tx_mutex_performance_put_count++;
112 #endif
113
114 /* If trace is enabled, insert this event into the trace buffer. */
115 TX_TRACE_IN_LINE_INSERT(TX_TRACE_MUTEX_PUT, mutex_ptr, TX_POINTER_TO_ULONG_CONVERT(mutex_ptr -> tx_mutex_owner), mutex_ptr -> tx_mutex_ownership_count, TX_POINTER_TO_ULONG_CONVERT(&old_priority), TX_TRACE_MUTEX_EVENTS)
116
117 /* Log this kernel call. */
118 TX_EL_MUTEX_PUT_INSERT
119
120 /* Determine if this mutex is owned. */
121 if (mutex_ptr -> tx_mutex_ownership_count != ((UINT) 0))
122 {
123
124 /* Pickup the owning thread pointer. */
125 thread_ptr = mutex_ptr -> tx_mutex_owner;
126
127 /* Pickup thread pointer. */
128 TX_THREAD_GET_CURRENT(current_thread)
129
130 /* Check to see if the mutex is owned by the calling thread. */
131 if (mutex_ptr -> tx_mutex_owner != current_thread)
132 {
133
134 /* Determine if the preempt disable flag is set, indicating that
135 the caller is not the application but from ThreadX. In such
136 cases, the thread mutex owner does not need to match. */
137 if (_tx_thread_preempt_disable == ((UINT) 0))
138 {
139
140 /* Invalid mutex release. */
141
142 /* Restore interrupts. */
143 TX_RESTORE
144
145 /* Caller does not own the mutex. */
146 status = TX_NOT_OWNED;
147 }
148 }
149
150 /* Determine if we should continue. */
151 if (status == TX_NOT_DONE)
152 {
153
154 /* Decrement the mutex ownership count. */
155 mutex_ptr -> tx_mutex_ownership_count--;
156
157 /* Determine if the mutex is still owned by the current thread. */
158 if (mutex_ptr -> tx_mutex_ownership_count != ((UINT) 0))
159 {
160
161 /* Restore interrupts. */
162 TX_RESTORE
163
164 /* Mutex is still owned, just return successful status. */
165 status = TX_SUCCESS;
166 }
167 else
168 {
169
170 /* Check for a NULL thread pointer, which can only happen during initialization. */
171 if (thread_ptr == TX_NULL)
172 {
173
174 /* Restore interrupts. */
175 TX_RESTORE
176
177 /* Mutex is now available, return successful status. */
178 status = TX_SUCCESS;
179 }
180 else
181 {
182
183 /* The mutex is now available. */
184
185 /* Remove this mutex from the owned mutex list. */
186
187 /* Decrement the ownership count. */
188 thread_ptr -> tx_thread_owned_mutex_count--;
189
190 /* Determine if this mutex was the only one on the list. */
191 if (thread_ptr -> tx_thread_owned_mutex_count == ((UINT) 0))
192 {
193
194 /* Yes, the list is empty. Simply set the head pointer to NULL. */
195 thread_ptr -> tx_thread_owned_mutex_list = TX_NULL;
196 }
197 else
198 {
199
200 /* No, there are more mutexes on the list. */
201
202 /* Link-up the neighbors. */
203 next_mutex = mutex_ptr -> tx_mutex_owned_next;
204 previous_mutex = mutex_ptr -> tx_mutex_owned_previous;
205 next_mutex -> tx_mutex_owned_previous = previous_mutex;
206 previous_mutex -> tx_mutex_owned_next = next_mutex;
207
208 /* See if we have to update the created list head pointer. */
209 if (thread_ptr -> tx_thread_owned_mutex_list == mutex_ptr)
210 {
211
212 /* Yes, move the head pointer to the next link. */
213 thread_ptr -> tx_thread_owned_mutex_list = next_mutex;
214 }
215 }
216
217 /* Determine if the simple, non-suspension, non-priority inheritance case is present. */
218 if (mutex_ptr -> tx_mutex_suspension_list == TX_NULL)
219 {
220
221 /* Is this a priority inheritance mutex? */
222 if (mutex_ptr -> tx_mutex_inherit == TX_FALSE)
223 {
224
225 /* Yes, we are done - set the mutex owner to NULL. */
226 mutex_ptr -> tx_mutex_owner = TX_NULL;
227
228 /* Restore interrupts. */
229 TX_RESTORE
230
231 /* Mutex is now available, return successful status. */
232 status = TX_SUCCESS;
233 }
234 }
235
236 /* Determine if the processing is complete. */
237 if (status == TX_NOT_DONE)
238 {
239
240 /* Initialize original owner and thread priority. */
241 old_owner = TX_NULL;
242 old_priority = thread_ptr -> tx_thread_user_priority;
243
244 /* Does this mutex support priority inheritance? */
245 if (mutex_ptr -> tx_mutex_inherit == TX_TRUE)
246 {
247
248 #ifndef TX_NOT_INTERRUPTABLE
249
250 /* Temporarily disable preemption. */
251 _tx_thread_preempt_disable++;
252
253 /* Restore interrupts. */
254 TX_RESTORE
255 #endif
256
257 /* Default the inheritance priority to disabled. */
258 inheritance_priority = ((UINT) TX_MAX_PRIORITIES);
259
260 /* Search the owned mutexes for this thread to determine the highest priority for this
261 former mutex owner to return to. */
262 next_mutex = thread_ptr -> tx_thread_owned_mutex_list;
263 while (next_mutex != TX_NULL)
264 {
265
266 /* Does this mutex support priority inheritance? */
267 if (next_mutex -> tx_mutex_inherit == TX_TRUE)
268 {
269
270 /* Determine if highest priority field of the mutex is higher than the priority to
271 restore. */
272 if (next_mutex -> tx_mutex_highest_priority_waiting < inheritance_priority)
273 {
274
275 /* Use this priority to return releasing thread to. */
276 inheritance_priority = next_mutex -> tx_mutex_highest_priority_waiting;
277 }
278 }
279
280 /* Move mutex pointer to the next mutex in the list. */
281 next_mutex = next_mutex -> tx_mutex_owned_next;
282
283 /* Are we at the end of the list? */
284 if (next_mutex == thread_ptr -> tx_thread_owned_mutex_list)
285 {
286
287 /* Yes, set the next mutex to NULL. */
288 next_mutex = TX_NULL;
289 }
290 }
291
292 #ifndef TX_NOT_INTERRUPTABLE
293
294 /* Disable interrupts. */
295 TX_DISABLE
296
297 /* Undo the temporarily preemption disable. */
298 _tx_thread_preempt_disable--;
299 #endif
300
301 /* Set the inherit priority to that of the highest priority thread waiting on the mutex. */
302 thread_ptr -> tx_thread_inherit_priority = inheritance_priority;
303
304 /* Determine if the inheritance priority is less than the default old priority. */
305 if (inheritance_priority < old_priority)
306 {
307
308 /* Yes, update the old priority. */
309 old_priority = inheritance_priority;
310 }
311 }
312
313 /* Determine if priority inheritance is in effect and there are one or more
314 threads suspended on the mutex. */
315 if (mutex_ptr -> tx_mutex_suspended_count > ((UINT) 1))
316 {
317
318 /* Is priority inheritance in effect? */
319 if (mutex_ptr -> tx_mutex_inherit == TX_TRUE)
320 {
321
322 /* Yes, this code is simply to ensure the highest priority thread is positioned
323 at the front of the suspension list. */
324
325 #ifndef TX_NOT_INTERRUPTABLE
326
327 /* Temporarily disable preemption. */
328 _tx_thread_preempt_disable++;
329
330 /* Restore interrupts. */
331 TX_RESTORE
332 #endif
333
334 /* Call the mutex prioritize processing to ensure the
335 highest priority thread is resumed. */
336 #ifdef TX_MISRA_ENABLE
337 do
338 {
339 status = _tx_mutex_prioritize(mutex_ptr);
340 } while (status != TX_SUCCESS);
341 #else
342 _tx_mutex_prioritize(mutex_ptr);
343 #endif
344
345 /* At this point, the highest priority thread is at the
346 front of the suspension list. */
347
348 /* Optional processing extension. */
349 TX_MUTEX_PUT_EXTENSION_1
350
351 #ifndef TX_NOT_INTERRUPTABLE
352
353 /* Disable interrupts. */
354 TX_DISABLE
355
356 /* Back off the preemption disable. */
357 _tx_thread_preempt_disable--;
358 #endif
359 }
360 }
361
362 /* Now determine if there are any threads still waiting on the mutex. */
363 if (mutex_ptr -> tx_mutex_suspension_list == TX_NULL)
364 {
365
366 /* No, there are no longer any threads waiting on the mutex. */
367
368 #ifndef TX_NOT_INTERRUPTABLE
369
370 /* Temporarily disable preemption. */
371 _tx_thread_preempt_disable++;
372
373 /* Restore interrupts. */
374 TX_RESTORE
375 #endif
376
377 /* Mutex is not owned, but it is possible that a thread that
378 caused a priority inheritance to occur is no longer waiting
379 on the mutex. */
380
381 /* Setup the highest priority waiting thread. */
382 mutex_ptr -> tx_mutex_highest_priority_waiting = (UINT) TX_MAX_PRIORITIES;
383
384 /* Determine if we need to restore priority. */
385 if ((mutex_ptr -> tx_mutex_owner) -> tx_thread_priority != old_priority)
386 {
387
388 /* Yes, restore the priority of thread. */
389 _tx_mutex_priority_change(mutex_ptr -> tx_mutex_owner, old_priority);
390 }
391
392 #ifndef TX_NOT_INTERRUPTABLE
393
394 /* Disable interrupts again. */
395 TX_DISABLE
396
397 /* Back off the preemption disable. */
398 _tx_thread_preempt_disable--;
399 #endif
400
401 /* Set the mutex owner to NULL. */
402 mutex_ptr -> tx_mutex_owner = TX_NULL;
403
404 /* Restore interrupts. */
405 TX_RESTORE
406
407 /* Check for preemption. */
408 _tx_thread_system_preempt_check();
409
410 /* Set status to success. */
411 status = TX_SUCCESS;
412 }
413 else
414 {
415
416 /* Pickup the thread at the front of the suspension list. */
417 thread_ptr = mutex_ptr -> tx_mutex_suspension_list;
418
419 /* Save the previous ownership information, if inheritance is
420 in effect. */
421 if (mutex_ptr -> tx_mutex_inherit == TX_TRUE)
422 {
423
424 /* Remember the old mutex owner. */
425 old_owner = mutex_ptr -> tx_mutex_owner;
426
427 /* Setup owner thread priority information. */
428 mutex_ptr -> tx_mutex_original_priority = thread_ptr -> tx_thread_priority;
429
430 /* Setup the highest priority waiting thread. */
431 mutex_ptr -> tx_mutex_highest_priority_waiting = (UINT) TX_MAX_PRIORITIES;
432 }
433
434 /* Determine how many mutexes are owned by this thread. */
435 owned_count = thread_ptr -> tx_thread_owned_mutex_count;
436
437 /* Determine if this thread owns any other mutexes that have priority inheritance. */
438 if (owned_count == ((UINT) 0))
439 {
440
441 /* The owned mutex list is empty. Add mutex to empty list. */
442 thread_ptr -> tx_thread_owned_mutex_list = mutex_ptr;
443 mutex_ptr -> tx_mutex_owned_next = mutex_ptr;
444 mutex_ptr -> tx_mutex_owned_previous = mutex_ptr;
445 }
446 else
447 {
448
449 /* Non-empty list. Link up the mutex. */
450
451 /* Pickup tail pointer. */
452 next_mutex = thread_ptr -> tx_thread_owned_mutex_list;
453 previous_mutex = next_mutex -> tx_mutex_owned_previous;
454
455 /* Place the owned mutex in the list. */
456 next_mutex -> tx_mutex_owned_previous = mutex_ptr;
457 previous_mutex -> tx_mutex_owned_next = mutex_ptr;
458
459 /* Setup this mutex's next and previous created links. */
460 mutex_ptr -> tx_mutex_owned_previous = previous_mutex;
461 mutex_ptr -> tx_mutex_owned_next = next_mutex;
462 }
463
464 /* Increment the number of mutexes owned counter. */
465 thread_ptr -> tx_thread_owned_mutex_count = owned_count + ((UINT) 1);
466
467 /* Mark the Mutex as owned and fill in the corresponding information. */
468 mutex_ptr -> tx_mutex_ownership_count = (UINT) 1;
469 mutex_ptr -> tx_mutex_owner = thread_ptr;
470
471 /* Remove the suspended thread from the list. */
472
473 /* Decrement the suspension count. */
474 mutex_ptr -> tx_mutex_suspended_count--;
475
476 /* Pickup the suspended count. */
477 suspended_count = mutex_ptr -> tx_mutex_suspended_count;
478
479 /* See if this is the only suspended thread on the list. */
480 if (suspended_count == TX_NO_SUSPENSIONS)
481 {
482
483 /* Yes, the only suspended thread. */
484
485 /* Update the head pointer. */
486 mutex_ptr -> tx_mutex_suspension_list = TX_NULL;
487 }
488 else
489 {
490
491 /* At least one more thread is on the same expiration list. */
492
493 /* Update the list head pointer. */
494 next_thread = thread_ptr -> tx_thread_suspended_next;
495 mutex_ptr -> tx_mutex_suspension_list = next_thread;
496
497 /* Update the links of the adjacent threads. */
498 previous_thread = thread_ptr -> tx_thread_suspended_previous;
499 next_thread -> tx_thread_suspended_previous = previous_thread;
500 previous_thread -> tx_thread_suspended_next = next_thread;
501 }
502
503 /* Prepare for resumption of the first thread. */
504
505 /* Clear cleanup routine to avoid timeout. */
506 thread_ptr -> tx_thread_suspend_cleanup = TX_NULL;
507
508 /* Put return status into the thread control block. */
509 thread_ptr -> tx_thread_suspend_status = TX_SUCCESS;
510
511 #ifdef TX_NOT_INTERRUPTABLE
512
513 /* Determine if priority inheritance is enabled for this mutex. */
514 if (mutex_ptr -> tx_mutex_inherit == TX_TRUE)
515 {
516
517 /* Yes, priority inheritance is requested. */
518
519 /* Determine if there are any more threads still suspended on the mutex. */
520 if (mutex_ptr -> tx_mutex_suspended_count != ((ULONG) 0))
521 {
522
523 /* Determine if there are more than one thread suspended on the mutex. */
524 if (mutex_ptr -> tx_mutex_suspended_count > ((ULONG) 1))
525 {
526
527 /* If so, prioritize the list so the highest priority thread is placed at the
528 front of the suspension list. */
529 #ifdef TX_MISRA_ENABLE
530 do
531 {
532 status = _tx_mutex_prioritize(mutex_ptr);
533 } while (status != TX_SUCCESS);
534 #else
535 _tx_mutex_prioritize(mutex_ptr);
536 #endif
537 }
538
539 /* Now, pickup the list head and set the priority. */
540
541 /* Determine if there still are threads suspended for this mutex. */
542 suspended_thread = mutex_ptr -> tx_mutex_suspension_list;
543 if (suspended_thread != TX_NULL)
544 {
545
546 /* Setup the highest priority thread waiting on this mutex. */
547 mutex_ptr -> tx_mutex_highest_priority_waiting = suspended_thread -> tx_thread_priority;
548 }
549 }
550
551 /* Restore previous priority needs to be restored after priority
552 inheritance. */
553
554 /* Determine if we need to restore priority. */
555 if (old_owner -> tx_thread_priority != old_priority)
556 {
557
558 /* Restore priority of thread. */
559 _tx_mutex_priority_change(old_owner, old_priority);
560 }
561 }
562
563 /* Resume the thread! */
564 _tx_thread_system_ni_resume(thread_ptr);
565
566 /* Restore interrupts. */
567 TX_RESTORE
568 #else
569
570 /* Temporarily disable preemption. */
571 _tx_thread_preempt_disable++;
572
573 /* Restore interrupts. */
574 TX_RESTORE
575
576 /* Determine if priority inheritance is enabled for this mutex. */
577 if (mutex_ptr -> tx_mutex_inherit == TX_TRUE)
578 {
579
580 /* Yes, priority inheritance is requested. */
581
582 /* Determine if there are any more threads still suspended on the mutex. */
583 if (mutex_ptr -> tx_mutex_suspended_count != TX_NO_SUSPENSIONS)
584 {
585
586 /* Prioritize the list so the highest priority thread is placed at the
587 front of the suspension list. */
588 #ifdef TX_MISRA_ENABLE
589 do
590 {
591 status = _tx_mutex_prioritize(mutex_ptr);
592 } while (status != TX_SUCCESS);
593 #else
594 _tx_mutex_prioritize(mutex_ptr);
595 #endif
596
597 /* Now, pickup the list head and set the priority. */
598
599 /* Optional processing extension. */
600 TX_MUTEX_PUT_EXTENSION_2
601
602 /* Disable interrupts. */
603 TX_DISABLE
604
605 /* Determine if there still are threads suspended for this mutex. */
606 suspended_thread = mutex_ptr -> tx_mutex_suspension_list;
607 if (suspended_thread != TX_NULL)
608 {
609
610 /* Setup the highest priority thread waiting on this mutex. */
611 mutex_ptr -> tx_mutex_highest_priority_waiting = suspended_thread -> tx_thread_priority;
612 }
613
614 /* Restore interrupts. */
615 TX_RESTORE
616 }
617
618 /* Restore previous priority needs to be restored after priority
619 inheritance. */
620
621 /* Is the priority different? */
622 if (old_owner -> tx_thread_priority != old_priority)
623 {
624
625 /* Restore the priority of thread. */
626 _tx_mutex_priority_change(old_owner, old_priority);
627 }
628 }
629
630 /* Resume thread. */
631 _tx_thread_system_resume(thread_ptr);
632 #endif
633
634 /* Return a successful status. */
635 status = TX_SUCCESS;
636 }
637 }
638 }
639 }
640 }
641 }
642 else
643 {
644
645 /* Restore interrupts. */
646 TX_RESTORE
647
648 /* Caller does not own the mutex. */
649 status = TX_NOT_OWNED;
650 }
651
652 /* Return the completion status. */
653 return(status);
654 }
655
656