Blame sysdeps/x86/elide.h

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