summaryrefslogtreecommitdiff
path: root/tests/test-rwlock1.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test-rwlock1.c')
-rw-r--r--tests/test-rwlock1.c157
1 files changed, 157 insertions, 0 deletions
diff --git a/tests/test-rwlock1.c b/tests/test-rwlock1.c
new file mode 100644
index 0000000..e68e59d
--- /dev/null
+++ b/tests/test-rwlock1.c
@@ -0,0 +1,157 @@
+/* Test of glthread_rwlock_rdlock function.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2005.
+ Inspired by
+ https://github.com/linux-test-project/ltp/blob/master/testcases/open_posix_testsuite/conformance/interfaces/pthread_rwlock_rdlock/2-2.c
+ by Intel Corporation. */
+
+#include <config.h>
+
+#include "glthread/lock.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "glthread/thread.h"
+#include "glthread/yield.h"
+
+/* Verify that in a situation where
+ - an rwlock is taken by a reader and has a writer waiting,
+ - an additional reader requests the lock,
+ - the waiting writer and the requesting reader threads have the same
+ priority,
+ the requesting reader thread gets blocked, so that at some point the
+ waiting writer can acquire the lock.
+ Without such a guarantee, when there a N readers and each of the readers
+ spends more than 1/Nth of the time with the lock held, there is a high
+ probability that the waiting writer will not get the lock in a given finite
+ time, a phenomenon called "writer starvation".
+ Without such a guarantee, applications have a hard time avoiding writer
+ starvation.
+
+ POSIX:2008 makes this requirement only for implementations that support TPS
+ (Thread Priority Scheduling) and only for the scheduling policies SCHED_FIFO
+ and SCHED_RR, see
+ http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_rdlock.html
+ but test verifies the guarantee regardless of TPS and regardless of
+ scheduling policy. */
+
+#define SUCCEED() exit (0)
+#define FAILURE() exit (1)
+#define UNEXPECTED(n) (fprintf (stderr, "Unexpected outcome %d\n", n), abort ())
+
+/* 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 gl_rwlock_t lock;
+static gl_thread_t reader1;
+static gl_thread_t writer;
+static gl_thread_t reader2;
+static gl_thread_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 gl_lock_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 (glthread_lock_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 (glthread_lock_lock (&baton))
+ UNEXPECTED (8);
+ usleep (100000);
+ /* Step 9 omitted. */
+ /* Step 10: Launch a timer, to test whether the next call blocks. */
+ if (glthread_create (&timer, timer_func, NULL))
+ UNEXPECTED (10);
+ /* Step 11: Release the baton. */
+ if (glthread_lock_unlock (&baton))
+ UNEXPECTED (11);
+ /* Step 12: The second reader thread requests the lock. */
+ err = glthread_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 (glthread_lock_lock (&baton))
+ UNEXPECTED (4);
+ /* Step 5: Create the second reader thread. */
+ if (glthread_create (&reader2, reader2_func, NULL))
+ UNEXPECTED (5);
+ /* Step 6: Release the baton. */
+ if (glthread_lock_unlock (&baton))
+ UNEXPECTED (6);
+ /* Step 7: The writer thread requests the lock. */
+ if (glthread_rwlock_wrlock (&lock))
+ UNEXPECTED (7);
+ return NULL;
+}
+
+int
+main ()
+{
+ reader1 = gl_thread_self ();
+
+ /* Step 1: The main thread initializes the lock and the baton. */
+ if (glthread_rwlock_init (&lock))
+ UNEXPECTED (1);
+ if (glthread_lock_init (&baton))
+ UNEXPECTED (1);
+ /* Step 2: The main thread acquires the lock as a reader. */
+ if (glthread_rwlock_rdlock (&lock))
+ UNEXPECTED (2);
+ /* Step 3: Create the writer thread. */
+ if (glthread_create (&writer, writer_func, NULL))
+ UNEXPECTED (3);
+ /* Job done. Go to sleep. */
+ for (;;)
+ {
+ /* In cooperative threads implementations (Pth), give other threads
+ a chance to run. */
+ gl_thread_yield ();
+ sleep (1);
+ }
+}