pthread_rwlock_rdlock.m4 5.59 KB
Newer Older
1
# pthread_rwlock_rdlock.m4 serial 2
2
dnl Copyright (C) 2017-2018 Free Software Foundation, Inc.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.

dnl From Bruno Haible.
dnl Inspired by
dnl https://github.com/linux-test-project/ltp/blob/master/testcases/open_posix_testsuite/conformance/interfaces/pthread_rwlock_rdlock/2-2.c
dnl by Intel Corporation.

dnl Test whether in a situation where
dnl   - an rwlock is taken by a reader and has a writer waiting,
dnl   - an additional reader requests the lock,
dnl   - the waiting writer and the requesting reader threads have the same
dnl     priority,
dnl the requesting reader thread gets blocked, so that at some point the
dnl waiting writer can acquire the lock.
dnl Without such a guarantee, when there a N readers and each of the readers
dnl spends more than 1/Nth of the time with the lock held, there is a high
dnl probability that the waiting writer will not get the lock in a given finite
dnl time, a phenomenon called "writer starvation".
dnl Without such a guarantee, applications have a hard time avoiding writer
dnl starvation.
dnl
26
dnl POSIX:2017 makes this requirement only for implementations that support TPS
27 28 29
dnl (Thread Priority Scheduling) and only for the scheduling policies SCHED_FIFO
dnl and SCHED_RR, see
dnl http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_rdlock.html
30
dnl but this test verifies the guarantee regardless of TPS and regardless of
31
dnl scheduling policy.
32 33
dnl Glibc currently does not provide this guarantee, see
dnl https://sourceware.org/bugzilla/show_bug.cgi?id=13701
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
AC_DEFUN([gl_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER],
[
  AC_REQUIRE([gl_THREADLIB_EARLY])
  AC_CACHE_CHECK([whether pthread_rwlock_rdlock prefers a writer to a reader],
    [gl_cv_pthread_rwlock_rdlock_prefer_writer],
    [save_LIBS="$LIBS"
     LIBS="$LIBS $LIBMULTITHREAD"
     AC_RUN_IFELSE(
       [AC_LANG_SOURCE([[
#include <errno.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>

#define SUCCEED() exit (0)
#define FAILURE() exit (1)
#define UNEXPECTED(n) (exit (10 + (n)))

/* The main thread creates the waiting writer and the requesting reader threads
   in the default way; this guarantees that they have the same priority.
   We can reuse the main thread as first reader thread.  */

static pthread_rwlock_t lock;
static pthread_t reader1;
static pthread_t writer;
static pthread_t reader2;
static pthread_t timer;
/* Used to pass control from writer to reader2 and from reader2 to timer,
   as in a relay race.
   Passing control from one running thread to another running thread
   is most likely faster than to create the second thread.  */
static pthread_mutex_t baton;

static void *
timer_func (void *ignored)
{
  /* Step 13 (can be before or after step 12):
     The timer thread takes the baton, then waits a moment to make sure
     it can tell whether the second reader thread is blocked at step 12.  */
  if (pthread_mutex_lock (&baton))
    UNEXPECTED (13);
  usleep (100000);
  /* By the time we get here, it's clear that the second reader thread is
     blocked at step 12.  This is the desired behaviour.  */
  SUCCEED ();
}

static void *
reader2_func (void *ignored)
{
  int err;

  /* Step 8 (can be before or after step 7):
     The second reader thread takes the baton, then waits a moment to make sure
     the writer thread has reached step 7.  */
  if (pthread_mutex_lock (&baton))
    UNEXPECTED (8);
  usleep (100000);
  /* Step 9: The second reader thread requests the lock.  */
  err = pthread_rwlock_tryrdlock (&lock);
  if (err == 0)
    FAILURE ();
  else if (err != EBUSY)
    UNEXPECTED (9);
  /* Step 10: Launch a timer, to test whether the next call blocks.  */
  if (pthread_create (&timer, NULL, timer_func, NULL))
    UNEXPECTED (10);
  /* Step 11: Release the baton.  */
  if (pthread_mutex_unlock (&baton))
    UNEXPECTED (11);
  /* Step 12: The second reader thread requests the lock.  */
  err = pthread_rwlock_rdlock (&lock);
  if (err == 0)
    FAILURE ();
  else
    UNEXPECTED (12);
}

static void *
writer_func (void *ignored)
{
  /* Step 4: Take the baton, so that the second reader thread does not go ahead
     too early.  */
  if (pthread_mutex_lock (&baton))
    UNEXPECTED (4);
  /* Step 5: Create the second reader thread.  */
  if (pthread_create (&reader2, NULL, reader2_func, NULL))
    UNEXPECTED (5);
  /* Step 6: Release the baton.  */
  if (pthread_mutex_unlock (&baton))
    UNEXPECTED (6);
  /* Step 7: The writer thread requests the lock.  */
  if (pthread_rwlock_wrlock (&lock))
    UNEXPECTED (7);
  return NULL;
}

int
main ()
{
  reader1 = pthread_self ();

  /* Step 1: The main thread initializes the lock and the baton.  */
  if (pthread_rwlock_init (&lock, NULL))
    UNEXPECTED (1);
  if (pthread_mutex_init (&baton, NULL))
    UNEXPECTED (1);
  /* Step 2: The main thread acquires the lock as a reader.  */
  if (pthread_rwlock_rdlock (&lock))
    UNEXPECTED (2);
  /* Step 3: Create the writer thread.  */
  if (pthread_create (&writer, NULL, writer_func, NULL))
    UNEXPECTED (3);
  /* Job done.  Go to sleep.  */
  for (;;)
    {
      sleep (1);
    }
}
]])],
       [gl_cv_pthread_rwlock_rdlock_prefer_writer=yes],
       [gl_cv_pthread_rwlock_rdlock_prefer_writer=no],
       [gl_cv_pthread_rwlock_rdlock_prefer_writer="guessing yes"])
     LIBS="$save_LIBS"
    ])
  case "$gl_cv_pthread_rwlock_rdlock_prefer_writer" in
    *yes)
      AC_DEFINE([HAVE_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER], [1],
        [Define if the 'pthread_rwlock_rdlock' function prefers a writer to a reader.])
      ;;
  esac
])