text: /* Copyright (C) 2009-2017 Free Software Foundation, Inc.
     Contributed by Richard Henderson <rth@redhat.com>.
  
     This file is part of the GNU Transactional Memory Library (libitm).
  
     Libitm is free software; you can redistribute it and/or modify it
     under the terms of the GNU General Public License as published by
     the Free Software Foundation; either version 3 of the License, or
     (at your option) any later version.
  
     Libitm is distributed in the hope that it will be useful, but WITHOUT ANY
     WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
     FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
     more details.
  
     Under Section 7 of GPL version 3, you are granted additional
     permissions described in the GCC Runtime Library Exception, version
     3.1, as published by the Free Software Foundation.
  
     You should have received a copy of the GNU General Public License and
     a copy of the GCC Runtime Library Exception along with this program;
     see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
     <http://www.gnu.org/licenses/>.  */
  
  #include "libitm_i.h"
  
  using namespace GTM;
  
  struct clone_entry
  {
    void *orig, *clone;
  };
  
  struct clone_table
  {
    clone_entry *table;
    size_t size;
    clone_table *next;
  };
  
  static clone_table *all_tables;
  
  static void *
  find_clone (void *ptr)
  {
    clone_table *table;
  
    for (table = all_tables; table ; table = table->next)
      {
        clone_entry *t = table->table;
        size_t lo = 0, hi = table->size, i;
  
        /* Quick test for whether PTR is present in this table.  */
        if (ptr < t[0].orig || ptr > t[hi - 1].orig)
  	continue;
  
        /* Otherwise binary search.  */
        while (lo < hi)
  	{
  	  i = (lo + hi) / 2;
  	  if (ptr < t[i].orig)
  	    hi = i;
  	  else if (ptr > t[i].orig)
  	    lo = i + 1;
  	  else
  	    return t[i].clone;
  	}
  
        /* Given the quick test above, if we don't find the entry in
  	 this table then it doesn't exist.  */
        break;
      }
  
    return NULL;
  }
  
  
  void * ITM_REGPARM
  _ITM_getTMCloneOrIrrevocable (void *ptr)
  {
    void *ret = find_clone (ptr);
    if (ret)
      return ret;
  
    gtm_thr()->serialirr_mode ();
  
    return ptr;
  }
  
  void * ITM_REGPARM
  _ITM_getTMCloneSafe (void *ptr)
  {
    void *ret = find_clone (ptr);
    if (ret == NULL)
      abort ();
    return ret;
  }
  
  static int
  clone_entry_compare (const void *a, const void *b)
  {
    const clone_entry *aa = (const clone_entry *)a;
    const clone_entry *bb = (const clone_entry *)b;
  
    if (aa->orig < bb->orig)
      return -1;
    else if (aa->orig > bb->orig)
      return 1;
    else
      return 0;
  }
  
  namespace {
  
  // Within find_clone, we know that we are inside a transaction.  Because
  // of that, we have already synchronized with serial_lock.  By taking the
  // serial_lock for write, we exclude all transactions while we make this
  // change to the clone tables, without having to synchronize on a separate
  // lock.  Do be careful not to attempt a recursive write lock.
  
  class ExcludeTransaction
  {
    bool do_lock;
  
   public:
    ExcludeTransaction()
    {
      gtm_thread *tx = gtm_thr();
      do_lock = !(tx && (tx->state & gtm_thread::STATE_SERIAL));
  
      if (do_lock)
        gtm_thread::serial_lock.write_lock ();
    }
  
    ~ExcludeTransaction()
    {
      if (do_lock)
        gtm_thread::serial_lock.write_unlock ();
    }
  };
  
  } // end anon namespace
  
  
  void
  _ITM_registerTMCloneTable (void *xent, size_t size)
  {
    clone_entry *ent = static_cast<clone_entry *>(xent);
    clone_table *table;
  
    table = (clone_table *) xmalloc (sizeof (clone_table));
    table->table = ent;
    table->size = size;
  
    qsort (ent, size, sizeof (clone_entry), clone_entry_compare);
  
    // Hold the serial_lock while we update the ALL_TABLES datastructure.
    {
      ExcludeTransaction exclude;
      table->next = all_tables;
      all_tables = table;
    }
  }
  
  void
  _ITM_deregisterTMCloneTable (void *xent)
  {
    clone_entry *ent = static_cast<clone_entry *>(xent);
    clone_table *tab;
  
    // Hold the serial_lock while we update the ALL_TABLES datastructure.
    {
      ExcludeTransaction exclude;
      clone_table **pprev;
  
      for (pprev = &all_tables;
  	 tab = *pprev, tab->table != ent;
  	 pprev = &tab->next)
        continue;
      *pprev = tab->next;
    }
  
    free (tab);
  }
  