Blame nptl/pthread_rwlock_tryrdlock.c

Packit Service 82fcde
/* Copyright (C) 2002-2018 Free Software Foundation, Inc.
Packit Service 82fcde
   This file is part of the GNU C Library.
Packit Service 82fcde
   Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is free software; you can redistribute it and/or
Packit Service 82fcde
   modify it under the terms of the GNU Lesser General Public
Packit Service 82fcde
   License as published by the Free Software Foundation; either
Packit Service 82fcde
   version 2.1 of the License, or (at your option) any later version.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is distributed in the hope that it will be useful,
Packit Service 82fcde
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 82fcde
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 82fcde
   Lesser General Public License for more details.
Packit Service 82fcde
Packit Service 82fcde
   You should have received a copy of the GNU Lesser General Public
Packit Service 82fcde
   License along with the GNU C Library; if not, see
Packit Service 82fcde
   <http://www.gnu.org/licenses/>.  */
Packit Service 82fcde
Packit Service 82fcde
#include <errno.h>
Packit Service 82fcde
#include "pthreadP.h"
Packit Service 82fcde
#include <atomic.h>
Packit Service 82fcde
#include <stdbool.h>
Packit Service 82fcde
#include "pthread_rwlock_common.c"
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* See pthread_rwlock_common.c for an overview.  */
Packit Service 82fcde
int
Packit Service 82fcde
__pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock)
Packit Service 82fcde
{
Packit Service 82fcde
  /* For tryrdlock, we could speculate that we will succeed and go ahead and
Packit Service 82fcde
     register as a reader.  However, if we misspeculate, we have to do the
Packit Service 82fcde
     same steps as a timed-out rdlock, which will increase contention.
Packit Service 82fcde
     Therefore, there is a trade-off between being able to use a combinable
Packit Service 82fcde
     read-modify-write operation and a CAS loop as used below; we pick the
Packit Service 82fcde
     latter because it simplifies the code, and should perform better when
Packit Service 82fcde
     tryrdlock is used in cases where writers are infrequent.
Packit Service 82fcde
     Because POSIX does not require a failed trylock to "synchronize memory",
Packit Service 82fcde
     relaxed MO is sufficient here and on the failure path of the CAS
Packit Service 82fcde
     below.  */
Packit Service 82fcde
  unsigned int r = atomic_load_relaxed (&rwlock->__data.__readers);
Packit Service 82fcde
  unsigned int rnew;
Packit Service 82fcde
  do
Packit Service 82fcde
    {
Packit Service 82fcde
      if ((r & PTHREAD_RWLOCK_WRPHASE) == 0)
Packit Service 82fcde
	{
Packit Service 82fcde
	  /* If we are in a read phase, try to acquire unless there is a
Packit Service 82fcde
	     primary writer and we prefer writers and there will be no
Packit Service 82fcde
	     recursive read locks.  */
Packit Service 82fcde
	  if (((r & PTHREAD_RWLOCK_WRLOCKED) != 0)
Packit Service 82fcde
	      && (rwlock->__data.__flags
Packit Service 82fcde
		  == PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP))
Packit Service 82fcde
	    return EBUSY;
Packit Service 82fcde
	  rnew = r + (1 << PTHREAD_RWLOCK_READER_SHIFT);
Packit Service 82fcde
	}
Packit Service 82fcde
      else
Packit Service 82fcde
	{
Packit Service 82fcde
	  /* If there is a writer that has acquired the lock and we are in
Packit Service 82fcde
	     a write phase, fail.  */
Packit Service 82fcde
	  if ((r & PTHREAD_RWLOCK_WRLOCKED) != 0)
Packit Service 82fcde
	    return EBUSY;
Packit Service 82fcde
	  else
Packit Service 82fcde
	    {
Packit Service 82fcde
	      /* If we do not care about potentially waiting writers, just
Packit Service 82fcde
		 try to acquire.  */
Packit Service 82fcde
	      rnew = (r + (1 << PTHREAD_RWLOCK_READER_SHIFT))
Packit Service 82fcde
		  ^ PTHREAD_RWLOCK_WRPHASE;
Packit Service 82fcde
	    }
Packit Service 82fcde
	}
Packit Service 82fcde
      /* If we could have caused an overflow or take effect during an
Packit Service 82fcde
	 overflow, we just can / need to return EAGAIN.  There is no need to
Packit Service 82fcde
	 have actually modified the number of readers because we could have
Packit Service 82fcde
	 done that and cleaned up immediately.  */
Packit Service 82fcde
      if (rnew >= PTHREAD_RWLOCK_READER_OVERFLOW)
Packit Service 82fcde
	return EAGAIN;
Packit Service 82fcde
    }
Packit Service 82fcde
  /* If the CAS fails, we retry; this prevents that tryrdlock fails spuriously
Packit Service 82fcde
     (i.e., fails to acquire the lock although there is no writer), which is
Packit Service 82fcde
     fine for C++14 but not currently allowed by POSIX.
Packit Service 82fcde
     However, because tryrdlock must not appear to block, we should avoid
Packit Service 82fcde
     starving this CAS loop due to constant changes to __readers:
Packit Service 82fcde
     While normal rdlock readers that won't be able to acquire will just block
Packit Service 82fcde
     (and we expect timeouts on timedrdlock to be longer than one retry of the
Packit Service 82fcde
     CAS loop), we can have concurrently failing tryrdlock calls due to
Packit Service 82fcde
     readers or writers that acquire and release in the meantime.  Using
Packit Service 82fcde
     randomized exponential back-off to make a live-lock unlikely should be
Packit Service 82fcde
     sufficient.
Packit Service 82fcde
     TODO Back-off.
Packit Service 82fcde
     Acquire MO so we synchronize with prior writers.  */
Packit Service 82fcde
  while (!atomic_compare_exchange_weak_acquire (&rwlock->__data.__readers,
Packit Service 82fcde
      &r, rnew));
Packit Service 82fcde
Packit Service 82fcde
  if ((r & PTHREAD_RWLOCK_WRPHASE) != 0)
Packit Service 82fcde
    {
Packit Service 82fcde
      /* Same as in __pthread_rwlock_rdlock_full:
Packit Service 82fcde
	 We started the read phase, so we are also responsible for
Packit Service 82fcde
	 updating the write-phase futex.  Relaxed MO is sufficient.
Packit Service 82fcde
	 Note that there can be no other reader that we have to wake
Packit Service 82fcde
	 because all other readers will see the read phase started by us
Packit Service 82fcde
	 (or they will try to start it themselves); if a writer started
Packit Service 82fcde
	 the read phase, we cannot have started it.  Furthermore, we
Packit Service 82fcde
	 cannot discard a PTHREAD_RWLOCK_FUTEX_USED flag because we will
Packit Service 82fcde
	 overwrite the value set by the most recent writer (or the readers
Packit Service 82fcde
	 before it in case of explicit hand-over) and we know that there
Packit Service 82fcde
	 are no waiting readers.  */
Packit Service 82fcde
      atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 0);
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  return 0;
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
}
Packit Service 82fcde
strong_alias (__pthread_rwlock_tryrdlock, pthread_rwlock_tryrdlock)