Blame sysdeps/powerpc/nptl/elide.h

Packit 6c4009
/* elide.h: Generic lock elision support for powerpc.
Packit 6c4009
   Copyright (C) 2015-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
#ifndef ELIDE_PPC_H
Packit 6c4009
# define ELIDE_PPC_H
Packit 6c4009
Packit 6c4009
# include <htm.h>
Packit 6c4009
# include <elision-conf.h>
Packit 6c4009
Packit 6c4009
/* Get the new value of adapt_count according to the elision
Packit 6c4009
   configurations.  Returns true if the system should retry again or false
Packit 6c4009
   otherwise.  */
Packit 6c4009
static inline bool
Packit 6c4009
__get_new_count (uint8_t *adapt_count, int attempt)
Packit 6c4009
{
Packit 6c4009
  /* A persistent failure indicates that a retry will probably
Packit 6c4009
     result in another failure.  Use normal locking now and
Packit 6c4009
     for the next couple of calls.  */
Packit 6c4009
  if (_TEXASRU_FAILURE_PERSISTENT (__builtin_get_texasru ()))
Packit 6c4009
    {
Packit 6c4009
      if (__elision_aconf.skip_lock_internal_abort > 0)
Packit 6c4009
	*adapt_count = __elision_aconf.skip_lock_internal_abort;
Packit 6c4009
      return false;
Packit 6c4009
    }
Packit 6c4009
  /* Same logic as above, but for a number of temporary failures in a
Packit 6c4009
     a row.  */
Packit 6c4009
  else if (attempt <= 1 && __elision_aconf.skip_lock_out_of_tbegin_retries > 0
Packit 6c4009
	   && __elision_aconf.try_tbegin > 0)
Packit 6c4009
    *adapt_count = __elision_aconf.skip_lock_out_of_tbegin_retries;
Packit 6c4009
  return true;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* CONCURRENCY NOTES:
Packit 6c4009
Packit 6c4009
   The evaluation of the macro expression is_lock_free encompasses one or
Packit 6c4009
   more loads from memory locations that are concurrently modified by other
Packit 6c4009
   threads.  For lock elision to work, this evaluation and the rest of the
Packit 6c4009
   critical section protected by the lock must be atomic because an
Packit 6c4009
   execution with lock elision must be equivalent to an execution in which
Packit 6c4009
   the lock would have been actually acquired and released.  Therefore, we
Packit 6c4009
   evaluate is_lock_free inside of the transaction that represents the
Packit 6c4009
   critical section for which we want to use lock elision, which ensures
Packit 6c4009
   the atomicity that we require.  */
Packit 6c4009
Packit 6c4009
/* Returns 0 if the lock defined by is_lock_free was elided.
Packit 6c4009
   ADAPT_COUNT is a per-lock state variable.  */
Packit 6c4009
# define ELIDE_LOCK(adapt_count, is_lock_free)				\
Packit 6c4009
  ({									\
Packit 6c4009
    int ret = 0;							\
Packit 6c4009
    if (adapt_count > 0)						\
Packit 6c4009
      (adapt_count)--;							\
Packit 6c4009
    else								\
Packit 6c4009
      for (int i = __elision_aconf.try_tbegin; i > 0; i--)		\
Packit 6c4009
	{								\
Packit 6c4009
	  if (__libc_tbegin (0))					\
Packit 6c4009
	    {								\
Packit 6c4009
	      if (is_lock_free)						\
Packit 6c4009
		{							\
Packit 6c4009
		  ret = 1;						\
Packit 6c4009
		  break;						\
Packit 6c4009
		}							\
Packit 6c4009
	      __libc_tabort (_ABORT_LOCK_BUSY);				\
Packit 6c4009
	    }								\
Packit 6c4009
	  else								\
Packit 6c4009
	    if (!__get_new_count (&adapt_count,i))			\
Packit 6c4009
	      break;							\
Packit 6c4009
	}								\
Packit 6c4009
    ret;								\
Packit 6c4009
  })
Packit 6c4009
Packit 6c4009
# define ELIDE_TRYLOCK(adapt_count, is_lock_free, write)	\
Packit 6c4009
  ({								\
Packit 6c4009
    int ret = 0;						\
Packit 6c4009
    if (__elision_aconf.try_tbegin > 0)				\
Packit 6c4009
      {								\
Packit 6c4009
	if (write)						\
Packit 6c4009
	  __libc_tabort (_ABORT_NESTED_TRYLOCK);		\
Packit 6c4009
	ret = ELIDE_LOCK (adapt_count, is_lock_free);		\
Packit 6c4009
      }								\
Packit 6c4009
    ret;							\
Packit 6c4009
  })
Packit 6c4009
Packit 6c4009
Packit 6c4009
static inline bool
Packit 6c4009
__elide_unlock (int is_lock_free)
Packit 6c4009
{
Packit 6c4009
  if (is_lock_free)
Packit 6c4009
    {
Packit 6c4009
      /* This code is expected to crash when trying to unlock a lock not
Packit 6c4009
	 held by this thread.  More information is available in the
Packit 6c4009
	 __pthread_rwlock_unlock() implementation.  */
Packit 6c4009
      __libc_tend (0);
Packit 6c4009
      return true;
Packit 6c4009
    }
Packit 6c4009
  return false;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
# define ELIDE_UNLOCK(is_lock_free) \
Packit 6c4009
  __elide_unlock (is_lock_free)
Packit 6c4009
Packit 6c4009
#endif