Blame sysdeps/unix/sysv/linux/s390/elision-trylock.c

Packit 6c4009
/* Elided pthread mutex trylock.
Packit 6c4009
   Copyright (C) 2014-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 <pthread.h>
Packit 6c4009
#include <pthreadP.h>
Packit 6c4009
#include <lowlevellock.h>
Packit 6c4009
#include <htm.h>
Packit 6c4009
#include <elision-conf.h>
Packit 6c4009
Packit 6c4009
#define aconf __elision_aconf
Packit 6c4009
Packit 6c4009
/* Try to elide a futex trylock.  FUTEX is the futex variable.  ADAPT_COUNT is
Packit 6c4009
   the adaptation counter in the mutex.  */
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
__lll_trylock_elision (int *futex, short *adapt_count)
Packit 6c4009
{
Packit 6c4009
  /* Implement POSIX semantics by forbiding nesting elided trylocks.
Packit 6c4009
     Sorry.  After the abort the code is re-executed
Packit 6c4009
     non transactional and if the lock was already locked
Packit 6c4009
     return an error.  */
Packit 6c4009
  if (__libc_tx_nesting_depth () > 0)
Packit 6c4009
    {
Packit 6c4009
      /* Note that this abort may terminate an outermost transaction that
Packit 6c4009
	 was created outside glibc.
Packit 6c4009
	 This persistently aborts the current transactions to force
Packit 6c4009
	 them to use the default lock instead of retrying transactions
Packit 6c4009
	 until their try_tbegin is zero.
Packit 6c4009
      */
Packit 6c4009
      __libc_tabort (_HTM_FIRST_USER_ABORT_CODE | 1);
Packit 6c4009
      __builtin_unreachable ();
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* adapt_count can be accessed concurrently; these accesses can be both
Packit 6c4009
     inside of transactions (if critical sections are nested and the outer
Packit 6c4009
     critical section uses lock elision) and outside of transactions.  Thus,
Packit 6c4009
     we need to use atomic accesses to avoid data races.  However, the
Packit 6c4009
     value of adapt_count is just a hint, so relaxed MO accesses are
Packit 6c4009
     sufficient.  */
Packit 6c4009
    if (atomic_load_relaxed (adapt_count) <= 0 && aconf.try_tbegin > 0)
Packit 6c4009
    {
Packit 6c4009
      int status = __libc_tbegin ((void *) 0);
Packit 6c4009
      if (__glibc_likely (status  == _HTM_TBEGIN_STARTED))
Packit 6c4009
	{
Packit 6c4009
	  /* Check the futex to make sure nobody has touched it in the
Packit 6c4009
	     mean time.  This forces the futex into the cache and makes
Packit 6c4009
	     sure the transaction aborts if another thread acquires the lock
Packit 6c4009
	     concurrently.  */
Packit 6c4009
	  if (__glibc_likely (atomic_load_relaxed (futex) == 0))
Packit 6c4009
	    /* Lock was free.  Return to user code in a transaction.  */
Packit 6c4009
	    return 0;
Packit 6c4009
Packit 6c4009
	  /* Lock was busy.  Fall back to normal locking.
Packit 6c4009
	     This can be the case if e.g. adapt_count was decremented to zero
Packit 6c4009
	     by a former release and another thread has been waken up and
Packit 6c4009
	     acquired it.
Packit 6c4009
	     Since we are in a non-nested transaction there is no need to abort,
Packit 6c4009
	     which is expensive.  Simply end the started transaction.  */
Packit 6c4009
	  __libc_tend ();
Packit 6c4009
	  /* Note: Changing the adapt_count here might abort a transaction on a
Packit 6c4009
	     different CPU, but that could happen anyway when the futex is
Packit 6c4009
	     acquired, so there's no need to check the nesting depth here.
Packit 6c4009
	     See above for why relaxed MO is sufficient.  */
Packit 6c4009
	  if (aconf.skip_lock_busy > 0)
Packit 6c4009
	    atomic_store_relaxed (adapt_count, aconf.skip_lock_busy);
Packit 6c4009
	}
Packit 6c4009
      else if (status != _HTM_TBEGIN_TRANSIENT)
Packit 6c4009
	{
Packit 6c4009
	  /* A persistent abort (cc 1 or 3) indicates that a retry is
Packit 6c4009
	     probably futile.  Use the normal locking now and for the
Packit 6c4009
	     next couple of calls.
Packit 6c4009
	     Be careful to avoid writing to the lock.  */
Packit 6c4009
	  if (aconf.skip_trylock_internal_abort > 0)
Packit 6c4009
	    *adapt_count = aconf.skip_trylock_internal_abort;
Packit 6c4009
	}
Packit 6c4009
      /* Could do some retries here.  */
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Use normal locking as fallback path if the transaction does not
Packit 6c4009
     succeed.  */
Packit 6c4009
  return lll_trylock (*futex);
Packit 6c4009
}