Blame sysdeps/x86/elide.h

Packit 6c4009
/* elide.h: Generic lock elision support.
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
#ifndef ELIDE_H
Packit 6c4009
#define ELIDE_H 1
Packit 6c4009
Packit 6c4009
#include <hle.h>
Packit 6c4009
#include <elision-conf.h>
Packit 6c4009
#include <atomic.h>
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Adapt elision with ADAPT_COUNT and STATUS and decide retries.  */
Packit 6c4009
Packit 6c4009
static inline bool
Packit 6c4009
elision_adapt(signed char *adapt_count, unsigned int status)
Packit 6c4009
{
Packit 6c4009
  if (status & _XABORT_RETRY)
Packit 6c4009
    return false;
Packit 6c4009
  if ((status & _XABORT_EXPLICIT)
Packit 6c4009
      && _XABORT_CODE (status) == _ABORT_LOCK_BUSY)
Packit 6c4009
    {
Packit 6c4009
      /* Right now we skip here.  Better would be to wait a bit
Packit 6c4009
	 and retry.  This likely needs some spinning. Be careful
Packit 6c4009
	 to avoid writing the lock.
Packit 6c4009
	 Using relaxed MO and separate atomic accesses is sufficient because
Packit 6c4009
	 adapt_count is just a hint.  */
Packit 6c4009
      if (atomic_load_relaxed (adapt_count) != __elision_aconf.skip_lock_busy)
Packit 6c4009
	atomic_store_relaxed (adapt_count, __elision_aconf.skip_lock_busy);
Packit 6c4009
    }
Packit 6c4009
  /* Internal abort.  There is no chance for retry.
Packit 6c4009
     Use the normal locking and next time use lock.
Packit 6c4009
     Be careful to avoid writing to the lock.  See above for MO.  */
Packit 6c4009
  else if (atomic_load_relaxed (adapt_count)
Packit 6c4009
      != __elision_aconf.skip_lock_internal_abort)
Packit 6c4009
    atomic_store_relaxed (adapt_count,
Packit 6c4009
	__elision_aconf.skip_lock_internal_abort);
Packit 6c4009
  return true;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* is_lock_free must be executed inside the transaction */
Packit 6c4009
Packit 6c4009
/* Returns true if lock defined by IS_LOCK_FREE was elided.
Packit 6c4009
   ADAPT_COUNT is a per-lock state variable; it must be accessed atomically
Packit 6c4009
   to avoid data races but is just a hint, so using relaxed MO and separate
Packit 6c4009
   atomic loads and stores instead of atomic read-modify-write operations is
Packit 6c4009
   sufficient.  */
Packit 6c4009
Packit 6c4009
#define ELIDE_LOCK(adapt_count, is_lock_free)			\
Packit 6c4009
  ({								\
Packit 6c4009
    int ret = 0;						\
Packit 6c4009
								\
Packit 6c4009
    if (atomic_load_relaxed (&(adapt_count)) <= 0)		\
Packit 6c4009
      {								\
Packit 6c4009
        for (int i = __elision_aconf.retry_try_xbegin; i > 0; i--) \
Packit 6c4009
          {							\
Packit 6c4009
            unsigned int status;				\
Packit 6c4009
	    if ((status = _xbegin ()) == _XBEGIN_STARTED)	\
Packit 6c4009
	      {							\
Packit 6c4009
	        if (is_lock_free)				\
Packit 6c4009
	          {						\
Packit 6c4009
		    ret = 1;					\
Packit 6c4009
		    break;					\
Packit 6c4009
	          }						\
Packit 6c4009
	        _xabort (_ABORT_LOCK_BUSY);			\
Packit 6c4009
	      }							\
Packit 6c4009
	    if (!elision_adapt (&(adapt_count), status))	\
Packit 6c4009
	      break;						\
Packit 6c4009
          }							\
Packit 6c4009
      }								\
Packit 6c4009
    else 							\
Packit 6c4009
      atomic_store_relaxed (&(adapt_count),			\
Packit 6c4009
	  atomic_load_relaxed (&(adapt_count)) - 1);		\
Packit 6c4009
    ret;							\
Packit 6c4009
  })
Packit 6c4009
Packit 6c4009
/* Returns true if lock defined by IS_LOCK_FREE was try-elided.
Packit 6c4009
   ADAPT_COUNT is a per-lock state variable.  */
Packit 6c4009
Packit 6c4009
#define ELIDE_TRYLOCK(adapt_count, is_lock_free, write) ({	\
Packit 6c4009
  int ret = 0;						\
Packit 6c4009
  if (__elision_aconf.retry_try_xbegin > 0)		\
Packit 6c4009
    {  							\
Packit 6c4009
      if (write)					\
Packit 6c4009
        _xabort (_ABORT_NESTED_TRYLOCK);		\
Packit 6c4009
      ret = ELIDE_LOCK (adapt_count, is_lock_free);     \
Packit 6c4009
    }							\
Packit 6c4009
    ret;						\
Packit 6c4009
    })
Packit 6c4009
Packit 6c4009
/* Returns true if lock defined by IS_LOCK_FREE was elided.  The call
Packit 6c4009
   to _xend crashes if the application incorrectly tries to unlock a
Packit 6c4009
   lock which has not been locked.  */
Packit 6c4009
Packit 6c4009
#define ELIDE_UNLOCK(is_lock_free)		\
Packit 6c4009
  ({						\
Packit 6c4009
  int ret = 0;					\
Packit 6c4009
  if (is_lock_free)				\
Packit 6c4009
    {						\
Packit 6c4009
      _xend ();					\
Packit 6c4009
      ret = 1;					\
Packit 6c4009
    }						\
Packit 6c4009
  ret;						\
Packit 6c4009
  })
Packit 6c4009
Packit 6c4009
#endif