1  /* elision-lock.c: Elided pthread mutex lock.
       2     Copyright (C) 2015-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <stdio.h>
      20  #include <pthread.h>
      21  #include <pthreadP.h>
      22  #include <lowlevellock.h>
      23  #include <elision-conf.h>
      24  #include "htm.h"
      25  
      26  #ifndef EXTRAARG
      27  # define EXTRAARG
      28  #endif
      29  #ifndef LLL_LOCK
      30  # define LLL_LOCK(a,b) lll_lock(a,b), 0
      31  #endif
      32  
      33  #define aconf __elision_aconf
      34  
      35  /* Adaptive lock using transactions.
      36     By default the lock region is run as a transaction, and when it
      37     aborts or the lock is busy the lock adapts itself.  */
      38  
      39  int
      40  __lll_lock_elision (int *lock, short *adapt_count, EXTRAARG int pshared)
      41  {
      42    /* adapt_count is accessed concurrently but is just a hint.  Thus,
      43       use atomic accesses but relaxed MO is sufficient.  */
      44    if (atomic_load_relaxed (adapt_count) > 0)
      45      {
      46        goto use_lock;
      47      }
      48  
      49    for (int i = aconf.try_tbegin; i > 0; i--)
      50      {
      51        if (__libc_tbegin (0))
      52  	{
      53  	  if (*lock == 0)
      54  	    return 0;
      55  	  /* Lock was busy.  Fall back to normal locking.  */
      56  	  __libc_tabort (_ABORT_LOCK_BUSY);
      57  	}
      58        else
      59  	{
      60  	  /* A persistent failure indicates that a retry will probably
      61  	     result in another failure.  Use normal locking now and
      62  	     for the next couple of calls.  */
      63  	  if (_TEXASRU_FAILURE_PERSISTENT (__builtin_get_texasru ()))
      64  	    {
      65  	      if (aconf.skip_lock_internal_abort > 0)
      66  		atomic_store_relaxed (adapt_count,
      67  				      aconf.skip_lock_internal_abort);
      68  	      goto use_lock;
      69  	    }
      70  	}
      71       }
      72  
      73    /* Fall back to locks for a bit if retries have been exhausted */
      74    if (aconf.try_tbegin > 0 && aconf.skip_lock_out_of_tbegin_retries > 0)
      75      atomic_store_relaxed (adapt_count,
      76  			  aconf.skip_lock_out_of_tbegin_retries);
      77  
      78  use_lock:
      79    return LLL_LOCK ((*lock), pshared);
      80  }
      81  libc_hidden_def (__lll_lock_elision)