1 /* Copyright (C) 2002-2022 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3 
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8 
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, see
16    <https://www.gnu.org/licenses/>.  */
17 
18 #include <assert.h>
19 #include <errno.h>
20 #include <stdlib.h>
21 #include "pthreadP.h"
22 #include <lowlevellock.h>
23 #include <futex-internal.h>
24 
25 int
___pthread_mutex_trylock(pthread_mutex_t * mutex)26 ___pthread_mutex_trylock (pthread_mutex_t *mutex)
27 {
28   int oldval;
29   pid_t id = THREAD_GETMEM (THREAD_SELF, tid);
30 
31   /* See concurrency notes regarding mutex type which is loaded from __kind
32      in struct __pthread_mutex_s in sysdeps/nptl/bits/thread-shared-types.h.  */
33   switch (__builtin_expect (PTHREAD_MUTEX_TYPE_ELISION (mutex),
34 			    PTHREAD_MUTEX_TIMED_NP))
35     {
36       /* Recursive mutex.  */
37     case PTHREAD_MUTEX_RECURSIVE_NP|PTHREAD_MUTEX_ELISION_NP:
38     case PTHREAD_MUTEX_RECURSIVE_NP:
39       /* Check whether we already hold the mutex.  */
40       if (mutex->__data.__owner == id)
41 	{
42 	  /* Just bump the counter.  */
43 	  if (__glibc_unlikely (mutex->__data.__count + 1 == 0))
44 	    /* Overflow of the counter.  */
45 	    return EAGAIN;
46 
47 	  ++mutex->__data.__count;
48 	  return 0;
49 	}
50 
51       if (lll_trylock (mutex->__data.__lock) == 0)
52 	{
53 	  /* Record the ownership.  */
54 	  mutex->__data.__owner = id;
55 	  mutex->__data.__count = 1;
56 	  ++mutex->__data.__nusers;
57 	  return 0;
58 	}
59       break;
60 
61     case PTHREAD_MUTEX_TIMED_ELISION_NP:
62     elision: __attribute__((unused))
63       if (lll_trylock_elision (mutex->__data.__lock,
64 			       mutex->__data.__elision) != 0)
65 	break;
66       /* Don't record the ownership.  */
67       return 0;
68 
69     case PTHREAD_MUTEX_TIMED_NP:
70       FORCE_ELISION (mutex, goto elision);
71       /*FALL THROUGH*/
72     case PTHREAD_MUTEX_ADAPTIVE_NP:
73     case PTHREAD_MUTEX_ERRORCHECK_NP:
74       if (lll_trylock (mutex->__data.__lock) != 0)
75 	break;
76 
77       /* Record the ownership.  */
78       mutex->__data.__owner = id;
79       ++mutex->__data.__nusers;
80 
81       return 0;
82 
83     case PTHREAD_MUTEX_ROBUST_RECURSIVE_NP:
84     case PTHREAD_MUTEX_ROBUST_ERRORCHECK_NP:
85     case PTHREAD_MUTEX_ROBUST_NORMAL_NP:
86     case PTHREAD_MUTEX_ROBUST_ADAPTIVE_NP:
87       THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
88 		     &mutex->__data.__list.__next);
89       /* We need to set op_pending before starting the operation.  Also
90 	 see comments at ENQUEUE_MUTEX.  */
91       __asm ("" ::: "memory");
92 
93       oldval = mutex->__data.__lock;
94       do
95 	{
96 	again:
97 	  if ((oldval & FUTEX_OWNER_DIED) != 0)
98 	    {
99 	      /* The previous owner died.  Try locking the mutex.  */
100 	      int newval = id | (oldval & FUTEX_WAITERS);
101 
102 	      newval
103 		= atomic_compare_and_exchange_val_acq (&mutex->__data.__lock,
104 						       newval, oldval);
105 
106 	      if (newval != oldval)
107 		{
108 		  oldval = newval;
109 		  goto again;
110 		}
111 
112 	      /* We got the mutex.  */
113 	      mutex->__data.__count = 1;
114 	      /* But it is inconsistent unless marked otherwise.  */
115 	      mutex->__data.__owner = PTHREAD_MUTEX_INCONSISTENT;
116 
117 	      /* We must not enqueue the mutex before we have acquired it.
118 		 Also see comments at ENQUEUE_MUTEX.  */
119 	      __asm ("" ::: "memory");
120 	      ENQUEUE_MUTEX (mutex);
121 	      /* We need to clear op_pending after we enqueue the mutex.  */
122 	      __asm ("" ::: "memory");
123 	      THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
124 
125 	      /* Note that we deliberately exit here.  If we fall
126 		 through to the end of the function __nusers would be
127 		 incremented which is not correct because the old
128 		 owner has to be discounted.  */
129 	      return EOWNERDEAD;
130 	    }
131 
132 	  /* Check whether we already hold the mutex.  */
133 	  if (__glibc_unlikely ((oldval & FUTEX_TID_MASK) == id))
134 	    {
135 	      int kind = PTHREAD_MUTEX_TYPE (mutex);
136 	      if (kind == PTHREAD_MUTEX_ROBUST_ERRORCHECK_NP)
137 		{
138 		  /* We do not need to ensure ordering wrt another memory
139 		     access.  Also see comments at ENQUEUE_MUTEX. */
140 		  THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
141 				 NULL);
142 		  return EDEADLK;
143 		}
144 
145 	      if (kind == PTHREAD_MUTEX_ROBUST_RECURSIVE_NP)
146 		{
147 		  /* We do not need to ensure ordering wrt another memory
148 		     access.  */
149 		  THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
150 				 NULL);
151 
152 		  /* Just bump the counter.  */
153 		  if (__glibc_unlikely (mutex->__data.__count + 1 == 0))
154 		    /* Overflow of the counter.  */
155 		    return EAGAIN;
156 
157 		  ++mutex->__data.__count;
158 
159 		  return 0;
160 		}
161 	    }
162 
163 	  oldval = atomic_compare_and_exchange_val_acq (&mutex->__data.__lock,
164 							id, 0);
165 	  if (oldval != 0 && (oldval & FUTEX_OWNER_DIED) == 0)
166 	    {
167 	      /* We haven't acquired the lock as it is already acquired by
168 		 another owner.  We do not need to ensure ordering wrt another
169 		 memory access.  */
170 	      THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
171 
172 	      return EBUSY;
173 	    }
174 
175 	  if (__builtin_expect (mutex->__data.__owner
176 				== PTHREAD_MUTEX_NOTRECOVERABLE, 0))
177 	    {
178 	      /* This mutex is now not recoverable.  */
179 	      mutex->__data.__count = 0;
180 	      if (oldval == id)
181 		lll_unlock (mutex->__data.__lock,
182 			    PTHREAD_ROBUST_MUTEX_PSHARED (mutex));
183 	      /* FIXME This violates the mutex destruction requirements.  See
184 		 __pthread_mutex_unlock_full.  */
185 	      THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
186 	      return ENOTRECOVERABLE;
187 	    }
188 	}
189       while ((oldval & FUTEX_OWNER_DIED) != 0);
190 
191       /* We must not enqueue the mutex before we have acquired it.
192 	 Also see comments at ENQUEUE_MUTEX.  */
193       __asm ("" ::: "memory");
194       ENQUEUE_MUTEX (mutex);
195       /* We need to clear op_pending after we enqueue the mutex.  */
196       __asm ("" ::: "memory");
197       THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
198 
199       mutex->__data.__owner = id;
200       ++mutex->__data.__nusers;
201       mutex->__data.__count = 1;
202 
203       return 0;
204 
205     /* The PI support requires the Linux futex system call.  If that's not
206        available, pthread_mutex_init should never have allowed the type to
207        be set.  So it will get the default case for an invalid type.  */
208 #ifdef __NR_futex
209     case PTHREAD_MUTEX_PI_RECURSIVE_NP:
210     case PTHREAD_MUTEX_PI_ERRORCHECK_NP:
211     case PTHREAD_MUTEX_PI_NORMAL_NP:
212     case PTHREAD_MUTEX_PI_ADAPTIVE_NP:
213     case PTHREAD_MUTEX_PI_ROBUST_RECURSIVE_NP:
214     case PTHREAD_MUTEX_PI_ROBUST_ERRORCHECK_NP:
215     case PTHREAD_MUTEX_PI_ROBUST_NORMAL_NP:
216     case PTHREAD_MUTEX_PI_ROBUST_ADAPTIVE_NP:
217       {
218 	int kind, robust;
219 	{
220 	  /* See concurrency notes regarding __kind in struct __pthread_mutex_s
221 	     in sysdeps/nptl/bits/thread-shared-types.h.  */
222 	  int mutex_kind = atomic_load_relaxed (&(mutex->__data.__kind));
223 	  kind = mutex_kind & PTHREAD_MUTEX_KIND_MASK_NP;
224 	  robust = mutex_kind & PTHREAD_MUTEX_ROBUST_NORMAL_NP;
225 	}
226 
227 	if (robust)
228 	  {
229 	    /* Note: robust PI futexes are signaled by setting bit 0.  */
230 	    THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
231 			   (void *) (((uintptr_t) &mutex->__data.__list.__next)
232 				     | 1));
233 	    /* We need to set op_pending before starting the operation.  Also
234 	       see comments at ENQUEUE_MUTEX.  */
235 	    __asm ("" ::: "memory");
236 	  }
237 
238 	oldval = mutex->__data.__lock;
239 
240 	/* Check whether we already hold the mutex.  */
241 	if (__glibc_unlikely ((oldval & FUTEX_TID_MASK) == id))
242 	  {
243 	    if (kind == PTHREAD_MUTEX_ERRORCHECK_NP)
244 	      {
245 		/* We do not need to ensure ordering wrt another memory
246 		   access.  */
247 		THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
248 		return EDEADLK;
249 	      }
250 
251 	    if (kind == PTHREAD_MUTEX_RECURSIVE_NP)
252 	      {
253 		/* We do not need to ensure ordering wrt another memory
254 		   access.  */
255 		THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
256 
257 		/* Just bump the counter.  */
258 		if (__glibc_unlikely (mutex->__data.__count + 1 == 0))
259 		  /* Overflow of the counter.  */
260 		  return EAGAIN;
261 
262 		++mutex->__data.__count;
263 
264 		return 0;
265 	      }
266 	  }
267 
268 	oldval
269 	  = atomic_compare_and_exchange_val_acq (&mutex->__data.__lock,
270 						 id, 0);
271 
272 	if (oldval != 0)
273 	  {
274 	    if ((oldval & FUTEX_OWNER_DIED) == 0)
275 	      {
276 		/* We haven't acquired the lock as it is already acquired by
277 		   another owner.  We do not need to ensure ordering wrt another
278 		   memory access.  */
279 		THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
280 
281 		return EBUSY;
282 	      }
283 
284 	    assert (robust);
285 
286 	    /* The mutex owner died.  The kernel will now take care of
287 	       everything.  */
288 	    int private = (robust
289 			   ? PTHREAD_ROBUST_MUTEX_PSHARED (mutex)
290 			   : PTHREAD_MUTEX_PSHARED (mutex));
291 	    int e = INTERNAL_SYSCALL_CALL (futex, &mutex->__data.__lock,
292 					   __lll_private_flag (FUTEX_TRYLOCK_PI,
293 							       private), 0, 0);
294 
295 	    if (INTERNAL_SYSCALL_ERROR_P (e)
296 		&& INTERNAL_SYSCALL_ERRNO (e) == EWOULDBLOCK)
297 	      {
298 		/* The kernel has not yet finished the mutex owner death.
299 		   We do not need to ensure ordering wrt another memory
300 		   access.  */
301 		THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
302 
303 		return EBUSY;
304 	      }
305 
306 	    oldval = mutex->__data.__lock;
307 	  }
308 
309 	if (__glibc_unlikely (oldval & FUTEX_OWNER_DIED))
310 	  {
311 	    atomic_and (&mutex->__data.__lock, ~FUTEX_OWNER_DIED);
312 
313 	    /* We got the mutex.  */
314 	    mutex->__data.__count = 1;
315 	    /* But it is inconsistent unless marked otherwise.  */
316 	    mutex->__data.__owner = PTHREAD_MUTEX_INCONSISTENT;
317 
318 	    /* We must not enqueue the mutex before we have acquired it.
319 	       Also see comments at ENQUEUE_MUTEX.  */
320 	    __asm ("" ::: "memory");
321 	    ENQUEUE_MUTEX (mutex);
322 	    /* We need to clear op_pending after we enqueue the mutex.  */
323 	    __asm ("" ::: "memory");
324 	    THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
325 
326 	    /* Note that we deliberately exit here.  If we fall
327 	       through to the end of the function __nusers would be
328 	       incremented which is not correct because the old owner
329 	       has to be discounted.  */
330 	    return EOWNERDEAD;
331 	  }
332 
333 	if (robust
334 	    && __builtin_expect (mutex->__data.__owner
335 				 == PTHREAD_MUTEX_NOTRECOVERABLE, 0))
336 	  {
337 	    /* This mutex is now not recoverable.  */
338 	    mutex->__data.__count = 0;
339 
340 	    futex_unlock_pi ((unsigned int *) &mutex->__data.__lock,
341 			     PTHREAD_ROBUST_MUTEX_PSHARED (mutex));
342 
343 	    /* To the kernel, this will be visible after the kernel has
344 	       acquired the mutex in the syscall.  */
345 	    THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
346 	    return ENOTRECOVERABLE;
347 	  }
348 
349 	if (robust)
350 	  {
351 	    /* We must not enqueue the mutex before we have acquired it.
352 	       Also see comments at ENQUEUE_MUTEX.  */
353 	    __asm ("" ::: "memory");
354 	    ENQUEUE_MUTEX_PI (mutex);
355 	    /* We need to clear op_pending after we enqueue the mutex.  */
356 	    __asm ("" ::: "memory");
357 	    THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
358 	  }
359 
360 	mutex->__data.__owner = id;
361 	++mutex->__data.__nusers;
362 	mutex->__data.__count = 1;
363 
364 	return 0;
365       }
366 #endif  /* __NR_futex.  */
367 
368     case PTHREAD_MUTEX_PP_RECURSIVE_NP:
369     case PTHREAD_MUTEX_PP_ERRORCHECK_NP:
370     case PTHREAD_MUTEX_PP_NORMAL_NP:
371     case PTHREAD_MUTEX_PP_ADAPTIVE_NP:
372       {
373 	/* See concurrency notes regarding __kind in struct __pthread_mutex_s
374 	   in sysdeps/nptl/bits/thread-shared-types.h.  */
375 	int kind = atomic_load_relaxed (&(mutex->__data.__kind))
376 	  & PTHREAD_MUTEX_KIND_MASK_NP;
377 
378 	oldval = mutex->__data.__lock;
379 
380 	/* Check whether we already hold the mutex.  */
381 	if (mutex->__data.__owner == id)
382 	  {
383 	    if (kind == PTHREAD_MUTEX_ERRORCHECK_NP)
384 	      return EDEADLK;
385 
386 	    if (kind == PTHREAD_MUTEX_RECURSIVE_NP)
387 	      {
388 		/* Just bump the counter.  */
389 		if (__glibc_unlikely (mutex->__data.__count + 1 == 0))
390 		  /* Overflow of the counter.  */
391 		  return EAGAIN;
392 
393 		++mutex->__data.__count;
394 
395 		return 0;
396 	      }
397 	  }
398 
399 	int oldprio = -1, ceilval;
400 	do
401 	  {
402 	    int ceiling = (oldval & PTHREAD_MUTEX_PRIO_CEILING_MASK)
403 			  >> PTHREAD_MUTEX_PRIO_CEILING_SHIFT;
404 
405 	    if (__pthread_current_priority () > ceiling)
406 	      {
407 		if (oldprio != -1)
408 		  __pthread_tpp_change_priority (oldprio, -1);
409 		return EINVAL;
410 	      }
411 
412 	    int retval = __pthread_tpp_change_priority (oldprio, ceiling);
413 	    if (retval)
414 	      return retval;
415 
416 	    ceilval = ceiling << PTHREAD_MUTEX_PRIO_CEILING_SHIFT;
417 	    oldprio = ceiling;
418 
419 	    oldval
420 	      = atomic_compare_and_exchange_val_acq (&mutex->__data.__lock,
421 						     ceilval | 1, ceilval);
422 
423 	    if (oldval == ceilval)
424 	      break;
425 	  }
426 	while ((oldval & PTHREAD_MUTEX_PRIO_CEILING_MASK) != ceilval);
427 
428 	if (oldval != ceilval)
429 	  {
430 	    __pthread_tpp_change_priority (oldprio, -1);
431 	    break;
432 	  }
433 
434 	assert (mutex->__data.__owner == 0);
435 	/* Record the ownership.  */
436 	mutex->__data.__owner = id;
437 	++mutex->__data.__nusers;
438 	mutex->__data.__count = 1;
439 
440 	return 0;
441       }
442       break;
443 
444     default:
445       /* Correct code cannot set any other type.  */
446       return EINVAL;
447     }
448 
449   return EBUSY;
450 }
451 versioned_symbol (libc, ___pthread_mutex_trylock,
452 		  pthread_mutex_trylock, GLIBC_2_34);
453 libc_hidden_ver (___pthread_mutex_trylock, __pthread_mutex_trylock)
454 #ifndef SHARED
455 strong_alias (___pthread_mutex_trylock, __pthread_mutex_trylock)
456 #endif
457 
458 #if OTHER_SHLIB_COMPAT (libpthread, GLIBC_2_0, GLIBC_2_34)
459 compat_symbol (libpthread, ___pthread_mutex_trylock,
460 	       pthread_mutex_trylock, GLIBC_2_0);
461 compat_symbol (libpthread, ___pthread_mutex_trylock,
462 	       __pthread_mutex_trylock, GLIBC_2_0);
463 #endif
464