(root)/
coreutils-9.4/
gnulib-tests/
test-rwlock1.c
       1  /* Test of glthread_rwlock_rdlock function.
       2     Copyright (C) 2017-2023 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)
      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)
      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)
     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 ()
     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 ()
     165  {
     166    fputs ("Skipping test: multithreading not enabled\n", stderr);
     167    return 77;
     168  }
     169  
     170  #endif