Initial tests for "Skeletal support for TSan-compatible annotations"
(r10810).  The rwlock test is kludged and needs de-kludging.



git-svn-id: svn://svn.valgrind.org/valgrind/trunk@10811 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/helgrind/tests/annotate_rwlock.c b/helgrind/tests/annotate_rwlock.c
new file mode 100644
index 0000000..8842b3c
--- /dev/null
+++ b/helgrind/tests/annotate_rwlock.c
@@ -0,0 +1,176 @@
+
+/* 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;
+}