root/maint/gnulib/tests/test-rwlock1.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. timer_func
  2. reader2_func
  3. writer_func
  4. main
  5. main

   1 /* Test of glthread_rwlock_rdlock function.
   2    Copyright (C) 2017-2021 Free Software Foundation, Inc.
   3 
   4    This program is free software: you can redistribute it and/or modify
   5    it under the terms of the GNU General Public License as published by
   6    the Free Software Foundation; either version 3 of the License, or
   7    (at your option) any later version.
   8 
   9    This program is distributed in the hope that it will be useful,
  10    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12    GNU General Public License for more details.
  13 
  14    You should have received a copy of the GNU General Public License
  15    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
  16 
  17 /* Written by Bruno Haible <bruno@clisp.org>, 2005.
  18    Inspired by
  19    https://github.com/linux-test-project/ltp/blob/master/testcases/open_posix_testsuite/conformance/interfaces/pthread_rwlock_rdlock/2-2.c
  20    by Intel Corporation.  */
  21 
  22 #include <config.h>
  23 
  24 #if USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS
  25 
  26 #include "glthread/lock.h"
  27 
  28 #include <errno.h>
  29 #include <stdio.h>
  30 #include <stdlib.h>
  31 #include <unistd.h>
  32 
  33 #include "glthread/thread.h"
  34 
  35 /* Verify that in a situation where
  36      - an rwlock is taken by a reader and has a writer waiting,
  37      - an additional reader requests the lock,
  38      - the waiting writer and the requesting reader threads have the same
  39        priority,
  40    the requesting reader thread gets blocked, so that at some point the
  41    waiting writer can acquire the lock.
  42    Without such a guarantee, when there a N readers and each of the readers
  43    spends more than 1/Nth of the time with the lock held, there is a high
  44    probability that the waiting writer will not get the lock in a given finite
  45    time, a phenomenon called "writer starvation".
  46    Without such a guarantee, applications have a hard time avoiding writer
  47    starvation.
  48 
  49    POSIX:2008 makes this requirement only for implementations that support TPS
  50    (Thread Priority Scheduling) and only for the scheduling policies SCHED_FIFO
  51    and SCHED_RR, see
  52    https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_rdlock.html
  53    but test verifies the guarantee regardless of TPS and regardless of
  54    scheduling policy.  */
  55 
  56 #define SUCCEED() exit (0)
  57 #define FAILURE() exit (1)
  58 #define UNEXPECTED(n) (fprintf (stderr, "Unexpected outcome %d\n", n), abort ())
  59 
  60 /* The main thread creates the waiting writer and the requesting reader threads
  61    in the default way; this guarantees that they have the same priority.
  62    We can reuse the main thread as first reader thread.  */
  63 
  64 static gl_rwlock_t lock;
  65 static gl_thread_t reader1;
  66 static gl_thread_t writer;
  67 static gl_thread_t reader2;
  68 static gl_thread_t timer;
  69 /* Used to pass control from writer to reader2 and from reader2 to timer,
  70    as in a relay race.
  71    Passing control from one running thread to another running thread
  72    is most likely faster than to create the second thread.  */
  73 static gl_lock_t baton;
  74 
  75 static void *
  76 timer_func (void *ignored)
     /* [previous][next][first][last][top][bottom][index][help] */
  77 {
  78   /* Step 13 (can be before or after step 12):
  79      The timer thread takes the baton, then waits a moment to make sure
  80      it can tell whether the second reader thread is blocked at step 12.  */
  81   if (glthread_lock_lock (&baton))
  82     UNEXPECTED (13);
  83   usleep (100000);
  84   /* By the time we get here, it's clear that the second reader thread is
  85      blocked at step 12.  This is the desired behaviour.  */
  86   SUCCEED ();
  87 }
  88 
  89 static void *
  90 reader2_func (void *ignored)
     /* [previous][next][first][last][top][bottom][index][help] */
  91 {
  92   int err;
  93 
  94   /* Step 8 (can be before or after step 7):
  95      The second reader thread takes the baton, then waits a moment to make sure
  96      the writer thread has reached step 7.  */
  97   if (glthread_lock_lock (&baton))
  98     UNEXPECTED (8);
  99   usleep (100000);
 100   /* Step 9 omitted.  */
 101   /* Step 10: Launch a timer, to test whether the next call blocks.  */
 102   if (glthread_create (&timer, timer_func, NULL))
 103     UNEXPECTED (10);
 104   /* Step 11: Release the baton.  */
 105   if (glthread_lock_unlock (&baton))
 106     UNEXPECTED (11);
 107   /* Step 12: The second reader thread requests the lock.  */
 108   err = glthread_rwlock_rdlock (&lock);
 109   if (err == 0)
 110     FAILURE ();
 111   else
 112     UNEXPECTED (12);
 113 }
 114 
 115 static void *
 116 writer_func (void *ignored)
     /* [previous][next][first][last][top][bottom][index][help] */
 117 {
 118   /* Step 4: Take the baton, so that the second reader thread does not go ahead
 119      too early.  */
 120   if (glthread_lock_lock (&baton))
 121     UNEXPECTED (4);
 122   /* Step 5: Create the second reader thread.  */
 123   if (glthread_create (&reader2, reader2_func, NULL))
 124     UNEXPECTED (5);
 125   /* Step 6: Release the baton.  */
 126   if (glthread_lock_unlock (&baton))
 127     UNEXPECTED (6);
 128   /* Step 7: The writer thread requests the lock.  */
 129   if (glthread_rwlock_wrlock (&lock))
 130     UNEXPECTED (7);
 131   return NULL;
 132 }
 133 
 134 int
 135 main ()
     /* [previous][next][first][last][top][bottom][index][help] */
 136 {
 137   reader1 = gl_thread_self ();
 138 
 139   /* Step 1: The main thread initializes the lock and the baton.  */
 140   if (glthread_rwlock_init (&lock))
 141     UNEXPECTED (1);
 142   if (glthread_lock_init (&baton))
 143     UNEXPECTED (1);
 144   /* Step 2: The main thread acquires the lock as a reader.  */
 145   if (glthread_rwlock_rdlock (&lock))
 146     UNEXPECTED (2);
 147   /* Step 3: Create the writer thread.  */
 148   if (glthread_create (&writer, writer_func, NULL))
 149     UNEXPECTED (3);
 150   /* Job done.  Go to sleep.  */
 151   for (;;)
 152     {
 153       sleep (1);
 154     }
 155 }
 156 
 157 #else
 158 
 159 /* No multithreading available.  */
 160 
 161 #include <stdio.h>
 162 
 163 int
 164 main ()
     /* [previous][next][first][last][top][bottom][index][help] */
 165 {
 166   fputs ("Skipping test: multithreading not enabled\n", stderr);
 167   return 77;
 168 }
 169 
 170 #endif

/* [previous][next][first][last][top][bottom][index][help] */