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