Blame gl/m4/pthread_rwlock_rdlock.m4

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