From 4fd9bca85e390cbb98fe232a8405df7db6eafb51 Mon Sep 17 00:00:00 2001
From: Thomas Pfaff <tpfaff@gmx.net>
Date: Tue, 18 Mar 2003 20:04:24 +0000
Subject: [PATCH] * winsup.api/pthread/rwlock1.c: New test. *
 winsup.api/pthread/rwlock2.c: Ditto. * winsup.api/pthread/rwlock3.c: Ditto. *
 winsup.api/pthread/rwlock4.c: Ditto. * winsup.api/pthread/rwlock5.c: Ditto. *
 winsup.api/pthread/rwlock6.c: Ditto. * winsup.api/pthread/rwlock7.c: Ditto.

---
 winsup/testsuite/ChangeLog                    |  10 +
 winsup/testsuite/winsup.api/pthread/rwlock1.c |  63 ++++++
 winsup/testsuite/winsup.api/pthread/rwlock2.c |  68 +++++++
 winsup/testsuite/winsup.api/pthread/rwlock3.c |  78 ++++++++
 winsup/testsuite/winsup.api/pthread/rwlock4.c |  78 ++++++++
 winsup/testsuite/winsup.api/pthread/rwlock5.c |  80 ++++++++
 winsup/testsuite/winsup.api/pthread/rwlock6.c | 103 ++++++++++
 winsup/testsuite/winsup.api/pthread/rwlock7.c | 189 ++++++++++++++++++
 8 files changed, 669 insertions(+)
 create mode 100644 winsup/testsuite/winsup.api/pthread/rwlock1.c
 create mode 100644 winsup/testsuite/winsup.api/pthread/rwlock2.c
 create mode 100644 winsup/testsuite/winsup.api/pthread/rwlock3.c
 create mode 100644 winsup/testsuite/winsup.api/pthread/rwlock4.c
 create mode 100644 winsup/testsuite/winsup.api/pthread/rwlock5.c
 create mode 100644 winsup/testsuite/winsup.api/pthread/rwlock6.c
 create mode 100644 winsup/testsuite/winsup.api/pthread/rwlock7.c

diff --git a/winsup/testsuite/ChangeLog b/winsup/testsuite/ChangeLog
index be94fb2c7..cdec40fb2 100644
--- a/winsup/testsuite/ChangeLog
+++ b/winsup/testsuite/ChangeLog
@@ -1,3 +1,13 @@
+2003-03-18  Thomas Pfaff  <tpfaff@gmx.net>
+
+	* winsup.api/pthread/rwlock1.c: New test.
+	* winsup.api/pthread/rwlock2.c: Ditto.
+	* winsup.api/pthread/rwlock3.c: Ditto.
+	* winsup.api/pthread/rwlock4.c: Ditto.
+	* winsup.api/pthread/rwlock5.c: Ditto.
+	* winsup.api/pthread/rwlock6.c: Ditto.
+	* winsup.api/pthread/rwlock7.c: Ditto.
+
 2003-03-18  Thomas Pfaff  <tpfaff@gmx.net>
 
 	* winsup.api/pthread/condvar7.c: New test.
diff --git a/winsup/testsuite/winsup.api/pthread/rwlock1.c b/winsup/testsuite/winsup.api/pthread/rwlock1.c
new file mode 100644
index 000000000..cebb485a5
--- /dev/null
+++ b/winsup/testsuite/winsup.api/pthread/rwlock1.c
@@ -0,0 +1,63 @@
+/* 
+ * rwlock1.c
+ *
+ *
+ * --------------------------------------------------------------------------
+ *
+ * --------------------------------------------------------------------------
+ *
+ *      Pthreads-win32 - POSIX Threads Library for Win32
+ *      Copyright(C) 1998 John E. Bossom
+ *      Copyright(C) 1999,2002 Pthreads-win32 contributors
+ * 
+ *      Contact Email: rpj@ise.canberra.edu.au
+ * 
+ *      The current list of contributors is contained
+ *      in the file CONTRIBUTORS included with the source
+ *      code distribution. The list can also be seen at the
+ *      following World Wide Web location:
+ *      http://sources.redhat.com/pthreads-win32/contributors.html
+ * 
+ *      This library is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU Lesser General Public
+ *      License as published by the Free Software Foundation; either
+ *      version 2 of the License, or (at your option) any later version.
+ * 
+ *      This library 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
+ *      Lesser General Public License for more details.
+ * 
+ *      You should have received a copy of the GNU Lesser General Public
+ *      License along with this library in the file COPYING.LIB;
+ *      if not, write to the Free Software Foundation, Inc.,
+ *      59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Create a simple rwlock object and then destroy it.
+ *
+ * Depends on API functions:
+ * 	pthread_rwlock_init()
+ *	pthread_rwlock_destroy()
+ */
+
+#include "test.h"
+
+pthread_rwlock_t rwlock = NULL;
+
+int
+main()
+{
+  assert(rwlock == NULL);
+
+  assert(pthread_rwlock_init(&rwlock, NULL) == 0);
+
+  assert(rwlock != NULL);
+
+  assert(pthread_rwlock_destroy(&rwlock) == 0);
+
+  assert(rwlock == NULL);
+
+  return 0;
+}
diff --git a/winsup/testsuite/winsup.api/pthread/rwlock2.c b/winsup/testsuite/winsup.api/pthread/rwlock2.c
new file mode 100644
index 000000000..67b52861e
--- /dev/null
+++ b/winsup/testsuite/winsup.api/pthread/rwlock2.c
@@ -0,0 +1,68 @@
+/* 
+ * rwlock2.c
+ *
+ *
+ * --------------------------------------------------------------------------
+ *
+ * --------------------------------------------------------------------------
+ *
+ *      Pthreads-win32 - POSIX Threads Library for Win32
+ *      Copyright(C) 1998 John E. Bossom
+ *      Copyright(C) 1999,2002 Pthreads-win32 contributors
+ * 
+ *      Contact Email: rpj@ise.canberra.edu.au
+ * 
+ *      The current list of contributors is contained
+ *      in the file CONTRIBUTORS included with the source
+ *      code distribution. The list can also be seen at the
+ *      following World Wide Web location:
+ *      http://sources.redhat.com/pthreads-win32/contributors.html
+ * 
+ *      This library is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU Lesser General Public
+ *      License as published by the Free Software Foundation; either
+ *      version 2 of the License, or (at your option) any later version.
+ * 
+ *      This library 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
+ *      Lesser General Public License for more details.
+ * 
+ *      You should have received a copy of the GNU Lesser General Public
+ *      License along with this library in the file COPYING.LIB;
+ *      if not, write to the Free Software Foundation, Inc.,
+ *      59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Declare a static rwlock object, lock it, 
+ * and then unlock it again.
+ *
+ * Depends on API functions: 
+ *	pthread_rwlock_rdlock()
+ *	pthread_rwlock_unlock()
+ */
+
+#include "test.h"
+ 
+pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
+
+int
+main()
+{
+  assert(rwlock == PTHREAD_RWLOCK_INITIALIZER);
+
+  assert(pthread_rwlock_rdlock(&rwlock) == 0);
+
+  assert(rwlock != PTHREAD_RWLOCK_INITIALIZER);
+
+  assert(rwlock != NULL);
+
+  assert(pthread_rwlock_unlock(&rwlock) == 0);
+
+  assert(pthread_rwlock_destroy(&rwlock) == 0);
+
+  assert(rwlock == NULL);
+
+  return 0;
+}
diff --git a/winsup/testsuite/winsup.api/pthread/rwlock3.c b/winsup/testsuite/winsup.api/pthread/rwlock3.c
new file mode 100644
index 000000000..65aa3797b
--- /dev/null
+++ b/winsup/testsuite/winsup.api/pthread/rwlock3.c
@@ -0,0 +1,78 @@
+/* 
+ * rwlock3.c
+ *
+ *
+ * --------------------------------------------------------------------------
+ *
+ * --------------------------------------------------------------------------
+ *
+ *      Pthreads-win32 - POSIX Threads Library for Win32
+ *      Copyright(C) 1998 John E. Bossom
+ *      Copyright(C) 1999,2002 Pthreads-win32 contributors
+ * 
+ *      Contact Email: rpj@ise.canberra.edu.au
+ * 
+ *      The current list of contributors is contained
+ *      in the file CONTRIBUTORS included with the source
+ *      code distribution. The list can also be seen at the
+ *      following World Wide Web location:
+ *      http://sources.redhat.com/pthreads-win32/contributors.html
+ * 
+ *      This library is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU Lesser General Public
+ *      License as published by the Free Software Foundation; either
+ *      version 2 of the License, or (at your option) any later version.
+ * 
+ *      This library 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
+ *      Lesser General Public License for more details.
+ * 
+ *      You should have received a copy of the GNU Lesser General Public
+ *      License along with this library in the file COPYING.LIB;
+ *      if not, write to the Free Software Foundation, Inc.,
+ *      59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Declare a static rwlock object, wrlock it, trywrlock it, 
+ * and then unlock it again.
+ *
+ * Depends on API functions: 
+ *	pthread_rwlock_wrlock()
+ *	pthread_rwlock_trywrlock()
+ *	pthread_rwlock_unlock()
+ */
+
+#include "test.h"
+ 
+pthread_rwlock_t rwlock1 = PTHREAD_RWLOCK_INITIALIZER;
+
+static int washere = 0;
+
+void * func(void * arg)
+{
+  assert(pthread_rwlock_trywrlock(&rwlock1) == EBUSY);
+
+  washere = 1;
+
+  return 0; 
+}
+ 
+int
+main()
+{
+  pthread_t t;
+
+  assert(pthread_rwlock_wrlock(&rwlock1) == 0);
+
+  assert(pthread_create(&t, NULL, func, NULL) == 0);
+
+  Sleep(2000);
+
+  assert(pthread_rwlock_unlock(&rwlock1) == 0);
+
+  assert(washere == 1);
+
+  return 0;
+}
diff --git a/winsup/testsuite/winsup.api/pthread/rwlock4.c b/winsup/testsuite/winsup.api/pthread/rwlock4.c
new file mode 100644
index 000000000..6605bf6f2
--- /dev/null
+++ b/winsup/testsuite/winsup.api/pthread/rwlock4.c
@@ -0,0 +1,78 @@
+/* 
+ * rwlock4.c
+ *
+ *
+ * --------------------------------------------------------------------------
+ *
+ * --------------------------------------------------------------------------
+ *
+ *      Pthreads-win32 - POSIX Threads Library for Win32
+ *      Copyright(C) 1998 John E. Bossom
+ *      Copyright(C) 1999,2002 Pthreads-win32 contributors
+ * 
+ *      Contact Email: rpj@ise.canberra.edu.au
+ * 
+ *      The current list of contributors is contained
+ *      in the file CONTRIBUTORS included with the source
+ *      code distribution. The list can also be seen at the
+ *      following World Wide Web location:
+ *      http://sources.redhat.com/pthreads-win32/contributors.html
+ * 
+ *      This library is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU Lesser General Public
+ *      License as published by the Free Software Foundation; either
+ *      version 2 of the License, or (at your option) any later version.
+ * 
+ *      This library 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
+ *      Lesser General Public License for more details.
+ * 
+ *      You should have received a copy of the GNU Lesser General Public
+ *      License along with this library in the file COPYING.LIB;
+ *      if not, write to the Free Software Foundation, Inc.,
+ *      59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Declare a static rwlock object, rdlock it, trywrlock it, 
+ * and then unlock it again.
+ *
+ * Depends on API functions: 
+ *	pthread_rwlock_rdlock()
+ *	pthread_rwlock_trywrlock()
+ *	pthread_rwlock_unlock()
+ */
+
+#include "test.h"
+ 
+pthread_rwlock_t rwlock1 = PTHREAD_RWLOCK_INITIALIZER;
+
+static int washere = 0;
+
+void * func(void * arg)
+{
+  assert(pthread_rwlock_trywrlock(&rwlock1) == EBUSY);
+
+  washere = 1;
+
+  return 0; 
+}
+ 
+int
+main()
+{
+  pthread_t t;
+
+  assert(pthread_rwlock_rdlock(&rwlock1) == 0);
+
+  assert(pthread_create(&t, NULL, func, NULL) == 0);
+
+  Sleep(2000);
+
+  assert(pthread_rwlock_unlock(&rwlock1) == 0);
+
+  assert(washere == 1);
+
+  return 0;
+}
diff --git a/winsup/testsuite/winsup.api/pthread/rwlock5.c b/winsup/testsuite/winsup.api/pthread/rwlock5.c
new file mode 100644
index 000000000..43e2c6af2
--- /dev/null
+++ b/winsup/testsuite/winsup.api/pthread/rwlock5.c
@@ -0,0 +1,80 @@
+/* 
+ * rwlock5.c
+ *
+ *
+ * --------------------------------------------------------------------------
+ *
+ * --------------------------------------------------------------------------
+ *
+ *      Pthreads-win32 - POSIX Threads Library for Win32
+ *      Copyright(C) 1998 John E. Bossom
+ *      Copyright(C) 1999,2002 Pthreads-win32 contributors
+ * 
+ *      Contact Email: rpj@ise.canberra.edu.au
+ * 
+ *      The current list of contributors is contained
+ *      in the file CONTRIBUTORS included with the source
+ *      code distribution. The list can also be seen at the
+ *      following World Wide Web location:
+ *      http://sources.redhat.com/pthreads-win32/contributors.html
+ * 
+ *      This library is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU Lesser General Public
+ *      License as published by the Free Software Foundation; either
+ *      version 2 of the License, or (at your option) any later version.
+ * 
+ *      This library 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
+ *      Lesser General Public License for more details.
+ * 
+ *      You should have received a copy of the GNU Lesser General Public
+ *      License along with this library in the file COPYING.LIB;
+ *      if not, write to the Free Software Foundation, Inc.,
+ *      59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Declare a static rwlock object, rdlock it, tryrdlock it, 
+ * and then unlock it again.
+ *
+ * Depends on API functions: 
+ *	pthread_rwlock_rdlock()
+ *	pthread_rwlock_tryrdlock()
+ *	pthread_rwlock_unlock()
+ */
+
+#include "test.h"
+ 
+pthread_rwlock_t rwlock1 = PTHREAD_RWLOCK_INITIALIZER;
+
+static int washere = 0;
+
+void * func(void * arg)
+{
+  assert(pthread_rwlock_tryrdlock(&rwlock1) == 0);
+
+  assert(pthread_rwlock_unlock(&rwlock1) == 0);
+
+  washere = 1;
+
+  return 0; 
+}
+ 
+int
+main()
+{
+  pthread_t t;
+
+  assert(pthread_rwlock_rdlock(&rwlock1) == 0);
+
+  assert(pthread_create(&t, NULL, func, NULL) == 0);
+
+  Sleep(2000);
+
+  assert(pthread_rwlock_unlock(&rwlock1) == 0);
+
+  assert(washere == 1);
+
+  return 0;
+}
diff --git a/winsup/testsuite/winsup.api/pthread/rwlock6.c b/winsup/testsuite/winsup.api/pthread/rwlock6.c
new file mode 100644
index 000000000..9a4832e81
--- /dev/null
+++ b/winsup/testsuite/winsup.api/pthread/rwlock6.c
@@ -0,0 +1,103 @@
+/*
+ * rwlock6.c
+ *
+ *
+ * --------------------------------------------------------------------------
+ *
+ * --------------------------------------------------------------------------
+ *
+ *      Pthreads-win32 - POSIX Threads Library for Win32
+ *      Copyright(C) 1998 John E. Bossom
+ *      Copyright(C) 1999,2002 Pthreads-win32 contributors
+ * 
+ *      Contact Email: rpj@ise.canberra.edu.au
+ * 
+ *      The current list of contributors is contained
+ *      in the file CONTRIBUTORS included with the source
+ *      code distribution. The list can also be seen at the
+ *      following World Wide Web location:
+ *      http://sources.redhat.com/pthreads-win32/contributors.html
+ * 
+ *      This library is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU Lesser General Public
+ *      License as published by the Free Software Foundation; either
+ *      version 2 of the License, or (at your option) any later version.
+ * 
+ *      This library 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
+ *      Lesser General Public License for more details.
+ * 
+ *      You should have received a copy of the GNU Lesser General Public
+ *      License along with this library in the file COPYING.LIB;
+ *      if not, write to the Free Software Foundation, Inc.,
+ *      59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Check writer and reader locking
+ *
+ * Depends on API functions:
+ *      pthread_rwlock_rdlock()
+ *      pthread_rwlock_wrlock()
+ *      pthread_rwlock_unlock()
+ */
+
+#include "test.h"
+
+static pthread_rwlock_t rwlock1 = PTHREAD_RWLOCK_INITIALIZER;
+
+static int bankAccount = 0;
+
+void * wrfunc(void * arg)
+{
+  int ba;
+
+  assert(pthread_rwlock_wrlock(&rwlock1) == 0);
+  Sleep(2000);
+  bankAccount += 10;
+  ba = bankAccount;
+  assert(pthread_rwlock_unlock(&rwlock1) == 0);
+
+  return ((void *) ba);
+}
+
+void * rdfunc(void * arg)
+{
+  int ba;
+
+  assert(pthread_rwlock_rdlock(&rwlock1) == 0);
+  ba = bankAccount;
+  assert(pthread_rwlock_unlock(&rwlock1) == 0);
+
+  return ((void *) ba);
+}
+
+int
+main()
+{
+  pthread_t wrt1;
+  pthread_t wrt2;
+  pthread_t rdt;
+  int wr1Result = 0;
+  int wr2Result = 0;
+  int rdResult = 0;
+
+  bankAccount = 0;
+
+  assert(pthread_create(&wrt1, NULL, wrfunc, NULL) == 0);
+  Sleep(500);
+  assert(pthread_create(&rdt, NULL, rdfunc, NULL) == 0);
+  Sleep(500);
+  assert(pthread_create(&wrt2, NULL, wrfunc, NULL) == 0);
+
+  assert(pthread_join(wrt1, (void **) &wr1Result) == 0);
+  assert(pthread_join(rdt, (void **) &rdResult) == 0);
+  assert(pthread_join(wrt2, (void **) &wr2Result) == 0);
+
+  assert(wr1Result == 10);
+  assert(rdResult == 20);
+  assert(wr2Result == 20);
+
+  return 0;
+}
diff --git a/winsup/testsuite/winsup.api/pthread/rwlock7.c b/winsup/testsuite/winsup.api/pthread/rwlock7.c
new file mode 100644
index 000000000..d8af507b5
--- /dev/null
+++ b/winsup/testsuite/winsup.api/pthread/rwlock7.c
@@ -0,0 +1,189 @@
+/*
+ * rwlock7.c
+ *
+ * Hammer on a bunch of rwlocks to test robustness and fairness.
+ * Printed stats should be roughly even for each thread.
+ */
+
+#include "test.h"
+#include <sys/time.h>
+#include <sys/timeb.h>
+
+#ifdef __GNUC__
+#include <stdlib.h>
+#endif
+
+#define THREADS         5
+#define DATASIZE        15
+#define ITERATIONS      1000000
+
+#define rand_r( _seed ) \
+	( _seed == _seed? rand() : rand() )
+
+/*
+ * Keep statistics for each thread.
+ */
+typedef struct thread_tag {
+  int         thread_num;
+  pthread_t   thread_id;
+  int         updates;
+  int         reads;
+  int         interval;
+} thread_t;
+
+/*
+ * Read-write lock and shared data
+ */
+typedef struct data_tag {
+  pthread_rwlock_t    lock;
+  int                 data;
+  int                 updates;
+} data_t;
+
+static thread_t threads[THREADS];
+static data_t data[DATASIZE];
+
+/*
+ * Thread start routine that uses read-write locks
+ */
+void *thread_routine (void *arg)
+{
+  thread_t *self = (thread_t*)arg;
+  int repeats = 0;
+  int iteration;
+  int element = 0;
+
+  for (iteration = 0; iteration < ITERATIONS; iteration++)
+    {
+      if (iteration % (ITERATIONS / 10) == 0)
+        {
+          putchar('.');
+          fflush(stdout);
+        }
+      /*
+       * Each "self->interval" iterations, perform an
+       * update operation (write lock instead of read
+       * lock).
+       */
+      if ((iteration % self->interval) == 0)
+        {
+          assert(pthread_rwlock_wrlock (&data[element].lock) == 0);
+          data[element].data = self->thread_num;
+          data[element].updates++;
+          self->updates++;
+          assert(pthread_rwlock_unlock (&data[element].lock) == 0);
+        } else {
+          /*
+           * Look at the current data element to see whether
+           * the current thread last updated it. Count the
+           * times, to report later.
+           */
+          assert(pthread_rwlock_rdlock (&data[element].lock) == 0);
+
+          self->reads++;
+
+          if (data[element].data == self->thread_num)
+            {
+              repeats++;
+            }
+
+          assert(pthread_rwlock_unlock (&data[element].lock) == 0);
+        }
+
+      element++;
+
+      if (element >= DATASIZE)
+        {
+          element = 0;
+        }
+    }
+
+  if (repeats > 0)
+    {
+      printf ("\nThread %d found unchanged elements %d times",
+              self->thread_num, repeats);
+      fflush(stdout);
+    }
+
+  return NULL;
+}
+
+int
+main (int argc, char *argv[])
+{
+  int count;
+  int data_count;
+  int thread_updates = 0;
+  int data_updates = 0;
+  int seed = 1;
+
+  struct timeb currSysTime1;
+  struct timeb currSysTime2;
+
+  /*
+   * Initialize the shared data.
+   */
+  for (data_count = 0; data_count < DATASIZE; data_count++)
+    {
+      data[data_count].data = 0;
+      data[data_count].updates = 0;
+
+      assert(pthread_rwlock_init (&data[data_count].lock, NULL) == 0);
+    }
+
+  _ftime(&currSysTime1);
+
+  /*
+   * Create THREADS threads to access shared data.
+   */
+  for (count = 0; count < THREADS; count++)
+    {
+      threads[count].thread_num = count;
+      threads[count].updates = 0;
+      threads[count].reads = 0;
+      threads[count].interval = rand_r (&seed) % 71;
+
+      assert(pthread_create (&threads[count].thread_id,
+                             NULL, thread_routine, (void*)&threads[count]) == 0);
+    }
+
+  /*
+   * Wait for all threads to complete, and collect
+   * statistics.
+   */
+  for (count = 0; count < THREADS; count++)
+    {
+      assert(pthread_join (threads[count].thread_id, NULL) == 0);
+      thread_updates += threads[count].updates;
+      printf ("%02d: interval %d, updates %d, reads %d\n",
+              count, threads[count].interval,
+              threads[count].updates, threads[count].reads);
+    }
+
+  putchar('\n');
+  fflush(stdout);
+
+  /*
+   * Collect statistics for the data.
+   */
+  for (data_count = 0; data_count < DATASIZE; data_count++)
+    {
+      data_updates += data[data_count].updates;
+      printf ("data %02d: value %d, %d updates\n",
+              data_count, data[data_count].data, data[data_count].updates);
+      assert(pthread_rwlock_destroy (&data[data_count].lock) == 0);
+    }
+
+  printf ("%d thread updates, %d data updates\n",
+          thread_updates, data_updates);
+
+  _ftime(&currSysTime2);
+
+  printf( "\nstart: %ld/%d, stop: %ld/%d, duration:%ld\n",
+          currSysTime1.time,currSysTime1.millitm,
+          currSysTime2.time,currSysTime2.millitm,
+          (currSysTime2.time*1000+currSysTime2.millitm) -
+          (currSysTime1.time*1000+currSysTime1.millitm));
+
+  return 0;
+}