1 /* Set current priority ceiling of pthread_mutex_t.
2    Copyright (C) 2006-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 #include <stdbool.h>
20 #include <errno.h>
21 #include <pthreadP.h>
22 #include <atomic.h>
23 #include <futex-internal.h>
24 #include <shlib-compat.h>
25 
26 int
__pthread_mutex_setprioceiling(pthread_mutex_t * mutex,int prioceiling,int * old_ceiling)27 __pthread_mutex_setprioceiling (pthread_mutex_t *mutex, int prioceiling,
28 				int *old_ceiling)
29 {
30   /* See concurrency notes regarding __kind in struct __pthread_mutex_s
31      in sysdeps/nptl/bits/thread-shared-types.h.  */
32   if ((atomic_load_relaxed (&(mutex->__data.__kind))
33        & PTHREAD_MUTEX_PRIO_PROTECT_NP) == 0)
34     return EINVAL;
35 
36   /* See __init_sched_fifo_prio.  */
37   if (atomic_load_relaxed (&__sched_fifo_min_prio) == -1
38       || atomic_load_relaxed (&__sched_fifo_max_prio) == -1)
39     __init_sched_fifo_prio ();
40 
41   if (__glibc_unlikely (prioceiling
42 			< atomic_load_relaxed (&__sched_fifo_min_prio))
43       || __glibc_unlikely (prioceiling
44 			   > atomic_load_relaxed (&__sched_fifo_max_prio))
45       || __glibc_unlikely ((prioceiling
46 			    & (PTHREAD_MUTEXATTR_PRIO_CEILING_MASK
47 			       >> PTHREAD_MUTEXATTR_PRIO_CEILING_SHIFT))
48 			   != prioceiling))
49     return EINVAL;
50 
51   /* Check whether we already hold the mutex.  */
52   bool locked = false;
53   int kind = PTHREAD_MUTEX_TYPE (mutex);
54   if (mutex->__data.__owner == THREAD_GETMEM (THREAD_SELF, tid))
55     {
56       if (kind == PTHREAD_MUTEX_PP_ERRORCHECK_NP)
57 	return EDEADLK;
58 
59       if (kind == PTHREAD_MUTEX_PP_RECURSIVE_NP)
60 	locked = true;
61     }
62 
63   int oldval = mutex->__data.__lock;
64   if (! locked)
65     do
66       {
67 	/* Need to lock the mutex, but without obeying the priority
68 	   protect protocol.  */
69 	int ceilval = (oldval & PTHREAD_MUTEX_PRIO_CEILING_MASK);
70 
71 	oldval = atomic_compare_and_exchange_val_acq (&mutex->__data.__lock,
72 						      ceilval | 1, ceilval);
73 	if (oldval == ceilval)
74 	  break;
75 
76 	do
77 	  {
78 	    oldval
79 	      = atomic_compare_and_exchange_val_acq (&mutex->__data.__lock,
80 						     ceilval | 2,
81 						     ceilval | 1);
82 
83 	    if ((oldval & PTHREAD_MUTEX_PRIO_CEILING_MASK) != ceilval)
84 	      break;
85 
86 	    if (oldval != ceilval)
87 	      futex_wait ((unsigned int *) &mutex->__data.__lock, ceilval | 2,
88 			  PTHREAD_MUTEX_PSHARED (mutex));
89 	  }
90 	while (atomic_compare_and_exchange_val_acq (&mutex->__data.__lock,
91 						    ceilval | 2, ceilval)
92 	       != ceilval);
93 
94 	if ((oldval & PTHREAD_MUTEX_PRIO_CEILING_MASK) != ceilval)
95 	  continue;
96       }
97     while (0);
98 
99   int oldprio = (oldval & PTHREAD_MUTEX_PRIO_CEILING_MASK)
100 		>> PTHREAD_MUTEX_PRIO_CEILING_SHIFT;
101   if (locked)
102     {
103       int ret = __pthread_tpp_change_priority (oldprio, prioceiling);
104       if (ret)
105 	return ret;
106     }
107 
108   if (old_ceiling != NULL)
109     *old_ceiling = oldprio;
110 
111   int newlock = 0;
112   if (locked)
113     newlock = (mutex->__data.__lock & ~PTHREAD_MUTEX_PRIO_CEILING_MASK);
114   mutex->__data.__lock = newlock
115 			 | (prioceiling << PTHREAD_MUTEX_PRIO_CEILING_SHIFT);
116   atomic_full_barrier ();
117 
118   futex_wake ((unsigned int *)&mutex->__data.__lock, INT_MAX,
119 	      PTHREAD_MUTEX_PSHARED (mutex));
120 
121   return 0;
122 }
123 versioned_symbol (libc, __pthread_mutex_setprioceiling,
124 		  pthread_mutex_setprioceiling, GLIBC_2_34);
125 
126 #if OTHER_SHLIB_COMPAT (libpthread, GLIBC_2_4, GLIBC_2_34)
127 compat_symbol (libpthread, __pthread_mutex_setprioceiling,
128                pthread_mutex_setprioceiling, GLIBC_2_4);
129 #endif
130