implemented Setup.hs to build boehm cpp libs and install them;
[hs-boehmgc.git] / gc-7.2 / libatomic_ops / src / atomic_ops_stack.c
diff --git a/gc-7.2/libatomic_ops/src/atomic_ops_stack.c b/gc-7.2/libatomic_ops/src/atomic_ops_stack.c
new file mode 100644 (file)
index 0000000..dd29848
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 2005 Hewlett-Packard Development Company, L.P.
+ * Original Author: Hans Boehm
+ *
+ * This file may be redistributed and/or modified under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2, or (at your option) any later version.
+ *
+ * It 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 in the
+ * file doc/COPYING for more details.
+ */
+
+#if defined(HAVE_CONFIG_H)
+# include "config.h"
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#define AO_REQUIRE_CAS
+#include "atomic_ops_stack.h"
+
+#if defined(_MSC_VER) \
+    || defined(_WIN32) && !defined(__CYGWIN32__) && !defined(__CYGWIN__)
+  /* AO_pause not defined elsewhere */
+  /* FIXME: At least AO_spin should be factored out.    */
+#include <windows.h>
+
+AO_t dummy;
+
+/* Spin for 2**n units. */
+static void AO_spin(int n)
+{
+  int i;
+  AO_T j = AO_load(&dummy);
+
+  for (i = 0; i < (2 << n); ++i)
+    {
+       j *= 5;
+       j -= 4;
+    }
+  AO_store(&dummy, j);
+}
+
+void AO_pause(int n)
+{
+    if (n < 12)
+      AO_spin(n);
+    else
+      {
+        DWORD msecs;
+
+        /* Short async-signal-safe sleep. */
+        msecs = (n > 18? 100 : (1 << (n - 12)));
+        Sleep(msecs);
+      }
+}
+
+#else
+
+/* AO_pause is available elsewhere */
+
+extern void AO_pause(int);
+
+#endif
+
+#ifdef AO_USE_ALMOST_LOCK_FREE
+
+/* LIFO linked lists based on compare-and-swap.  We need to avoid       */
+/* the case of a node deletion and reinsertion while I'm deleting       */
+/* it, since that may cause my CAS to succeed eventhough the next       */
+/* pointer is now wrong.  Our solution is not fully lock-free, but it   */
+/* is good enough for signal handlers, provided we have a suitably low  */
+/* bound on the number of recursive signal handler reentries.           */
+/* A list consists of a first pointer and a blacklist                   */
+/* of pointer values that are currently being removed.  No list element */
+/* on the blacklist may be inserted.  If we would otherwise do so, we   */
+/* are allowed to insert a variant that differs only in the least       */
+/* significant, ignored, bits.  If the list is full, we wait.           */
+
+/* Crucial observation: A particular padded pointer x (i.e. pointer     */
+/* plus arbitrary low order bits) can never be newly inserted into      */
+/* a list while it's in the corresponding auxiliary data structure.     */
+
+/* The second argument is a pointer to the link field of the element    */
+/* to be inserted.                                                      */
+/* Both list headers and link fields contain "perturbed" pointers, i.e. */
+/* pointers with extra bits "or"ed into the low order bits.             */
+void
+AO_stack_push_explicit_aux_release(volatile AO_t *list, AO_t *x,
+                                   AO_stack_aux *a)
+{
+  AO_t x_bits = (AO_t)x;
+  AO_t next;
+
+  /* No deletions of x can start here, since x is not currently in the  */
+  /* list.                                                              */
+ retry:
+# if AO_BL_SIZE == 2
+  {
+    /* Start all loads as close to concurrently as possible. */
+    AO_t entry1 = AO_load(a -> AO_stack_bl);
+    AO_t entry2 = AO_load(a -> AO_stack_bl + 1);
+    if (entry1 == x_bits || entry2 == x_bits)
+      {
+        /* Entry is currently being removed.  Change it a little.       */
+          ++x_bits;
+          if ((x_bits & AO_BIT_MASK) == 0)
+            /* Version count overflowed;         */
+            /* EXTREMELY unlikely, but possible. */
+            x_bits = (AO_t)x;
+        goto retry;
+      }
+  }
+# else
+  {
+    int i;
+    for (i = 0; i < AO_BL_SIZE; ++i)
+      {
+        if (AO_load(a -> AO_stack_bl + i) == x_bits)
+          {
+            /* Entry is currently being removed.  Change it a little.   */
+              ++x_bits;
+              if ((x_bits & AO_BIT_MASK) == 0)
+                /* Version count overflowed;         */
+                /* EXTREMELY unlikely, but possible. */
+                x_bits = (AO_t)x;
+            goto retry;
+          }
+      }
+  }
+# endif
+  /* x_bits is not currently being deleted */
+  do
+    {
+      next = AO_load(list);
+      *x = next;
+    }
+  while(!AO_compare_and_swap_release(list, next, x_bits));
+}
+
+/*
+ * I concluded experimentally that checking a value first before
+ * performing a compare-and-swap is usually beneficial on X86, but
+ * slows things down appreciably with contention on Itanium.
+ * Since the Itanium behavior makes more sense to me (more cache line
+ * movement unless we're mostly reading, but back-off should guard
+ * against that), we take Itanium as the default.  Measurements on
+ * other multiprocessor architectures would be useful.  (On a uniprocessor,
+ * the initial check is almost certainly a very small loss.) - HB
+ */
+#ifdef __i386__
+# define PRECHECK(a) (a) == 0 &&
+#else
+# define PRECHECK(a)
+#endif
+
+AO_t *
+AO_stack_pop_explicit_aux_acquire(volatile AO_t *list, AO_stack_aux * a)
+{
+  unsigned i;
+  int j = 0;
+  AO_t first;
+  AO_t * first_ptr;
+  AO_t next;
+
+ retry:
+  first = AO_load(list);
+  if (0 == first) return 0;
+  /* Insert first into aux black list.                                  */
+  /* This may spin if more than AO_BL_SIZE removals using auxiliary     */
+  /* structure a are currently in progress.                             */
+  for (i = 0; ; )
+    {
+      if (PRECHECK(a -> AO_stack_bl[i])
+          AO_compare_and_swap_acquire(a->AO_stack_bl+i, 0, first))
+        break;
+      ++i;
+      if ( i >= AO_BL_SIZE )
+        {
+          i = 0;
+          AO_pause(++j);
+        }
+    }
+  assert(i >= 0 && i < AO_BL_SIZE);
+  assert(a -> AO_stack_bl[i] == first);
+  /* First is on the auxiliary black list.  It may be removed by        */
+  /* another thread before we get to it, but a new insertion of x       */
+  /* cannot be started here.                                            */
+  /* Only we can remove it from the black list.                         */
+  /* We need to make sure that first is still the first entry on the    */
+  /* list.  Otherwise it's possible that a reinsertion of it was        */
+  /* already started before we added the black list entry.              */
+  if (first != AO_load(list)) {
+    AO_store_release(a->AO_stack_bl+i, 0);
+    goto retry;
+  }
+  first_ptr = AO_REAL_NEXT_PTR(first);
+  next = AO_load(first_ptr);
+  if (!AO_compare_and_swap_release(list, first, next)) {
+    AO_store_release(a->AO_stack_bl+i, 0);
+    goto retry;
+  }
+  assert(*list != first);
+  /* Since we never insert an entry on the black list, this cannot have */
+  /* succeeded unless first remained on the list while we were running. */
+  /* Thus its next link cannot have changed out from under us, and we   */
+  /* removed exactly one entry and preserved the rest of the list.      */
+  /* Note that it is quite possible that an additional entry was        */
+  /* inserted and removed while we were running; this is OK since the   */
+  /* part of the list following first must have remained unchanged, and */
+  /* first must again have been at the head of the list when the        */
+  /* compare_and_swap succeeded.                                        */
+  AO_store_release(a->AO_stack_bl+i, 0);
+  return first_ptr;
+}
+
+#else /* ! USE_ALMOST_LOCK_FREE */
+
+/* Better names for fields in AO_stack_t */
+#define ptr AO_val2
+#define version AO_val1
+
+#if defined(AO_HAVE_compare_double_and_swap_double)
+
+void AO_stack_push_release(AO_stack_t *list, AO_t *element)
+{
+    AO_t next;
+
+    do {
+      next = AO_load(&(list -> ptr));
+      *element = next;
+    } while (!AO_compare_and_swap_release
+                    ( &(list -> ptr), next, (AO_t) element));
+    /* This uses a narrow CAS here, an old optimization suggested       */
+    /* by Treiber.  Pop is still safe, since we run into the ABA        */
+    /* problem only if there were both intervening "pop"s and "push"es. */
+    /* In that case we still see a change in the version number.        */
+}
+
+AO_t *AO_stack_pop_acquire(AO_stack_t *list)
+{
+#   ifdef __clang__
+      AO_t *volatile cptr;
+                        /* Use volatile to workaround a bug in          */
+                        /* clang-1.1/x86 causing test_stack failure.    */
+#   else
+      AO_t *cptr;
+#   endif
+    AO_t next;
+    AO_t cversion;
+
+    do {
+      /* Version must be loaded first.  */
+      cversion = AO_load_acquire(&(list -> version));
+      cptr = (AO_t *)AO_load(&(list -> ptr));
+      if (cptr == 0) return 0;
+      next = *cptr;
+    } while (!AO_compare_double_and_swap_double_release
+                    (list, cversion, (AO_t) cptr, cversion+1, (AO_t) next));
+    return cptr;
+}
+
+
+#elif defined(AO_HAVE_compare_and_swap_double)
+
+/* Needed for future IA64 processors.  No current clients? */
+
+#error Untested!  Probably doesnt work.
+
+/* We have a wide CAS, but only does an AO_t-wide comparison.   */
+/* We can't use the Treiber optimization, since we only check   */
+/* for an unchanged version number, not an unchanged pointer.   */
+void AO_stack_push_release(AO_stack_t *list, AO_t *element)
+{
+    AO_t version;
+    AO_t next_ptr;
+
+    do {
+      /* Again version must be loaded first, for different reason.      */
+      version = AO_load_acquire(&(list -> version));
+      next_ptr = AO_load(&(list -> ptr));
+      *element = next_ptr;
+    } while (!AO_compare_and_swap_double_release(
+                           list, version,
+                           version+1, (AO_t) element));
+}
+
+AO_t *AO_stack_pop_acquire(AO_stack_t *list)
+{
+    AO_t *cptr;
+    AO_t next;
+    AO_t cversion;
+
+    do {
+      cversion = AO_load_acquire(&(list -> version));
+      cptr = (AO_t *)AO_load(&(list -> ptr));
+      if (cptr == 0) return 0;
+      next = *cptr;
+    } while (!AO_compare_double_and_swap_double_release
+                    (list, cversion, (AO_t) cptr, cversion+1, next));
+    return cptr;
+}
+
+
+#endif /* AO_HAVE_compare_and_swap_double */
+
+#endif /* ! USE_ALMOST_LOCK_FREE */