Blame nptl/pthread_spin_lock.c

Packit 6c4009
/* pthread_spin_lock -- lock a spin lock.  Generic version.
Packit 6c4009
   Copyright (C) 2012-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <atomic.h>
Packit 6c4009
#include "pthreadP.h"
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
pthread_spin_lock (pthread_spinlock_t *lock)
Packit 6c4009
{
Packit 6c4009
  int val = 0;
Packit 6c4009
Packit 6c4009
  /* We assume that the first try mostly will be successful, thus we use
Packit 6c4009
     atomic_exchange if it is not implemented by a CAS loop (we also assume
Packit 6c4009
     that atomic_exchange can be faster if it succeeds, see
Packit 6c4009
     ATOMIC_EXCHANGE_USES_CAS).  Otherwise, we use a weak CAS and not an
Packit 6c4009
     exchange so we bail out after the first failed attempt to change the
Packit 6c4009
     state.  For the subsequent attempts we use atomic_compare_and_exchange
Packit 6c4009
     after we observe that the lock is not acquired.
Packit 6c4009
     See also comment in pthread_spin_trylock.
Packit 6c4009
     We use acquire MO to synchronize-with the release MO store in
Packit 6c4009
     pthread_spin_unlock, and thus ensure that prior critical sections
Packit 6c4009
     happen-before this critical section.  */
Packit 6c4009
#if ! ATOMIC_EXCHANGE_USES_CAS
Packit 6c4009
  /* Try to acquire the lock with an exchange instruction as this architecture
Packit 6c4009
     has such an instruction and we assume it is faster than a CAS.
Packit 6c4009
     The acquisition succeeds if the lock is not in an acquired state.  */
Packit 6c4009
  if (__glibc_likely (atomic_exchange_acquire (lock, 1) == 0))
Packit 6c4009
    return 0;
Packit 6c4009
#else
Packit 6c4009
  /* Try to acquire the lock with a CAS instruction as this architecture
Packit 6c4009
     has no exchange instruction.  The acquisition succeeds if the lock is not
Packit 6c4009
     acquired.  */
Packit 6c4009
  if (__glibc_likely (atomic_compare_exchange_weak_acquire (lock, &val, 1)))
Packit 6c4009
    return 0;
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  do
Packit 6c4009
    {
Packit 6c4009
      /* The lock is contended and we need to wait.  Going straight back
Packit 6c4009
	 to cmpxchg is not a good idea on many targets as that will force
Packit 6c4009
	 expensive memory synchronizations among processors and penalize other
Packit 6c4009
	 running threads.
Packit 6c4009
	 There is no technical reason for throwing in a CAS every now and then,
Packit 6c4009
	 and so far we have no evidence that it can improve performance.
Packit 6c4009
	 If that would be the case, we have to adjust other spin-waiting loops
Packit 6c4009
	 elsewhere, too!
Packit 6c4009
	 Thus we use relaxed MO reads until we observe the lock to not be
Packit 6c4009
	 acquired anymore.  */
Packit 6c4009
      do
Packit 6c4009
	{
Packit 6c4009
	  /* TODO Back-off.  */
Packit 6c4009
Packit 6c4009
	  atomic_spin_nop ();
Packit 6c4009
Packit 6c4009
	  val = atomic_load_relaxed (lock);
Packit 6c4009
	}
Packit 6c4009
      while (val != 0);
Packit 6c4009
Packit 6c4009
      /* We need acquire memory order here for the same reason as mentioned
Packit 6c4009
	 for the first try to lock the spinlock.  */
Packit 6c4009
    }
Packit 6c4009
  while (!atomic_compare_exchange_weak_acquire (lock, &val, 1));
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}