Blame gnulib/tests/test-rwlock1.c

Packit 06dd63
/* Test of glthread_rwlock_rdlock function.
Packit 06dd63
   Copyright (C) 2017-2019 Free Software Foundation, Inc.
Packit 06dd63
Packit 06dd63
   This program is free software: you can redistribute it and/or modify
Packit 06dd63
   it under the terms of the GNU General Public License as published by
Packit 06dd63
   the Free Software Foundation; either version 3 of the License, or
Packit 06dd63
   (at your option) any later version.
Packit 06dd63
Packit 06dd63
   This program is distributed in the hope that it will be useful,
Packit 06dd63
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 06dd63
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 06dd63
   GNU General Public License for more details.
Packit 06dd63
Packit 06dd63
   You should have received a copy of the GNU General Public License
Packit 06dd63
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
Packit 06dd63
Packit 06dd63
/* Written by Bruno Haible <bruno@clisp.org>, 2005.
Packit 06dd63
   Inspired by
Packit 06dd63
   https://github.com/linux-test-project/ltp/blob/master/testcases/open_posix_testsuite/conformance/interfaces/pthread_rwlock_rdlock/2-2.c
Packit 06dd63
   by Intel Corporation.  */
Packit 06dd63
Packit 06dd63
#include <config.h>
Packit 06dd63
Packit 06dd63
#include "glthread/lock.h"
Packit 06dd63
Packit 06dd63
#include <errno.h>
Packit 06dd63
#include <stdio.h>
Packit 06dd63
#include <stdlib.h>
Packit 06dd63
#include <unistd.h>
Packit 06dd63
Packit 06dd63
#include "glthread/thread.h"
Packit 06dd63
#include "glthread/yield.h"
Packit 06dd63
Packit 06dd63
/* Verify that in a situation where
Packit 06dd63
     - an rwlock is taken by a reader and has a writer waiting,
Packit 06dd63
     - an additional reader requests the lock,
Packit 06dd63
     - the waiting writer and the requesting reader threads have the same
Packit 06dd63
       priority,
Packit 06dd63
   the requesting reader thread gets blocked, so that at some point the
Packit 06dd63
   waiting writer can acquire the lock.
Packit 06dd63
   Without such a guarantee, when there a N readers and each of the readers
Packit 06dd63
   spends more than 1/Nth of the time with the lock held, there is a high
Packit 06dd63
   probability that the waiting writer will not get the lock in a given finite
Packit 06dd63
   time, a phenomenon called "writer starvation".
Packit 06dd63
   Without such a guarantee, applications have a hard time avoiding writer
Packit 06dd63
   starvation.
Packit 06dd63
Packit 06dd63
   POSIX:2008 makes this requirement only for implementations that support TPS
Packit 06dd63
   (Thread Priority Scheduling) and only for the scheduling policies SCHED_FIFO
Packit 06dd63
   and SCHED_RR, see
Packit 06dd63
   http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_rdlock.html
Packit 06dd63
   but test verifies the guarantee regardless of TPS and regardless of
Packit 06dd63
   scheduling policy.  */
Packit 06dd63
Packit 06dd63
#define SUCCEED() exit (0)
Packit 06dd63
#define FAILURE() exit (1)
Packit 06dd63
#define UNEXPECTED(n) (fprintf (stderr, "Unexpected outcome %d\n", n), abort ())
Packit 06dd63
Packit 06dd63
/* The main thread creates the waiting writer and the requesting reader threads
Packit 06dd63
   in the default way; this guarantees that they have the same priority.
Packit 06dd63
   We can reuse the main thread as first reader thread.  */
Packit 06dd63
Packit 06dd63
static gl_rwlock_t lock;
Packit 06dd63
static gl_thread_t reader1;
Packit 06dd63
static gl_thread_t writer;
Packit 06dd63
static gl_thread_t reader2;
Packit 06dd63
static gl_thread_t timer;
Packit 06dd63
/* Used to pass control from writer to reader2 and from reader2 to timer,
Packit 06dd63
   as in a relay race.
Packit 06dd63
   Passing control from one running thread to another running thread
Packit 06dd63
   is most likely faster than to create the second thread.  */
Packit 06dd63
static gl_lock_t baton;
Packit 06dd63
Packit 06dd63
static void *
Packit 06dd63
timer_func (void *ignored)
Packit 06dd63
{
Packit 06dd63
  /* Step 13 (can be before or after step 12):
Packit 06dd63
     The timer thread takes the baton, then waits a moment to make sure
Packit 06dd63
     it can tell whether the second reader thread is blocked at step 12.  */
Packit 06dd63
  if (glthread_lock_lock (&baton))
Packit 06dd63
    UNEXPECTED (13);
Packit 06dd63
  usleep (100000);
Packit 06dd63
  /* By the time we get here, it's clear that the second reader thread is
Packit 06dd63
     blocked at step 12.  This is the desired behaviour.  */
Packit 06dd63
  SUCCEED ();
Packit 06dd63
}
Packit 06dd63
Packit 06dd63
static void *
Packit 06dd63
reader2_func (void *ignored)
Packit 06dd63
{
Packit 06dd63
  int err;
Packit 06dd63
Packit 06dd63
  /* Step 8 (can be before or after step 7):
Packit 06dd63
     The second reader thread takes the baton, then waits a moment to make sure
Packit 06dd63
     the writer thread has reached step 7.  */
Packit 06dd63
  if (glthread_lock_lock (&baton))
Packit 06dd63
    UNEXPECTED (8);
Packit 06dd63
  usleep (100000);
Packit 06dd63
  /* Step 9 omitted.  */
Packit 06dd63
  /* Step 10: Launch a timer, to test whether the next call blocks.  */
Packit 06dd63
  if (glthread_create (&timer, timer_func, NULL))
Packit 06dd63
    UNEXPECTED (10);
Packit 06dd63
  /* Step 11: Release the baton.  */
Packit 06dd63
  if (glthread_lock_unlock (&baton))
Packit 06dd63
    UNEXPECTED (11);
Packit 06dd63
  /* Step 12: The second reader thread requests the lock.  */
Packit 06dd63
  err = glthread_rwlock_rdlock (&lock);
Packit 06dd63
  if (err == 0)
Packit 06dd63
    FAILURE ();
Packit 06dd63
  else
Packit 06dd63
    UNEXPECTED (12);
Packit 06dd63
}
Packit 06dd63
Packit 06dd63
static void *
Packit 06dd63
writer_func (void *ignored)
Packit 06dd63
{
Packit 06dd63
  /* Step 4: Take the baton, so that the second reader thread does not go ahead
Packit 06dd63
     too early.  */
Packit 06dd63
  if (glthread_lock_lock (&baton))
Packit 06dd63
    UNEXPECTED (4);
Packit 06dd63
  /* Step 5: Create the second reader thread.  */
Packit 06dd63
  if (glthread_create (&reader2, reader2_func, NULL))
Packit 06dd63
    UNEXPECTED (5);
Packit 06dd63
  /* Step 6: Release the baton.  */
Packit 06dd63
  if (glthread_lock_unlock (&baton))
Packit 06dd63
    UNEXPECTED (6);
Packit 06dd63
  /* Step 7: The writer thread requests the lock.  */
Packit 06dd63
  if (glthread_rwlock_wrlock (&lock))
Packit 06dd63
    UNEXPECTED (7);
Packit 06dd63
  return NULL;
Packit 06dd63
}
Packit 06dd63
Packit 06dd63
int
Packit 06dd63
main ()
Packit 06dd63
{
Packit 06dd63
  reader1 = gl_thread_self ();
Packit 06dd63
Packit 06dd63
  /* Step 1: The main thread initializes the lock and the baton.  */
Packit 06dd63
  if (glthread_rwlock_init (&lock))
Packit 06dd63
    UNEXPECTED (1);
Packit 06dd63
  if (glthread_lock_init (&baton))
Packit 06dd63
    UNEXPECTED (1);
Packit 06dd63
  /* Step 2: The main thread acquires the lock as a reader.  */
Packit 06dd63
  if (glthread_rwlock_rdlock (&lock))
Packit 06dd63
    UNEXPECTED (2);
Packit 06dd63
  /* Step 3: Create the writer thread.  */
Packit 06dd63
  if (glthread_create (&writer, writer_func, NULL))
Packit 06dd63
    UNEXPECTED (3);
Packit 06dd63
  /* Job done.  Go to sleep.  */
Packit 06dd63
  for (;;)
Packit 06dd63
    {
Packit 06dd63
      /* In cooperative threads implementations (Pth), give other threads
Packit 06dd63
         a chance to run.  */
Packit 06dd63
      gl_thread_yield ();
Packit 06dd63
      sleep (1);
Packit 06dd63
    }
Packit 06dd63
}