Blame m4/pthread_rwlock_rdlock.m4

Packit 709fb3
# pthread_rwlock_rdlock.m4 serial 1
Packit 709fb3
dnl Copyright (C) 2017 Free Software Foundation, Inc.
Packit 709fb3
dnl This file is free software; the Free Software Foundation
Packit 709fb3
dnl gives unlimited permission to copy and/or distribute it,
Packit 709fb3
dnl with or without modifications, as long as this notice is preserved.
Packit 709fb3
Packit 709fb3
dnl From Bruno Haible.
Packit 709fb3
dnl Inspired by
Packit 709fb3
dnl https://github.com/linux-test-project/ltp/blob/master/testcases/open_posix_testsuite/conformance/interfaces/pthread_rwlock_rdlock/2-2.c
Packit 709fb3
dnl by Intel Corporation.
Packit 709fb3
Packit 709fb3
dnl Test whether in a situation where
Packit 709fb3
dnl   - an rwlock is taken by a reader and has a writer waiting,
Packit 709fb3
dnl   - an additional reader requests the lock,
Packit 709fb3
dnl   - the waiting writer and the requesting reader threads have the same
Packit 709fb3
dnl     priority,
Packit 709fb3
dnl the requesting reader thread gets blocked, so that at some point the
Packit 709fb3
dnl waiting writer can acquire the lock.
Packit 709fb3
dnl Without such a guarantee, when there a N readers and each of the readers
Packit 709fb3
dnl spends more than 1/Nth of the time with the lock held, there is a high
Packit 709fb3
dnl probability that the waiting writer will not get the lock in a given finite
Packit 709fb3
dnl time, a phenomenon called "writer starvation".
Packit 709fb3
dnl Without such a guarantee, applications have a hard time avoiding writer
Packit 709fb3
dnl starvation.
Packit 709fb3
dnl
Packit 709fb3
dnl POSIX:2008 makes this requirement only for implementations that support TPS
Packit 709fb3
dnl (Thread Priority Scheduling) and only for the scheduling policies SCHED_FIFO
Packit 709fb3
dnl and SCHED_RR, see
Packit 709fb3
dnl http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_rdlock.html
Packit 709fb3
dnl but test verifies the guarantee regardless of TPS and regardless of
Packit 709fb3
dnl scheduling policy.
Packit 709fb3
AC_DEFUN([gl_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER],
Packit 709fb3
[
Packit 709fb3
  AC_REQUIRE([gl_THREADLIB_EARLY])
Packit 709fb3
  AC_CACHE_CHECK([whether pthread_rwlock_rdlock prefers a writer to a reader],
Packit 709fb3
    [gl_cv_pthread_rwlock_rdlock_prefer_writer],
Packit 709fb3
    [save_LIBS="$LIBS"
Packit 709fb3
     LIBS="$LIBS $LIBMULTITHREAD"
Packit 709fb3
     AC_RUN_IFELSE(
Packit 709fb3
       [AC_LANG_SOURCE([[
Packit 709fb3
#include <errno.h>
Packit 709fb3
#include <pthread.h>
Packit 709fb3
#include <stdlib.h>
Packit 709fb3
#include <unistd.h>
Packit 709fb3
Packit 709fb3
#define SUCCEED() exit (0)
Packit 709fb3
#define FAILURE() exit (1)
Packit 709fb3
#define UNEXPECTED(n) (exit (10 + (n)))
Packit 709fb3
Packit 709fb3
/* The main thread creates the waiting writer and the requesting reader threads
Packit 709fb3
   in the default way; this guarantees that they have the same priority.
Packit 709fb3
   We can reuse the main thread as first reader thread.  */
Packit 709fb3
Packit 709fb3
static pthread_rwlock_t lock;
Packit 709fb3
static pthread_t reader1;
Packit 709fb3
static pthread_t writer;
Packit 709fb3
static pthread_t reader2;
Packit 709fb3
static pthread_t timer;
Packit 709fb3
/* Used to pass control from writer to reader2 and from reader2 to timer,
Packit 709fb3
   as in a relay race.
Packit 709fb3
   Passing control from one running thread to another running thread
Packit 709fb3
   is most likely faster than to create the second thread.  */
Packit 709fb3
static pthread_mutex_t baton;
Packit 709fb3
Packit 709fb3
static void *
Packit 709fb3
timer_func (void *ignored)
Packit 709fb3
{
Packit 709fb3
  /* Step 13 (can be before or after step 12):
Packit 709fb3
     The timer thread takes the baton, then waits a moment to make sure
Packit 709fb3
     it can tell whether the second reader thread is blocked at step 12.  */
Packit 709fb3
  if (pthread_mutex_lock (&baton))
Packit 709fb3
    UNEXPECTED (13);
Packit 709fb3
  usleep (100000);
Packit 709fb3
  /* By the time we get here, it's clear that the second reader thread is
Packit 709fb3
     blocked at step 12.  This is the desired behaviour.  */
Packit 709fb3
  SUCCEED ();
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
static void *
Packit 709fb3
reader2_func (void *ignored)
Packit 709fb3
{
Packit 709fb3
  int err;
Packit 709fb3
Packit 709fb3
  /* Step 8 (can be before or after step 7):
Packit 709fb3
     The second reader thread takes the baton, then waits a moment to make sure
Packit 709fb3
     the writer thread has reached step 7.  */
Packit 709fb3
  if (pthread_mutex_lock (&baton))
Packit 709fb3
    UNEXPECTED (8);
Packit 709fb3
  usleep (100000);
Packit 709fb3
  /* Step 9: The second reader thread requests the lock.  */
Packit 709fb3
  err = pthread_rwlock_tryrdlock (&lock);
Packit 709fb3
  if (err == 0)
Packit 709fb3
    FAILURE ();
Packit 709fb3
  else if (err != EBUSY)
Packit 709fb3
    UNEXPECTED (9);
Packit 709fb3
  /* Step 10: Launch a timer, to test whether the next call blocks.  */
Packit 709fb3
  if (pthread_create (&timer, NULL, timer_func, NULL))
Packit 709fb3
    UNEXPECTED (10);
Packit 709fb3
  /* Step 11: Release the baton.  */
Packit 709fb3
  if (pthread_mutex_unlock (&baton))
Packit 709fb3
    UNEXPECTED (11);
Packit 709fb3
  /* Step 12: The second reader thread requests the lock.  */
Packit 709fb3
  err = pthread_rwlock_rdlock (&lock);
Packit 709fb3
  if (err == 0)
Packit 709fb3
    FAILURE ();
Packit 709fb3
  else
Packit 709fb3
    UNEXPECTED (12);
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
static void *
Packit 709fb3
writer_func (void *ignored)
Packit 709fb3
{
Packit 709fb3
  /* Step 4: Take the baton, so that the second reader thread does not go ahead
Packit 709fb3
     too early.  */
Packit 709fb3
  if (pthread_mutex_lock (&baton))
Packit 709fb3
    UNEXPECTED (4);
Packit 709fb3
  /* Step 5: Create the second reader thread.  */
Packit 709fb3
  if (pthread_create (&reader2, NULL, reader2_func, NULL))
Packit 709fb3
    UNEXPECTED (5);
Packit 709fb3
  /* Step 6: Release the baton.  */
Packit 709fb3
  if (pthread_mutex_unlock (&baton))
Packit 709fb3
    UNEXPECTED (6);
Packit 709fb3
  /* Step 7: The writer thread requests the lock.  */
Packit 709fb3
  if (pthread_rwlock_wrlock (&lock))
Packit 709fb3
    UNEXPECTED (7);
Packit 709fb3
  return NULL;
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
int
Packit 709fb3
main ()
Packit 709fb3
{
Packit 709fb3
  reader1 = pthread_self ();
Packit 709fb3
Packit 709fb3
  /* Step 1: The main thread initializes the lock and the baton.  */
Packit 709fb3
  if (pthread_rwlock_init (&lock, NULL))
Packit 709fb3
    UNEXPECTED (1);
Packit 709fb3
  if (pthread_mutex_init (&baton, NULL))
Packit 709fb3
    UNEXPECTED (1);
Packit 709fb3
  /* Step 2: The main thread acquires the lock as a reader.  */
Packit 709fb3
  if (pthread_rwlock_rdlock (&lock))
Packit 709fb3
    UNEXPECTED (2);
Packit 709fb3
  /* Step 3: Create the writer thread.  */
Packit 709fb3
  if (pthread_create (&writer, NULL, writer_func, NULL))
Packit 709fb3
    UNEXPECTED (3);
Packit 709fb3
  /* Job done.  Go to sleep.  */
Packit 709fb3
  for (;;)
Packit 709fb3
    {
Packit 709fb3
      sleep (1);
Packit 709fb3
    }
Packit 709fb3
}
Packit 709fb3
]])],
Packit 709fb3
       [gl_cv_pthread_rwlock_rdlock_prefer_writer=yes],
Packit 709fb3
       [gl_cv_pthread_rwlock_rdlock_prefer_writer=no],
Packit 709fb3
       [gl_cv_pthread_rwlock_rdlock_prefer_writer="guessing yes"])
Packit 709fb3
     LIBS="$save_LIBS"
Packit 709fb3
    ])
Packit 709fb3
  case "$gl_cv_pthread_rwlock_rdlock_prefer_writer" in
Packit 709fb3
    *yes)
Packit 709fb3
      AC_DEFINE([HAVE_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER], [1],
Packit 709fb3
        [Define if the 'pthread_rwlock_rdlock' function prefers a writer to a reader.])
Packit 709fb3
      ;;
Packit 709fb3
  esac
Packit 709fb3
])