Blame m4/pthread_rwlock_rdlock.m4

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