Merge pull request #2053 from klaernie/support
[monitoring-plugins.git] / gl / m4 / pthread_rwlock_rdlock.m4
blobb8b5b117242cc8f00b2e6a0f8d5797f831b2838d
1 # pthread_rwlock_rdlock.m4
2 # serial 8
3 dnl Copyright (C) 2017-2024 Free Software Foundation, Inc.
4 dnl This file is free software; the Free Software Foundation
5 dnl gives unlimited permission to copy and/or distribute it,
6 dnl with or without modifications, as long as this notice is preserved.
8 dnl From Bruno Haible.
9 dnl Inspired by
10 dnl https://github.com/linux-test-project/ltp/blob/master/testcases/open_posix_testsuite/conformance/interfaces/pthread_rwlock_rdlock/2-2.c
11 dnl by Intel Corporation.
13 dnl Test whether in a situation where
14 dnl   - an rwlock is taken by a reader and has a writer waiting,
15 dnl   - an additional reader requests the lock,
16 dnl   - the waiting writer and the requesting reader threads have the same
17 dnl     priority,
18 dnl the requesting reader thread gets blocked, so that at some point the
19 dnl waiting writer can acquire the lock.
20 dnl Without such a guarantee, when there a N readers and each of the readers
21 dnl spends more than 1/Nth of the time with the lock held, there is a high
22 dnl probability that the waiting writer will not get the lock in a given finite
23 dnl time, a phenomenon called "writer starvation".
24 dnl Without such a guarantee, applications have a hard time avoiding writer
25 dnl starvation.
26 dnl
27 dnl POSIX:2017 makes this requirement only for implementations that support TPS
28 dnl (Thread Priority Scheduling) and only for the scheduling policies SCHED_FIFO
29 dnl and SCHED_RR, see
30 dnl https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_rdlock.html
31 dnl but this test verifies the guarantee regardless of TPS and regardless of
32 dnl scheduling policy.
33 dnl Glibc does not provide this guarantee (and never will on Linux), see
34 dnl https://sourceware.org/bugzilla/show_bug.cgi?id=13701
35 dnl https://bugzilla.redhat.com/show_bug.cgi?id=1410052
36 AC_DEFUN([gl_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER],
38   AC_REQUIRE([gl_THREADLIB])
39   AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
40   AC_CACHE_CHECK([whether pthread_rwlock_rdlock prefers a writer to a reader],
41     [gl_cv_pthread_rwlock_rdlock_prefer_writer],
42     [saved_LIBS="$LIBS"
43      LIBS="$LIBS $LIBMULTITHREAD"
44      AC_RUN_IFELSE(
45        [AC_LANG_SOURCE([[
46 #include <errno.h>
47 #include <pthread.h>
48 #include <stdlib.h>
49 #include <unistd.h>
51 #define SUCCEED() exit (0)
52 #define FAILURE() exit (1)
53 #define UNEXPECTED(n) (exit (10 + (n)))
55 /* The main thread creates the waiting writer and the requesting reader threads
56    in the default way; this guarantees that they have the same priority.
57    We can reuse the main thread as first reader thread.  */
59 static pthread_rwlock_t lock;
60 static pthread_t reader1;
61 static pthread_t writer;
62 static pthread_t reader2;
63 static pthread_t timer;
64 /* Used to pass control from writer to reader2 and from reader2 to timer,
65    as in a relay race.
66    Passing control from one running thread to another running thread
67    is most likely faster than to create the second thread.  */
68 static pthread_mutex_t baton;
70 static void *
71 timer_func (void *ignored)
73   /* Step 13 (can be before or after step 12):
74      The timer thread takes the baton, then waits a moment to make sure
75      it can tell whether the second reader thread is blocked at step 12.  */
76   if (pthread_mutex_lock (&baton))
77     UNEXPECTED (13);
78   usleep (100000);
79   /* By the time we get here, it's clear that the second reader thread is
80      blocked at step 12.  This is the desired behaviour.  */
81   SUCCEED ();
84 static void *
85 reader2_func (void *ignored)
87   int err;
89   /* Step 8 (can be before or after step 7):
90      The second reader thread takes the baton, then waits a moment to make sure
91      the writer thread has reached step 7.  */
92   if (pthread_mutex_lock (&baton))
93     UNEXPECTED (8);
94   usleep (100000);
95   /* Step 9: The second reader thread requests the lock.  */
96   err = pthread_rwlock_tryrdlock (&lock);
97   if (err == 0)
98     FAILURE ();
99   else if (err != EBUSY)
100     UNEXPECTED (9);
101   /* Step 10: Launch a timer, to test whether the next call blocks.  */
102   if (pthread_create (&timer, NULL, timer_func, NULL))
103     UNEXPECTED (10);
104   /* Step 11: Release the baton.  */
105   if (pthread_mutex_unlock (&baton))
106     UNEXPECTED (11);
107   /* Step 12: The second reader thread requests the lock.  */
108   err = pthread_rwlock_rdlock (&lock);
109   if (err == 0)
110     FAILURE ();
111   else
112     UNEXPECTED (12);
115 static void *
116 writer_func (void *ignored)
118   /* Step 4: Take the baton, so that the second reader thread does not go ahead
119      too early.  */
120   if (pthread_mutex_lock (&baton))
121     UNEXPECTED (4);
122   /* Step 5: Create the second reader thread.  */
123   if (pthread_create (&reader2, NULL, reader2_func, NULL))
124     UNEXPECTED (5);
125   /* Step 6: Release the baton.  */
126   if (pthread_mutex_unlock (&baton))
127     UNEXPECTED (6);
128   /* Step 7: The writer thread requests the lock.  */
129   if (pthread_rwlock_wrlock (&lock))
130     UNEXPECTED (7);
131   return NULL;
135 main ()
137   reader1 = pthread_self ();
139   /* Step 1: The main thread initializes the lock and the baton.  */
140   if (pthread_rwlock_init (&lock, NULL))
141     UNEXPECTED (1);
142   if (pthread_mutex_init (&baton, NULL))
143     UNEXPECTED (1);
144   /* Step 2: The main thread acquires the lock as a reader.  */
145   if (pthread_rwlock_rdlock (&lock))
146     UNEXPECTED (2);
147   /* Step 3: Create the writer thread.  */
148   if (pthread_create (&writer, NULL, writer_func, NULL))
149     UNEXPECTED (3);
150   /* Job done.  Go to sleep.  */
151   for (;;)
152     {
153       sleep (1);
154     }
156 ]])],
157        [gl_cv_pthread_rwlock_rdlock_prefer_writer=yes],
158        [gl_cv_pthread_rwlock_rdlock_prefer_writer=no],
159        [case "$host_os" in
160                               # Guess no on glibc systems.
161           *-gnu* | gnu*)      gl_cv_pthread_rwlock_rdlock_prefer_writer="guessing no" ;;
162                               # Guess no on musl systems.
163           *-musl* | midipix*) gl_cv_pthread_rwlock_rdlock_prefer_writer="guessing no" ;;
164                               # Guess no on bionic systems.
165           *-android*)         gl_cv_pthread_rwlock_rdlock_prefer_writer="guessing no" ;;
166                               # Guess yes on native Windows with the mingw-w64 winpthreads library.
167                               # Guess no on native Windows with the gnulib windows-rwlock module.
168           mingw* | windows*)  if test "$gl_use_threads" = yes || test "$gl_use_threads" = posix; then
169                                 gl_cv_pthread_rwlock_rdlock_prefer_writer="guessing yes"
170                               else
171                                 gl_cv_pthread_rwlock_rdlock_prefer_writer="guessing no"
172                               fi
173                               ;;
174                               # If we don't know, obey --enable-cross-guesses.
175           *)                  gl_cv_pthread_rwlock_rdlock_prefer_writer="$gl_cross_guess_normal" ;;
176          esac
177        ])
178      LIBS="$saved_LIBS"
179     ])
180   case "$gl_cv_pthread_rwlock_rdlock_prefer_writer" in
181     *yes)
182       AC_DEFINE([HAVE_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER], [1],
183         [Define if the 'pthread_rwlock_rdlock' function prefers a writer to a reader.])
184       ;;
185   esac