Blame m4/pthread_rwlock_rdlock.m4

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