(root)/
glibc-2.38/
nptl/
pthread_rwlock_tryrdlock.c
       1  /* Copyright (C) 2002-2023 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 <errno.h>
      19  #include "pthreadP.h"
      20  #include <atomic.h>
      21  #include <stdbool.h>
      22  #include "pthread_rwlock_common.c"
      23  
      24  
      25  /* See pthread_rwlock_common.c for an overview.  */
      26  int
      27  ___pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock)
      28  {
      29    /* For tryrdlock, we could speculate that we will succeed and go ahead and
      30       register as a reader.  However, if we misspeculate, we have to do the
      31       same steps as a timed-out rdlock, which will increase contention.
      32       Therefore, there is a trade-off between being able to use a combinable
      33       read-modify-write operation and a CAS loop as used below; we pick the
      34       latter because it simplifies the code, and should perform better when
      35       tryrdlock is used in cases where writers are infrequent.
      36       Because POSIX does not require a failed trylock to "synchronize memory",
      37       relaxed MO is sufficient here and on the failure path of the CAS
      38       below.  */
      39    unsigned int r = atomic_load_relaxed (&rwlock->__data.__readers);
      40    unsigned int rnew;
      41    do
      42      {
      43        if ((r & PTHREAD_RWLOCK_WRPHASE) == 0)
      44  	{
      45  	  /* If we are in a read phase, try to acquire unless there is a
      46  	     primary writer and we prefer writers and there will be no
      47  	     recursive read locks.  */
      48  	  if (((r & PTHREAD_RWLOCK_WRLOCKED) != 0)
      49  	      && (rwlock->__data.__flags
      50  		  == PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP))
      51  	    return EBUSY;
      52  	  rnew = r + (1 << PTHREAD_RWLOCK_READER_SHIFT);
      53  	}
      54        else
      55  	{
      56  	  /* If there is a writer that has acquired the lock and we are in
      57  	     a write phase, fail.  */
      58  	  if ((r & PTHREAD_RWLOCK_WRLOCKED) != 0)
      59  	    return EBUSY;
      60  	  else
      61  	    {
      62  	      /* If we do not care about potentially waiting writers, just
      63  		 try to acquire.  */
      64  	      rnew = (r + (1 << PTHREAD_RWLOCK_READER_SHIFT))
      65  		  ^ PTHREAD_RWLOCK_WRPHASE;
      66  	    }
      67  	}
      68        /* If we could have caused an overflow or take effect during an
      69  	 overflow, we just can / need to return EAGAIN.  There is no need to
      70  	 have actually modified the number of readers because we could have
      71  	 done that and cleaned up immediately.  */
      72        if (rnew >= PTHREAD_RWLOCK_READER_OVERFLOW)
      73  	return EAGAIN;
      74      }
      75    /* If the CAS fails, we retry; this prevents that tryrdlock fails spuriously
      76       (i.e., fails to acquire the lock although there is no writer), which is
      77       fine for C++14 but not currently allowed by POSIX.
      78       However, because tryrdlock must not appear to block, we should avoid
      79       starving this CAS loop due to constant changes to __readers:
      80       While normal rdlock readers that won't be able to acquire will just block
      81       (and we expect timeouts on timedrdlock to be longer than one retry of the
      82       CAS loop), we can have concurrently failing tryrdlock calls due to
      83       readers or writers that acquire and release in the meantime.  Using
      84       randomized exponential back-off to make a live-lock unlikely should be
      85       sufficient.
      86       TODO Back-off.
      87       Acquire MO so we synchronize with prior writers.  */
      88    while (!atomic_compare_exchange_weak_acquire (&rwlock->__data.__readers,
      89        &r, rnew));
      90  
      91    if ((r & PTHREAD_RWLOCK_WRPHASE) != 0)
      92      {
      93        /* Same as in __pthread_rwlock_rdlock_full:
      94  	 We started the read phase, so we are also responsible for
      95  	 updating the write-phase futex.  Relaxed MO is sufficient.
      96  	 We have to do the same steps as a writer would when handing over the
      97  	 read phase to use because other readers cannot distinguish between
      98  	 us and the writer.
      99  	 Note that __pthread_rwlock_tryrdlock callers will not have to be
     100  	 woken up because they will either see the read phase started by us
     101  	 or they will try to start it themselves; however, callers of
     102  	 __pthread_rwlock_rdlock_full just increase the reader count and then
     103  	 check what state the lock is in, so they cannot distinguish between
     104  	 us and a writer that acquired and released the lock in the
     105  	 meantime.  */
     106        if ((atomic_exchange_relaxed (&rwlock->__data.__wrphase_futex, 0)
     107  	  & PTHREAD_RWLOCK_FUTEX_USED) != 0)
     108  	{
     109  	  int private = __pthread_rwlock_get_private (rwlock);
     110  	  futex_wake (&rwlock->__data.__wrphase_futex, INT_MAX, private);
     111  	}
     112      }
     113  
     114    return 0;
     115  
     116  
     117  }
     118  versioned_symbol (libc, ___pthread_rwlock_tryrdlock,
     119  		  pthread_rwlock_tryrdlock, GLIBC_2_34);
     120  libc_hidden_ver (___pthread_rwlock_tryrdlock, __pthread_rwlock_tryrdlock)
     121  
     122  #if OTHER_SHLIB_COMPAT (libpthread, GLIBC_2_1, GLIBC_2_34)
     123  compat_symbol (libpthread, ___pthread_rwlock_tryrdlock,
     124  	       pthread_rwlock_tryrdlock, GLIBC_2_1);
     125  #endif
     126  #if OTHER_SHLIB_COMPAT (libpthread, GLIBC_2_2, GLIBC_2_34)
     127  compat_symbol (libpthread, ___pthread_rwlock_tryrdlock,
     128  	       __pthread_rwlock_tryrdlock, GLIBC_2_2);
     129  #endif