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