Blame m4/pthread_rwlock_rdlock.m4

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