(r10810). The rwlock test is kludged and needs de-kludging.
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@10811
dist_noinst_SCRIPTS = filter_stderr
EXTRA_DIST = \
+ annotate_hbefore.vgtest annotate_hbefore.stdout.exp \
+ annotate_hbefore.stderr.exp \
+ annotate_rwlock.vgtest annotate_rwlock.stdout.exp \
+ annotate_rwlock.stderr.exp \
bar_bad.vgtest bar_bad.stdout.exp bar_bad.stderr.exp \
bar_trivial.vgtest bar_trivial.stdout.exp bar_trivial.stderr.exp \
hg01_all_ok.vgtest hg01_all_ok.stdout.exp hg01_all_ok.stderr.exp \
hg04_race.vgtest hg04_race.stdout.exp hg04_race.stderr.exp \
hg05_race2.vgtest hg05_race2.stdout.exp hg05_race2.stderr.exp \
hg06_readshared.vgtest hg06_readshared.stdout.exp \
- hg06_readshared.stderr.exp \
+ hg06_readshared.stderr.exp \
pth_barrier1.vgtest pth_barrier1.stdout.exp pth_barrier1.stderr.exp \
pth_barrier2.vgtest pth_barrier2.stdout.exp pth_barrier2.stderr.exp \
pth_barrier3.vgtest pth_barrier3.stdout.exp pth_barrier3.stderr.exp \
# XXX: tc18_semabuse uses operations that are unsupported on Darwin. It
# should be conditionally compiled like tc20_verifywrap is.
check_PROGRAMS = \
+ annotate_hbefore \
+ annotate_rwlock \
hg01_all_ok \
hg02_deadlock \
hg03_inherit \
check_PROGRAMS += tc20_verifywrap
endif
+if HAVE_BUILTIN_ATOMIC
+check_PROGRAMS += annotate_rwlock
+endif
+
AM_CFLAGS += $(AM_FLAG_M3264_PRI)
AM_CXXFLAGS += $(AM_FLAG_M3264_PRI)
--- /dev/null
+
+/* Program which uses a happens-before edge to coordinate an access to
+ variable 'shared_var' between two threads. The h-b edge is created
+ by a custom (kludgesome!) mechanism and hence we need to use
+ ANNOTATES_HAPPEN_{BEFORE,AFTER} to explain to Helgrind what's going
+ on (else it reports a race). */
+
+#include <pthread.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "../../helgrind/helgrind.h"
+
+/* Todo: move all this do_acasW guff into a support library. It's
+ useful for multiple tests, not just this one.
+
+ XXX: all the do_acasW routines assume the supplied address
+ is UWord (naturally) aligned. */
+
+
+typedef unsigned long int UWord;
+
+#if defined(VGA_ppc64)
+
+// ppc64
+/* return 1 if success, 0 if failure */
+UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
+{
+ UWord old, success;
+
+ /* Fetch the old value, and set the reservation */
+ __asm__ __volatile__ (
+ "ldarx %0, 0,%1" "\n" // rD,rA,rB
+ : /*out*/ "=b"(old)
+ : /*in*/ "b"(addr)
+ : /*trash*/ "memory","cc"
+ );
+
+ /* If the old value isn't as expected, we've had it */
+ if (old != expected) return 0;
+
+ /* otherwise try to stuff the new value in */
+ __asm__ __volatile__(
+ "stdcx. %2, 0,%1" "\n" // rS,rA,rB
+ "mfcr %0" "\n\t"
+ "srdi %0,%0,29" "\n\t"
+ "andi. %0,%0,1" "\n"
+ : /*out*/ "=b"(success)
+ : /*in*/ "b"(addr), "b"(nyu)
+ );
+
+ assert(success == 0 || success == 1);
+ return success;
+}
+
+#elif defined(VGA_ppc32)
+
+// ppc32
+/* return 1 if success, 0 if failure */
+UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
+{
+ UWord old, success;
+
+ /* Fetch the old value, and set the reservation */
+ __asm__ __volatile__ (
+ "lwarx %0, 0,%1" "\n" // rD,rA,rB
+ : /*out*/ "=b"(old)
+ : /*in*/ "b"(addr)
+ : /*trash*/ "memory","cc"
+ );
+
+ /* If the old value isn't as expected, we've had it */
+ if (old != expected) return 0;
+
+ /* otherwise try to stuff the new value in */
+ __asm__ __volatile__(
+ "stwcx. %2, 0,%1" "\n" // rS,rA,rB
+ "mfcr %0" "\n\t"
+ "srwi %0,%0,29" "\n\t"
+ "andi. %0,%0,1" "\n"
+ : /*out*/ "=b"(success)
+ : /*in*/ "b"(addr), "b"(nyu)
+ );
+
+ assert(success == 0 || success == 1);
+ return success;
+}
+
+#elif defined(VGA_amd64)
+
+// amd64
+/* return 1 if success, 0 if failure */
+UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
+{
+ UWord block[4] = { (UWord)addr, expected, nyu, 2 };
+ __asm__ __volatile__(
+ "movq 0(%%rsi), %%rdi" "\n\t" // addr
+ "movq 8(%%rsi), %%rax" "\n\t" // expected
+ "movq 16(%%rsi), %%rbx" "\n\t" // nyu
+ "xorq %%rcx,%%rcx" "\n\t"
+ "lock; cmpxchgq %%rbx,(%%rdi)" "\n\t"
+ "setz %%cl" "\n\t"
+ "movq %%rcx, 24(%%rsi)" "\n"
+ : /*out*/
+ : /*in*/ "S"(&block[0])
+ : /*trash*/"memory","cc","rdi","rax","rbx","rcx"
+ );
+ assert(block[3] == 0 || block[3] == 1);
+ return block[3] & 1;
+}
+
+#elif defined(VGA_x86)
+
+// x86
+/* return 1 if success, 0 if failure */
+UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
+{
+ UWord block[4] = { (UWord)addr, expected, nyu, 2 };
+ __asm__ __volatile__(
+ "movl 0(%%esi), %%edi" "\n\t" // addr
+ "movl 4(%%esi), %%eax" "\n\t" // expected
+ "movl 8(%%esi), %%ebx" "\n\t" // nyu
+ "xorl %%ecx,%%ecx" "\n\t"
+ "lock; cmpxchgl %%ebx,(%%edi)" "\n\t"
+ "setz %%cl" "\n\t"
+ "movl %%ecx, 12(%%esi)" "\n"
+ : /*out*/
+ : /*in*/ "S"(&block[0])
+ : /*trash*/"memory","cc","edi","eax","ebx","ecx"
+ );
+ assert(block[3] == 0 || block[3] == 1);
+ return block[3] & 1;
+}
+
+#endif
+
+void atomic_incW ( UWord* w )
+{
+ while (1) {
+ UWord old = *w;
+ UWord nyu = old + 1;
+ UWord ok = do_acasW( w, old, nyu );
+ if (ok) break;
+ };
+}
+
+#if 0
+
+#define NNN 1000000
+
+void* thread_fn ( void* arg )
+{
+ UWord* w = (UWord*)arg;
+ int i;
+ for (i = 0; i < NNN; i++)
+ atomic_incW( w );
+ return NULL;
+}
+
+
+int main ( void )
+{
+ int r;
+ //ANNOTATE_HAPPENS_BEFORE(0);
+ //return 0;
+ UWord w = 0;
+ pthread_t t1, t2;
+
+ r= pthread_create( &t1, NULL, &thread_fn, (void*)&w ); assert(!r);
+ r= pthread_create( &t2, NULL, &thread_fn, (void*)&w ); assert(!r);
+
+ r= pthread_join( t1, NULL ); assert(!r);
+ r= pthread_join( t2, NULL ); assert(!r);
+
+ printf("result = %lu\n", w );
+ return 0;
+}
+
+#endif
+
+int shared_var = 0; // is not raced upon
+
+
+void delay100ms ( void )
+{
+ struct timespec ts = { 0, 100 * 1000 * 1000 };
+ nanosleep(&ts, NULL);
+}
+
+void do_wait ( UWord* w )
+{
+ UWord w0 = *w;
+ UWord volatile * wV = w;
+ while (*wV == w0)
+ ;
+ ANNOTATE_HAPPENS_AFTER(w);
+}
+
+void do_signal ( UWord* w )
+{
+ ANNOTATE_HAPPENS_BEFORE(w);
+ atomic_incW(w);
+}
+
+
+
+void* thread_fn1 ( void* arg )
+{
+ UWord* w = (UWord*)arg;
+ delay100ms(); // ensure t2 gets to its wait first
+ shared_var = 1; // first access
+ do_signal(w); // cause h-b edge to second thread
+
+ delay100ms();
+ return NULL;
+}
+
+void* thread_fn2 ( void* arg )
+{
+ UWord* w = (UWord*)arg;
+ do_wait(w); // wait for h-b edge from first thread
+ shared_var = 2; // second access
+
+ delay100ms();
+ return NULL;
+}
+
+
+
+
+
+
+int main ( void )
+{
+ int r;
+ UWord w = 0;
+ pthread_t t1, t2;
+
+ r= pthread_create( &t1, NULL, &thread_fn1, (void*)&w ); assert(!r);
+ r= pthread_create( &t2, NULL, &thread_fn2, (void*)&w ); assert(!r);
+
+ r= pthread_join( t1, NULL ); assert(!r);
+ r= pthread_join( t2, NULL ); assert(!r);
+ return 0;
+}
--- /dev/null
+vgopts: -q
+prog: annotate_hbefore
--- /dev/null
+
+/* This program is a marginally modified copy of
+ drd/tests/annotate_rwlock.c,
+
+ which was originally written by Bart van Assche.
+
+ Unfortunately due to the need to #include helgrind.h instead of
+ drd.h, it can't be an exact copy.
+*/
+
+/**
+ * @file annotate_rwlock.c
+ *
+ * @brief Multithreaded test program that triggers various access patterns
+ * without triggering any race conditions using a reader-writer lock
+ * implemented via busy-waiting. Annotations are used to tell DRD
+ * which higher-level rwlock operations are being performed.
+ */
+
+
+#define _GNU_SOURCE 1
+
+#include <assert.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h> /* usleep() */
+#include "../../config.h"
+#include "../../helgrind/helgrind.h"
+
+
+#ifndef HAVE_BUILTIN_ATOMIC
+#error Sorry, but this test program can only be compiled by a compiler that\
+has built-in functions for atomic memory access.
+#endif
+
+
+typedef struct {
+ volatile int locked;
+ int writer_count;
+ int reader_count;
+} rwlock_t;
+
+
+static rwlock_t s_rwlock;
+static int s_counter;
+
+
+static void rwlock_init(rwlock_t* p)
+{
+ // DRD_IGNORE_VAR(*p);
+ p->locked = 0;
+ p->writer_count = 0;
+ p->reader_count = 0;
+ ANNOTATE_RWLOCK_CREATE(p);
+}
+
+static void rwlock_destroy(rwlock_t* p)
+{
+ ANNOTATE_RWLOCK_DESTROY(p);
+ assert(p->locked == 0);
+ assert(p->writer_count == 0);
+ assert(p->reader_count == 0);
+}
+
+static void rwlock_rdlock(rwlock_t* p)
+{
+ while (1)
+ {
+ while (__sync_val_compare_and_swap(&p->locked, 0, 1) == 1)
+ ;
+ if (p->writer_count == 0)
+ break;
+#ifdef __APPLE__
+ /* Darwin doesn't have an implementation of pthread_yield(). */
+ usleep(100 * 1000);
+#else
+ pthread_yield();
+#endif
+ (void) __sync_fetch_and_sub(&p->locked, 1);
+ }
+ p->reader_count++;
+ assert(p->reader_count >= 0);
+ assert(p->writer_count >= 0);
+ assert(p->reader_count == 0 || p->writer_count == 0);
+ (void) __sync_fetch_and_sub(&p->locked, 1);
+ //ANNOTATE_READERLOCK_ACQUIRED(p);
+ ANNOTATE_RWLOCK_ACQUIRED(p, 0);
+}
+
+static void rwlock_wrlock(rwlock_t* p)
+{
+ while (1)
+ {
+ while (__sync_val_compare_and_swap(&p->locked, 0, 1) == 1)
+ ;
+ if (p->reader_count == 0)
+ break;
+#ifdef __APPLE__
+ /* Darwin doesn't have an implementation of pthread_yield(). */
+ usleep(100 * 1000);
+#else
+ pthread_yield();
+#endif
+ (void) __sync_fetch_and_sub(&p->locked, 1);
+ }
+ p->writer_count++;
+ assert(p->reader_count >= 0);
+ assert(p->writer_count >= 0);
+ assert(p->reader_count == 0 || p->writer_count == 0);
+ (void) __sync_fetch_and_sub(&p->locked, 1);
+ // ANNOTATE_WRITERLOCK_ACQUIRED(p);
+ ANNOTATE_RWLOCK_ACQUIRED(p, 1);
+}
+
+static void rwlock_unlock(rwlock_t* p)
+{
+ while (__sync_val_compare_and_swap(&p->locked, 0, 1) == 1)
+ ;
+ if (p->reader_count > 0)
+ {
+ p->reader_count--;
+ //ANNOTATE_READERLOCK_RELEASED(p);
+ ANNOTATE_RWLOCK_RELEASED(p, 0);
+ }
+ else
+ {
+ p->writer_count--;
+ //ANNOTATE_WRITERLOCK_RELEASED(p);
+ ANNOTATE_RWLOCK_RELEASED(p, 1);
+ }
+ assert(p->reader_count >= 0);
+ assert(p->writer_count >= 0);
+ assert(p->reader_count == 0 || p->writer_count == 0);
+ (void) __sync_fetch_and_sub(&p->locked, 1);
+}
+
+static void* thread_func(void* arg)
+{
+ int i;
+ int sum = 0;
+
+ for (i = 0; i < 1000; i++)
+ {
+ rwlock_rdlock(&s_rwlock);
+ sum += s_counter;
+ rwlock_unlock(&s_rwlock);
+ rwlock_wrlock(&s_rwlock);
+ s_counter++;
+ rwlock_unlock(&s_rwlock);
+ }
+
+ return 0;
+}
+
+int main(int argc, char** argv)
+{
+ const int thread_count = 10;
+ pthread_t tid[thread_count];
+ int i;
+
+ rwlock_init(&s_rwlock);
+ for (i = 0; i < thread_count; i++)
+ {
+ pthread_create(&tid[i], 0, thread_func, 0);
+ }
+
+ for (i = 0; i < thread_count; i++)
+ {
+ pthread_join(tid[i], 0);
+ }
+ rwlock_destroy(&s_rwlock);
+
+ fprintf(stderr, "Finished.\n");
+
+ return 0;
+}
--- /dev/null
+Thread #x was created
+ ...
+ by 0x........: pthread_create@* (hg_intercepts.c:...)
+ by 0x........: main (annotate_rwlock.c:164)
+
+Thread #x was created
+ ...
+ by 0x........: pthread_create@* (hg_intercepts.c:...)
+ by 0x........: main (annotate_rwlock.c:164)
+
+Possible data race during read of size 4 at 0x........ by thread #x
+ at 0x........: rwlock_rdlock (annotate_rwlock.c:71)
+ by 0x........: thread_func (annotate_rwlock.c:144)
+ by 0x........: mythread_wrapper (hg_intercepts.c:...)
+ ...
+ This conflicts with a previous write of size 4 by thread #x
+ at 0x........: rwlock_wrlock (annotate_rwlock.c:106)
+ by 0x........: thread_func (annotate_rwlock.c:147)
+ by 0x........: mythread_wrapper (hg_intercepts.c:...)
+ ...
+
+Possible data race during read of size 4 at 0x........ by thread #x
+ at 0x........: rwlock_rdlock (annotate_rwlock.c:81)
+ by 0x........: thread_func (annotate_rwlock.c:144)
+ by 0x........: mythread_wrapper (hg_intercepts.c:...)
+ ...
+ This conflicts with a previous write of size 4 by thread #x
+ at 0x........: rwlock_rdlock (annotate_rwlock.c:81)
+ by 0x........: thread_func (annotate_rwlock.c:144)
+ by 0x........: mythread_wrapper (hg_intercepts.c:...)
+ ...
+
+Possible data race during write of size 4 at 0x........ by thread #x
+ at 0x........: rwlock_rdlock (annotate_rwlock.c:81)
+ by 0x........: thread_func (annotate_rwlock.c:144)
+ by 0x........: mythread_wrapper (hg_intercepts.c:...)
+ ...
+ This conflicts with a previous read of size 4 by thread #x
+ at 0x........: rwlock_unlock (annotate_rwlock.c:131)
+ by 0x........: thread_func (annotate_rwlock.c:149)
+ by 0x........: mythread_wrapper (hg_intercepts.c:...)
+ ...
+
+Possible data race during write of size 4 at 0x........ by thread #x
+ at 0x........: rwlock_unlock (annotate_rwlock.c:121)
+ by 0x........: thread_func (annotate_rwlock.c:146)
+ by 0x........: mythread_wrapper (hg_intercepts.c:...)
+ ...
+ This conflicts with a previous read of size 4 by thread #x
+ at 0x........: rwlock_unlock (annotate_rwlock.c:131)
+ by 0x........: thread_func (annotate_rwlock.c:149)
+ by 0x........: mythread_wrapper (hg_intercepts.c:...)
+ ...
+
+Possible data race during write of size 4 at 0x........ by thread #x
+ at 0x........: rwlock_wrlock (annotate_rwlock.c:106)
+ by 0x........: thread_func (annotate_rwlock.c:147)
+ by 0x........: mythread_wrapper (hg_intercepts.c:...)
+ ...
+ This conflicts with a previous read of size 4 by thread #x
+ at 0x........: rwlock_unlock (annotate_rwlock.c:132)
+ by 0x........: thread_func (annotate_rwlock.c:149)
+ by 0x........: mythread_wrapper (hg_intercepts.c:...)
+ ...
+
+Possible data race during write of size 4 at 0x........ by thread #x
+ at 0x........: rwlock_unlock (annotate_rwlock.c:127)
+ by 0x........: thread_func (annotate_rwlock.c:149)
+ by 0x........: mythread_wrapper (hg_intercepts.c:...)
+ ...
+ This conflicts with a previous read of size 4 by thread #x
+ at 0x........: rwlock_unlock (annotate_rwlock.c:132)
+ by 0x........: thread_func (annotate_rwlock.c:149)
+ by 0x........: mythread_wrapper (hg_intercepts.c:...)
+ ...
+
+Finished.
--- /dev/null
+prereq: test -e annotate_rwlock
+vgopts: -q
+prog: annotate_rwlock