#define AO_REQUIRE_CAS
#include "atomic_ops_stack.h"
+/* This function call must be a part of a do-while loop with a CAS */
+/* designating the condition of the loop (see the use cases below). */
+#ifdef AO_THREAD_SANITIZER
+ AO_ATTR_NO_SANITIZE_THREAD
+ static void store_before_cas(AO_t *addr, AO_t value)
+ {
+ *addr = value;
+ }
+#else
+# define store_before_cas(addr, value) (void)(*(addr) = (value))
+#endif
+
#ifdef AO_USE_ALMOST_LOCK_FREE
void AO_pause(int); /* defined in atomic_ops.c */
/* 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. */
-AO_ATTR_NO_SANITIZE_THREAD
void AO_stack_push_explicit_aux_release(volatile AO_t *list, AO_t *x,
AO_stack_aux *a)
{
do
{
next = AO_load(list);
- *x = next; /* data race is OK here */
+ store_before_cas(x, next);
}
while (AO_EXPECT_FALSE(!AO_compare_and_swap_release(list, next, x_bits)));
}
# define PRECHECK(a)
#endif
+/* This function is used before CAS in the below AO_stack_pop() and the */
+/* data race (reported by TSan) is OK because it results in a retry. */
+#ifdef AO_THREAD_SANITIZER
+ AO_ATTR_NO_SANITIZE_THREAD
+ static AO_t AO_load_next(volatile AO_t *first_ptr)
+ {
+ /* Assuming an architecture on which loads of word type are atomic. */
+ /* AO_load cannot be used here because it cannot be instructed to */
+ /* suppress the warning about the race. */
+ return *first_ptr;
+ }
+#else
+# define AO_load_next AO_load
+#endif
+
AO_t *
AO_stack_pop_explicit_aux_acquire(volatile AO_t *list, AO_stack_aux * a)
{
goto retry;
}
first_ptr = AO_REAL_NEXT_PTR(first);
- next = AO_load(first_ptr);
+ next = AO_load_next(first_ptr);
# if defined(__alpha__) && (__GNUC__ == 4)
if (!AO_compare_and_swap_release(list, first, next))
# else
#else /* ! USE_ALMOST_LOCK_FREE */
+/* The functionality is the same as of AO_load_next but the atomicity */
+/* is not needed. The usage is similar to that of store_before_cas. */
+#ifdef AO_THREAD_SANITIZER
+ /* TODO: If compiled by Clang (as of clang-4.0) with -O3 flag, */
+ /* no_sanitize attribute is ignored unless the argument is volatile. */
+# if defined(__clang__)
+# define LOAD_BEFORE_CAS_VOLATILE volatile
+# else
+# define LOAD_BEFORE_CAS_VOLATILE /* empty */
+# endif
+ AO_ATTR_NO_SANITIZE_THREAD
+ static AO_t load_before_cas(const LOAD_BEFORE_CAS_VOLATILE AO_t *addr)
+ {
+ return *addr;
+ }
+#else
+# define load_before_cas(addr) (*(addr))
+#endif
+
/* Better names for fields in AO_stack_t */
#define ptr AO_val2
#define version AO_val1
volatile /* non-static */ AO_t AO_noop_sink;
#endif
-AO_ATTR_NO_SANITIZE_THREAD
void AO_stack_push_release(AO_stack_t *list, AO_t *element)
{
AO_t next;
do {
next = AO_load(&(list -> ptr));
- *element = next; /* data race is OK here */
+ store_before_cas(element, next);
} while (AO_EXPECT_FALSE(!AO_compare_and_swap_release(&(list -> ptr),
next, (AO_t)element)));
/* This uses a narrow CAS here, an old optimization suggested */
# endif
}
-AO_ATTR_NO_SANITIZE_THREAD
AO_t *AO_stack_pop_acquire(AO_stack_t *list)
{
# if defined(__clang__) && !AO_CLANG_PREREQ(3, 5)
/* 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; /* data race is OK here */
+ if (NULL == cptr)
+ return NULL;
+ next = load_before_cas((AO_t *)cptr);
} while (AO_EXPECT_FALSE(!AO_compare_double_and_swap_double_release(list,
cversion, (AO_t)cptr,
cversion+1, (AO_t)next)));
/* Again version must be loaded first, for different reason. */
version = AO_load_acquire(&(list -> version));
next_ptr = AO_load(&(list -> ptr));
- *element = next_ptr;
+ store_before_cas(element, next_ptr);
} while (!AO_compare_and_swap_double_release(
list, version,
version+1, (AO_t) element));
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));
+ if (NULL == cptr)
+ return NULL;
+ next = load_before_cas(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 */