Blame m4/pthread_rwlock_rdlock.m4

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