]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/lib/rwlock.c
Switch from GPLv2 to AGPLv3
[bacula/bacula] / bacula / src / lib / rwlock.c
index f6912b519de5eac9632687353607cabc47dd1706..b1ce1e2e9827e0a2f2853d33382e1a5bca8c7d62 100644 (file)
@@ -1,3 +1,30 @@
+/*
+   Bacula® - The Network Backup Solution
+
+   Copyright (C) 2001-2010 Free Software Foundation Europe e.V.
+
+   The main author of Bacula is Kern Sibbald, with contributions from
+   many others, a complete list can be found in the file AUTHORS.
+   This program is Free Software; you can redistribute it and/or
+   modify it under the terms of version three of the GNU Affero General Public
+   License as published by the Free Software Foundation and included
+   in the file LICENSE.
+
+   This program 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 for more details.
+
+   You should have received a copy of the GNU Affero General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA.
+
+   Bacula® is a registered trademark of Kern Sibbald.
+   The licensor of Bacula is the Free Software Foundation Europe
+   (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
+   Switzerland, email:ftf@fsfeurope.org.
+*/
 /*
  * Bacula Thread Read/Write locking code. It permits
  *  multiple readers but only one writer.  Note, however,
  *    David R. Butenhof
  *
  */
-/*
-   Copyright (C) 2000-2003 Kern Sibbald and John Walker
-
-   This program is free software; you can redistribute it and/or
-   modify it under the terms of the GNU General Public License as
-   published by the Free Software Foundation; either version 2 of
-   the License, or (at your option) any later version.
-
-   This program 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 for more details.
-
-   You should have received a copy of the GNU General Public
-   License along with this program; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
-   MA 02111-1307, USA.
-
- */
 
+#define _LOCKMGR_COMPLIANT
 #include "bacula.h"
 
-/*   
+/*
  * Initialize a read/write lock
  *
  *  Returns: 0 on success
- *          errno on failure
+ *           errno on failure
  */
-int rwl_init(brwlock_t *rwl)
+int rwl_init(brwlock_t *rwl, int priority)
 {
    int stat;
-                       
+
    rwl->r_active = rwl->w_active = 0;
    rwl->r_wait = rwl->w_wait = 0;
+   rwl->priority = priority;
    if ((stat = pthread_mutex_init(&rwl->mutex, NULL)) != 0) {
       return stat;
    }
@@ -66,7 +76,7 @@ int rwl_init(brwlock_t *rwl)
  * Destroy a read/write lock
  *
  * Returns: 0 on success
- *         errno on failure
+ *          errno on failure
  */
 int rwl_destroy(brwlock_t *rwl)
 {
@@ -79,7 +89,7 @@ int rwl_destroy(brwlock_t *rwl)
      return stat;
   }
 
-  /* 
+  /*
    * If any threads are active, report EBUSY
    */
   if (rwl->r_active > 0 || rwl->w_active) {
@@ -90,7 +100,7 @@ int rwl_destroy(brwlock_t *rwl)
   /*
    * If any threads are waiting, report EBUSY
    */
-  if (rwl->r_wait > 0 || rwl->w_wait > 0) { 
+  if (rwl->r_wait > 0 || rwl->w_wait > 0) {
      pthread_mutex_unlock(&rwl->mutex);
      return EBUSY;
   }
@@ -99,7 +109,7 @@ int rwl_destroy(brwlock_t *rwl)
   if ((stat = pthread_mutex_unlock(&rwl->mutex)) != 0) {
      return stat;
   }
-  stat = pthread_mutex_destroy(&rwl->mutex);
+  stat  = pthread_mutex_destroy(&rwl->mutex);
   stat1 = pthread_cond_destroy(&rwl->read);
   stat2 = pthread_cond_destroy(&rwl->write);
   return (stat != 0 ? stat : (stat1 != 0 ? stat1 : stat2));
@@ -135,7 +145,7 @@ static void rwl_write_release(void *arg)
 int rwl_readlock(brwlock_t *rwl)
 {
    int stat;
-    
+
    if (rwl->valid != RWLOCK_VALID) {
       return EINVAL;
    }
@@ -143,31 +153,31 @@ int rwl_readlock(brwlock_t *rwl)
       return stat;
    }
    if (rwl->w_active) {
-      rwl->r_wait++;                 /* indicate that we are waiting */
+      rwl->r_wait++;                  /* indicate that we are waiting */
       pthread_cleanup_push(rwl_read_release, (void *)rwl);
       while (rwl->w_active) {
-        stat = pthread_cond_wait(&rwl->read, &rwl->mutex);
-        if (stat != 0) {
-           break;                    /* error, bail out */
-        }
+         stat = pthread_cond_wait(&rwl->read, &rwl->mutex);
+         if (stat != 0) {
+            break;                    /* error, bail out */
+         }
       }
       pthread_cleanup_pop(0);
-      rwl->r_wait--;                 /* we are no longer waiting */
+      rwl->r_wait--;                  /* we are no longer waiting */
    }
    if (stat == 0) {
-      rwl->r_active++;               /* we are running */
+      rwl->r_active++;                /* we are running */
    }
    pthread_mutex_unlock(&rwl->mutex);
    return stat;
 }
 
-/* 
+/*
  * Attempt to lock for read access, don't wait
  */
 int rwl_readtrylock(brwlock_t *rwl)
 {
    int stat, stat2;
-    
+
    if (rwl->valid != RWLOCK_VALID) {
       return EINVAL;
    }
@@ -177,19 +187,19 @@ int rwl_readtrylock(brwlock_t *rwl)
    if (rwl->w_active) {
       stat = EBUSY;
    } else {
-      rwl->r_active++;               /* we are running */
+      rwl->r_active++;                /* we are running */
    }
    stat2 = pthread_mutex_unlock(&rwl->mutex);
    return (stat == 0 ? stat2 : stat);
 }
-   
-/* 
+
+/*
  * Unlock read lock
  */
 int rwl_readunlock(brwlock_t *rwl)
 {
    int stat, stat2;
-    
+
    if (rwl->valid != RWLOCK_VALID) {
       return EINVAL;
    }
@@ -198,7 +208,7 @@ int rwl_readunlock(brwlock_t *rwl)
    }
    rwl->r_active--;
    if (rwl->r_active == 0 && rwl->w_wait > 0) { /* if writers waiting */
-      stat = pthread_cond_signal(&rwl->write);
+      stat = pthread_cond_broadcast(&rwl->write);
    }
    stat2 = pthread_mutex_unlock(&rwl->mutex);
    return (stat == 0 ? stat2 : stat);
@@ -209,10 +219,10 @@ int rwl_readunlock(brwlock_t *rwl)
  * Lock for write access, wait until locked (or error).
  *   Multiple nested write locking is permitted.
  */
-int rwl_writelock(brwlock_t *rwl)
+int rwl_writelock_p(brwlock_t *rwl, const char *file, int line)
 {
    int stat;
-    
+
    if (rwl->valid != RWLOCK_VALID) {
       return EINVAL;
    }
@@ -224,32 +234,35 @@ int rwl_writelock(brwlock_t *rwl)
       pthread_mutex_unlock(&rwl->mutex);
       return 0;
    }
+   lmgr_pre_lock(rwl, rwl->priority, file, line);
    if (rwl->w_active || rwl->r_active > 0) {
-      rwl->w_wait++;                 /* indicate that we are waiting */
+      rwl->w_wait++;                  /* indicate that we are waiting */
       pthread_cleanup_push(rwl_write_release, (void *)rwl);
       while (rwl->w_active || rwl->r_active > 0) {
-        if ((stat = pthread_cond_wait(&rwl->write, &rwl->mutex)) != 0) {
-           break;                    /* error, bail out */
-        }
+         if ((stat = pthread_cond_wait(&rwl->write, &rwl->mutex)) != 0) {
+            lmgr_do_unlock(rwl);
+            break;                    /* error, bail out */
+         }
       }
       pthread_cleanup_pop(0);
-      rwl->w_wait--;                 /* we are no longer waiting */
+      rwl->w_wait--;                  /* we are no longer waiting */
    }
    if (stat == 0) {
-      rwl->w_active++;               /* we are running */
+      rwl->w_active++;                /* we are running */
       rwl->writer_id = pthread_self(); /* save writer thread's id */
-   }
+      lmgr_post_lock();
+   } 
    pthread_mutex_unlock(&rwl->mutex);
    return stat;
 }
 
-/* 
+/*
  * Attempt to lock for write access, don't wait
  */
 int rwl_writetrylock(brwlock_t *rwl)
 {
    int stat, stat2;
-    
+
    if (rwl->valid != RWLOCK_VALID) {
       return EINVAL;
    }
@@ -264,21 +277,22 @@ int rwl_writetrylock(brwlock_t *rwl)
    if (rwl->w_active || rwl->r_active > 0) {
       stat = EBUSY;
    } else {
-      rwl->w_active = 1;             /* we are running */
+      rwl->w_active = 1;              /* we are running */
       rwl->writer_id = pthread_self(); /* save writer thread's id */
+      lmgr_do_lock(rwl, rwl->priority, __FILE__, __LINE__);
    }
    stat2 = pthread_mutex_unlock(&rwl->mutex);
    return (stat == 0 ? stat2 : stat);
 }
-   
-/* 
+
+/*
  * Unlock write lock
  *  Start any waiting writers in preference to waiting readers
  */
 int rwl_writeunlock(brwlock_t *rwl)
 {
    int stat, stat2;
-    
+
    if (rwl->valid != RWLOCK_VALID) {
       return EINVAL;
    }
@@ -286,20 +300,23 @@ int rwl_writeunlock(brwlock_t *rwl)
       return stat;
    }
    if (rwl->w_active <= 0) {
-      Emsg0(M_ABORT, 0, "rwl_writeunlock called too many times.\n");
+      pthread_mutex_unlock(&rwl->mutex);
+      Jmsg0(NULL, M_ABORT, 0, _("rwl_writeunlock called too many times.\n"));
    }
    rwl->w_active--;
    if (!pthread_equal(pthread_self(), rwl->writer_id)) {
-      Emsg0(M_ABORT, 0, "rwl_writeunlock by non-owner.\n");
+      pthread_mutex_unlock(&rwl->mutex);
+      Jmsg0(NULL, M_ABORT, 0, _("rwl_writeunlock by non-owner.\n"));
    }
    if (rwl->w_active > 0) {
-      stat = 0;                      /* writers still active */
+      stat = 0;                       /* writers still active */
    } else {
+      lmgr_do_unlock(rwl);
       /* No more writers, awaken someone */
-      if (rwl->r_wait > 0) {        /* if readers waiting */
-        stat = pthread_cond_broadcast(&rwl->read);
+      if (rwl->r_wait > 0) {         /* if readers waiting */
+         stat = pthread_cond_broadcast(&rwl->read);
       } else if (rwl->w_wait > 0) {
-        stat = pthread_cond_signal(&rwl->write);
+         stat = pthread_cond_broadcast(&rwl->write);
       }
    }
    stat2 = pthread_mutex_unlock(&rwl->mutex);
@@ -308,9 +325,9 @@ int rwl_writeunlock(brwlock_t *rwl)
 
 #ifdef TEST_RWLOCK
 
-#define THREADS     5
+#define THREADS     300
 #define DATASIZE   15
-#define ITERATIONS 10000
+#define ITERATIONS 1000000
 
 /*
  * Keep statics for each thread.
@@ -323,7 +340,7 @@ typedef struct thread_tag {
    int interval;
 } thread_t;
 
-/* 
+/*
  * Read/write lock and shared data.
  */
 typedef struct data_tag {
@@ -332,10 +349,10 @@ typedef struct data_tag {
    int writes;
 } data_t;
 
-thread_t threads[THREADS];
-data_t data[DATASIZE];
+static thread_t threads[THREADS];
+static data_t data[DATASIZE];
 
-/* 
+/*
  * Thread start routine that uses read/write locks.
  */
 void *thread_routine(void *arg)
@@ -352,44 +369,70 @@ void *thread_routine(void *arg)
        * update operation (write lock instead of read
        * lock).
        */
-      if ((iteration % self->interval) == 0) {
-        status = rwl_writelock(&data[element].lock);
-        if (status != 0) {
-            Emsg1(M_ABORT, 0, "Write lock failed. ERR=%s\n", strerror(status));
-        }
-        data[element].data = self->thread_num;
-        data[element].writes++;
-        self->writes++;
-        status = rwl_writeunlock(&data[element].lock);
-        if (status != 0) {
-            Emsg1(M_ABORT, 0, "Write unlock failed. ERR=%s\n", strerror(status));
-        }
+//      if ((iteration % self->interval) == 0) {
+         status = rwl_writelock(&data[element].lock);
+         if (status != 0) {
+            berrno be;
+            printf("Write lock failed. ERR=%s\n", be.bstrerror(status));
+            exit(1);
+         }
+         data[element].data = self->thread_num;
+         data[element].writes++;
+         self->writes++;
+         status = rwl_writelock(&data[element].lock);
+         if (status != 0) {
+            berrno be;
+            printf("Write lock failed. ERR=%s\n", be.bstrerror(status));
+            exit(1);
+         }
+         data[element].data = self->thread_num;
+         data[element].writes++;
+         self->writes++;
+         status = rwl_writeunlock(&data[element].lock);
+         if (status != 0) {
+            berrno be;
+            printf("Write unlock failed. ERR=%s\n", be.bstrerror(status));
+            exit(1);
+         }
+         status = rwl_writeunlock(&data[element].lock);
+         if (status != 0) {
+            berrno be;
+            printf("Write unlock failed. ERR=%s\n", be.bstrerror(status));
+            exit(1);
+         }
+
+#ifdef xxx
       } else {
-        /*
-         * Look at the current data element to see whether
-         * the current thread last updated it. Count the
-         * times to report later.
-         */
-         status = rwl_readlock(&data[element].lock);
-         if (status != 0) {
-             Emsg1(M_ABORT, 0, "Read lock failed. ERR=%s\n", strerror(status));
-         }
-         self->reads++;
-         if (data[element].data == self->thread_num)
-            repeats++;
-         status = rwl_readunlock(&data[element].lock);
-         if (status != 0) {
-             Emsg1(M_ABORT, 0, "Read unlock failed. ERR=%s\n", strerror(status));
-         }
+         /*
+          * Look at the current data element to see whether
+          * the current thread last updated it. Count the
+          * times to report later.
+          */
+          status = rwl_readlock(&data[element].lock);
+          if (status != 0) {
+             berrno be;
+             printf("Read lock failed. ERR=%s\n", be.bstrerror(status));
+             exit(1);
+          }
+          self->reads++;
+          if (data[element].data == self->thread_num)
+             repeats++;
+          status = rwl_readunlock(&data[element].lock);
+          if (status != 0) {
+             berrno be;
+             printf("Read unlock failed. ERR=%s\n", be.bstrerror(status));
+             exit(1);
+          }
       }
+#endif
       element++;
       if (element >= DATASIZE) {
-        element = 0;
+         element = 0;
       }
    }
    if (repeats > 0) {
-      Pmsg2(000, "Thread %d found unchanged elements %d times\n",
-        self->thread_num, repeats);
+      Pmsg2(000, _("Thread %d found unchanged elements %d times\n"),
+         self->thread_num, repeats);
    }
    return NULL;
 }
@@ -416,27 +459,34 @@ int main (int argc, char *argv[])
      * Initialize the shared data.
      */
     for (data_count = 0; data_count < DATASIZE; data_count++) {
-       data[data_count].data = 0;
-       data[data_count].writes = 0;
-       status = rwl_init (&data[data_count].lock);
-       if (status != 0) {
-           Emsg1(M_ABORT, 0, "Init rwlock failed. ERR=%s\n", strerror(status));
-       }
+        data[data_count].data = 0;
+        data[data_count].writes = 0;
+        status = rwl_init(&data[data_count].lock);
+        if (status != 0) {
+           berrno be;
+           printf("Init rwlock failed. ERR=%s\n", be.bstrerror(status));
+           exit(1);
+        }
     }
 
     /*
      * Create THREADS threads to access shared data.
      */
     for (count = 0; count < THREADS; count++) {
-       threads[count].thread_num = count + 1;
-       threads[count].writes = 0;
-       threads[count].reads = 0;
-       threads[count].interval = rand_r (&seed) % 71;
-       status = pthread_create (&threads[count].thread_id,
-           NULL, thread_routine, (void*)&threads[count]);
-       if (status != 0) {
-           Emsg1(M_ABORT, 0, "Create thread failed. ERR=%s\n", strerror(status));
-       }
+        threads[count].thread_num = count + 1;
+        threads[count].writes = 0;
+        threads[count].reads = 0;
+        threads[count].interval = rand_r(&seed) % 71;
+        if (threads[count].interval <= 0) {
+           threads[count].interval = 1;
+        }
+        status = pthread_create (&threads[count].thread_id,
+            NULL, thread_routine, (void*)&threads[count]);
+        if (status != 0 || (int)threads[count].thread_id == 0) {
+           berrno be;
+           printf("Create thread failed. ERR=%s\n", be.bstrerror(status));
+           exit(1);
+        }
     }
 
     /*
@@ -444,28 +494,30 @@ int main (int argc, char *argv[])
      * statistics.
      */
     for (count = 0; count < THREADS; count++) {
-       status = pthread_join (threads[count].thread_id, NULL);
-       if (status != 0) {
-           Emsg1(M_ABORT, 0, "Join thread failed. ERR=%s\n", strerror(status));
-       }
-       thread_writes += threads[count].writes;
-        printf ("%02d: interval %d, writes %d, reads %d\n",
-           count, threads[count].interval,
-           threads[count].writes, threads[count].reads);
+        status = pthread_join (threads[count].thread_id, NULL);
+        if (status != 0) {
+           berrno be;
+           printf("Join thread failed. ERR=%s\n", be.bstrerror(status));
+           exit(1);
+        }
+        thread_writes += threads[count].writes;
+        printf (_("%02d: interval %d, writes %d, reads %d\n"),
+            count, threads[count].interval,
+            threads[count].writes, threads[count].reads);
     }
 
     /*
      * Collect statistics for the data.
      */
     for (data_count = 0; data_count < DATASIZE; data_count++) {
-       data_writes += data[data_count].writes;
-        printf ("data %02d: value %d, %d writes\n",
-           data_count, data[data_count].data, data[data_count].writes);
-       rwl_destroy (&data[data_count].lock);
+        data_writes += data[data_count].writes;
+        printf (_("data %02d: value %d, %d writes\n"),
+            data_count, data[data_count].data, data[data_count].writes);
+        rwl_destroy (&data[data_count].lock);
     }
 
-    printf ("Total: %d thread writes, %d data writes\n",
-       thread_writes, data_writes);
+    printf (_("Total: %d thread writes, %d data writes\n"),
+        thread_writes, data_writes);
     return 0;
 }
 
@@ -485,29 +537,29 @@ int main (int argc, char *argv[])
 #include "rwlock.h"
 #include "errors.h"
 
-#define THREADS        5
-#define ITERATIONS     1000
-#define DATASIZE       15
+#define THREADS         5
+#define ITERATIONS      1000
+#define DATASIZE        15
 
 /*
  * Keep statistics for each thread.
  */
 typedef struct thread_tag {
-    int        thread_num;
-    pthread_t  thread_id;
-    int        r_collisions;
-    int        w_collisions;
-    int        updates;
-    int        interval;
+    int         thread_num;
+    pthread_t   thread_id;
+    int         r_collisions;
+    int         w_collisions;
+    int         updates;
+    int         interval;
 } thread_t;
 
 /*
  * Read-write lock and shared data
  */
 typedef struct data_tag {
-    brwlock_t   lock;
-    int        data;
-    int        updates;
+    brwlock_t    lock;
+    int         data;
+    int         updates;
 } data_t;
 
 thread_t threads[THREADS];
@@ -522,40 +574,41 @@ void *thread_routine (void *arg)
     int iteration;
     int element;
     int status;
-
-    element = 0;                       /* Current data element */
+    lmgr_init_thread();
+    element = 0;                        /* Current data element */
 
     for (iteration = 0; iteration < ITERATIONS; iteration++) {
-       if ((iteration % self->interval) == 0) {
-           status = rwl_writetrylock (&data[element].lock);
-           if (status == EBUSY)
-               self->w_collisions++;
-           else if (status == 0) {
-               data[element].data++;
-               data[element].updates++;
-               self->updates++;
-               rwl_writeunlock (&data[element].lock);
-           } else
-                err_abort (status, "Try write lock");
-       } else {
-           status = rwl_readtrylock (&data[element].lock);
-           if (status == EBUSY)
-               self->r_collisions++;
-           else if (status != 0) {
-                err_abort (status, "Try read lock");
-           } else {
-               if (data[element].data != data[element].updates)
+        if ((iteration % self->interval) == 0) {
+            status = rwl_writetrylock (&data[element].lock);
+            if (status == EBUSY)
+                self->w_collisions++;
+            else if (status == 0) {
+                data[element].data++;
+                data[element].updates++;
+                self->updates++;
+                rwl_writeunlock (&data[element].lock);
+            } else
+                err_abort (status, _("Try write lock"));
+        } else {
+            status = rwl_readtrylock (&data[element].lock);
+            if (status == EBUSY)
+                self->r_collisions++;
+            else if (status != 0) {
+                err_abort (status, _("Try read lock"));
+            } else {
+                if (data[element].data != data[element].updates)
                     printf ("%d: data[%d] %d != %d\n",
-                       self->thread_num, element,
-                       data[element].data, data[element].updates);
-               rwl_readunlock (&data[element].lock);
-           }
-       }
-
-       element++;
-       if (element >= DATASIZE)
-           element = 0;
+                        self->thread_num, element,
+                        data[element].data, data[element].updates);
+                rwl_readunlock (&data[element].lock);
+            }
+        }
+
+        element++;
+        if (element >= DATASIZE)
+            element = 0;
     }
+    lmgr_cleanup_thread();
     return NULL;
 }
 
@@ -580,24 +633,24 @@ int main (int argc, char *argv[])
      * Initialize the shared data.
      */
     for (data_count = 0; data_count < DATASIZE; data_count++) {
-       data[data_count].data = 0;
-       data[data_count].updates = 0;
-       rwl_init (&data[data_count].lock);
+        data[data_count].data = 0;
+        data[data_count].updates = 0;
+        rwl_init(&data[data_count].lock);
     }
 
     /*
      * Create THREADS threads to access shared data.
      */
     for (count = 0; count < THREADS; count++) {
-       threads[count].thread_num = count;
-       threads[count].r_collisions = 0;
-       threads[count].w_collisions = 0;
-       threads[count].updates = 0;
-       threads[count].interval = rand_r (&seed) % ITERATIONS;
-       status = pthread_create (&threads[count].thread_id,
-           NULL, thread_routine, (void*)&threads[count]);
-       if (status != 0)
-            err_abort (status, "Create thread");
+        threads[count].thread_num = count;
+        threads[count].r_collisions = 0;
+        threads[count].w_collisions = 0;
+        threads[count].updates = 0;
+        threads[count].interval = rand_r (&seed) % ITERATIONS;
+        status = pthread_create (&threads[count].thread_id,
+            NULL, thread_routine, (void*)&threads[count]);
+        if (status != 0)
+            err_abort (status, _("Create thread"));
     }
 
     /*
@@ -605,25 +658,25 @@ int main (int argc, char *argv[])
      * statistics.
      */
     for (count = 0; count < THREADS; count++) {
-       status = pthread_join (threads[count].thread_id, NULL);
-       if (status != 0)
-            err_abort (status, "Join thread");
-       thread_updates += threads[count].updates;
-        printf ("%02d: interval %d, updates %d, "
-                "r_collisions %d, w_collisions %d\n",
-           count, threads[count].interval,
-           threads[count].updates,
-           threads[count].r_collisions, threads[count].w_collisions);
+        status = pthread_join (threads[count].thread_id, NULL);
+        if (status != 0)
+            err_abort (status, _("Join thread"));
+        thread_updates += threads[count].updates;
+        printf (_("%02d: interval %d, updates %d, "
+                "r_collisions %d, w_collisions %d\n"),
+            count, threads[count].interval,
+            threads[count].updates,
+            threads[count].r_collisions, threads[count].w_collisions);
     }
 
     /*
      * 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);
-       rwl_destroy (&data[data_count].lock);
+        data_updates += data[data_count].updates;
+        printf (_("data %02d: value %d, %d updates\n"),
+            data_count, data[data_count].data, data[data_count].updates);
+        rwl_destroy (&data[data_count].lock);
     }
 
     return 0;