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