|
Packit |
06dd63 |
/* Test of glthread_rwlock_rdlock function.
|
|
Packit |
06dd63 |
Copyright (C) 2017-2019 Free Software Foundation, Inc.
|
|
Packit |
06dd63 |
|
|
Packit |
06dd63 |
This program is free software: you can redistribute it and/or modify
|
|
Packit |
06dd63 |
it under the terms of the GNU General Public License as published by
|
|
Packit |
06dd63 |
the Free Software Foundation; either version 3 of the License, or
|
|
Packit |
06dd63 |
(at your option) any later version.
|
|
Packit |
06dd63 |
|
|
Packit |
06dd63 |
This program is distributed in the hope that it will be useful,
|
|
Packit |
06dd63 |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
06dd63 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
06dd63 |
GNU General Public License for more details.
|
|
Packit |
06dd63 |
|
|
Packit |
06dd63 |
You should have received a copy of the GNU General Public License
|
|
Packit |
06dd63 |
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
|
Packit |
06dd63 |
|
|
Packit |
06dd63 |
/* Written by Bruno Haible <bruno@clisp.org>, 2005.
|
|
Packit |
06dd63 |
Inspired by
|
|
Packit |
06dd63 |
https://github.com/linux-test-project/ltp/blob/master/testcases/open_posix_testsuite/conformance/interfaces/pthread_rwlock_rdlock/2-2.c
|
|
Packit |
06dd63 |
by Intel Corporation. */
|
|
Packit |
06dd63 |
|
|
Packit |
06dd63 |
#include <config.h>
|
|
Packit |
06dd63 |
|
|
Packit |
06dd63 |
#include "glthread/lock.h"
|
|
Packit |
06dd63 |
|
|
Packit |
06dd63 |
#include <errno.h>
|
|
Packit |
06dd63 |
#include <stdio.h>
|
|
Packit |
06dd63 |
#include <stdlib.h>
|
|
Packit |
06dd63 |
#include <unistd.h>
|
|
Packit |
06dd63 |
|
|
Packit |
06dd63 |
#include "glthread/thread.h"
|
|
Packit |
06dd63 |
#include "glthread/yield.h"
|
|
Packit |
06dd63 |
|
|
Packit |
06dd63 |
/* Verify that in a situation where
|
|
Packit |
06dd63 |
- an rwlock is taken by a reader and has a writer waiting,
|
|
Packit |
06dd63 |
- an additional reader requests the lock,
|
|
Packit |
06dd63 |
- the waiting writer and the requesting reader threads have the same
|
|
Packit |
06dd63 |
priority,
|
|
Packit |
06dd63 |
the requesting reader thread gets blocked, so that at some point the
|
|
Packit |
06dd63 |
waiting writer can acquire the lock.
|
|
Packit |
06dd63 |
Without such a guarantee, when there a N readers and each of the readers
|
|
Packit |
06dd63 |
spends more than 1/Nth of the time with the lock held, there is a high
|
|
Packit |
06dd63 |
probability that the waiting writer will not get the lock in a given finite
|
|
Packit |
06dd63 |
time, a phenomenon called "writer starvation".
|
|
Packit |
06dd63 |
Without such a guarantee, applications have a hard time avoiding writer
|
|
Packit |
06dd63 |
starvation.
|
|
Packit |
06dd63 |
|
|
Packit |
06dd63 |
POSIX:2008 makes this requirement only for implementations that support TPS
|
|
Packit |
06dd63 |
(Thread Priority Scheduling) and only for the scheduling policies SCHED_FIFO
|
|
Packit |
06dd63 |
and SCHED_RR, see
|
|
Packit |
06dd63 |
http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_rdlock.html
|
|
Packit |
06dd63 |
but test verifies the guarantee regardless of TPS and regardless of
|
|
Packit |
06dd63 |
scheduling policy. */
|
|
Packit |
06dd63 |
|
|
Packit |
06dd63 |
#define SUCCEED() exit (0)
|
|
Packit |
06dd63 |
#define FAILURE() exit (1)
|
|
Packit |
06dd63 |
#define UNEXPECTED(n) (fprintf (stderr, "Unexpected outcome %d\n", n), abort ())
|
|
Packit |
06dd63 |
|
|
Packit |
06dd63 |
/* The main thread creates the waiting writer and the requesting reader threads
|
|
Packit |
06dd63 |
in the default way; this guarantees that they have the same priority.
|
|
Packit |
06dd63 |
We can reuse the main thread as first reader thread. */
|
|
Packit |
06dd63 |
|
|
Packit |
06dd63 |
static gl_rwlock_t lock;
|
|
Packit |
06dd63 |
static gl_thread_t reader1;
|
|
Packit |
06dd63 |
static gl_thread_t writer;
|
|
Packit |
06dd63 |
static gl_thread_t reader2;
|
|
Packit |
06dd63 |
static gl_thread_t timer;
|
|
Packit |
06dd63 |
/* Used to pass control from writer to reader2 and from reader2 to timer,
|
|
Packit |
06dd63 |
as in a relay race.
|
|
Packit |
06dd63 |
Passing control from one running thread to another running thread
|
|
Packit |
06dd63 |
is most likely faster than to create the second thread. */
|
|
Packit |
06dd63 |
static gl_lock_t baton;
|
|
Packit |
06dd63 |
|
|
Packit |
06dd63 |
static void *
|
|
Packit |
06dd63 |
timer_func (void *ignored)
|
|
Packit |
06dd63 |
{
|
|
Packit |
06dd63 |
/* Step 13 (can be before or after step 12):
|
|
Packit |
06dd63 |
The timer thread takes the baton, then waits a moment to make sure
|
|
Packit |
06dd63 |
it can tell whether the second reader thread is blocked at step 12. */
|
|
Packit |
06dd63 |
if (glthread_lock_lock (&baton))
|
|
Packit |
06dd63 |
UNEXPECTED (13);
|
|
Packit |
06dd63 |
usleep (100000);
|
|
Packit |
06dd63 |
/* By the time we get here, it's clear that the second reader thread is
|
|
Packit |
06dd63 |
blocked at step 12. This is the desired behaviour. */
|
|
Packit |
06dd63 |
SUCCEED ();
|
|
Packit |
06dd63 |
}
|
|
Packit |
06dd63 |
|
|
Packit |
06dd63 |
static void *
|
|
Packit |
06dd63 |
reader2_func (void *ignored)
|
|
Packit |
06dd63 |
{
|
|
Packit |
06dd63 |
int err;
|
|
Packit |
06dd63 |
|
|
Packit |
06dd63 |
/* Step 8 (can be before or after step 7):
|
|
Packit |
06dd63 |
The second reader thread takes the baton, then waits a moment to make sure
|
|
Packit |
06dd63 |
the writer thread has reached step 7. */
|
|
Packit |
06dd63 |
if (glthread_lock_lock (&baton))
|
|
Packit |
06dd63 |
UNEXPECTED (8);
|
|
Packit |
06dd63 |
usleep (100000);
|
|
Packit |
06dd63 |
/* Step 9 omitted. */
|
|
Packit |
06dd63 |
/* Step 10: Launch a timer, to test whether the next call blocks. */
|
|
Packit |
06dd63 |
if (glthread_create (&timer, timer_func, NULL))
|
|
Packit |
06dd63 |
UNEXPECTED (10);
|
|
Packit |
06dd63 |
/* Step 11: Release the baton. */
|
|
Packit |
06dd63 |
if (glthread_lock_unlock (&baton))
|
|
Packit |
06dd63 |
UNEXPECTED (11);
|
|
Packit |
06dd63 |
/* Step 12: The second reader thread requests the lock. */
|
|
Packit |
06dd63 |
err = glthread_rwlock_rdlock (&lock);
|
|
Packit |
06dd63 |
if (err == 0)
|
|
Packit |
06dd63 |
FAILURE ();
|
|
Packit |
06dd63 |
else
|
|
Packit |
06dd63 |
UNEXPECTED (12);
|
|
Packit |
06dd63 |
}
|
|
Packit |
06dd63 |
|
|
Packit |
06dd63 |
static void *
|
|
Packit |
06dd63 |
writer_func (void *ignored)
|
|
Packit |
06dd63 |
{
|
|
Packit |
06dd63 |
/* Step 4: Take the baton, so that the second reader thread does not go ahead
|
|
Packit |
06dd63 |
too early. */
|
|
Packit |
06dd63 |
if (glthread_lock_lock (&baton))
|
|
Packit |
06dd63 |
UNEXPECTED (4);
|
|
Packit |
06dd63 |
/* Step 5: Create the second reader thread. */
|
|
Packit |
06dd63 |
if (glthread_create (&reader2, reader2_func, NULL))
|
|
Packit |
06dd63 |
UNEXPECTED (5);
|
|
Packit |
06dd63 |
/* Step 6: Release the baton. */
|
|
Packit |
06dd63 |
if (glthread_lock_unlock (&baton))
|
|
Packit |
06dd63 |
UNEXPECTED (6);
|
|
Packit |
06dd63 |
/* Step 7: The writer thread requests the lock. */
|
|
Packit |
06dd63 |
if (glthread_rwlock_wrlock (&lock))
|
|
Packit |
06dd63 |
UNEXPECTED (7);
|
|
Packit |
06dd63 |
return NULL;
|
|
Packit |
06dd63 |
}
|
|
Packit |
06dd63 |
|
|
Packit |
06dd63 |
int
|
|
Packit |
06dd63 |
main ()
|
|
Packit |
06dd63 |
{
|
|
Packit |
06dd63 |
reader1 = gl_thread_self ();
|
|
Packit |
06dd63 |
|
|
Packit |
06dd63 |
/* Step 1: The main thread initializes the lock and the baton. */
|
|
Packit |
06dd63 |
if (glthread_rwlock_init (&lock))
|
|
Packit |
06dd63 |
UNEXPECTED (1);
|
|
Packit |
06dd63 |
if (glthread_lock_init (&baton))
|
|
Packit |
06dd63 |
UNEXPECTED (1);
|
|
Packit |
06dd63 |
/* Step 2: The main thread acquires the lock as a reader. */
|
|
Packit |
06dd63 |
if (glthread_rwlock_rdlock (&lock))
|
|
Packit |
06dd63 |
UNEXPECTED (2);
|
|
Packit |
06dd63 |
/* Step 3: Create the writer thread. */
|
|
Packit |
06dd63 |
if (glthread_create (&writer, writer_func, NULL))
|
|
Packit |
06dd63 |
UNEXPECTED (3);
|
|
Packit |
06dd63 |
/* Job done. Go to sleep. */
|
|
Packit |
06dd63 |
for (;;)
|
|
Packit |
06dd63 |
{
|
|
Packit |
06dd63 |
/* In cooperative threads implementations (Pth), give other threads
|
|
Packit |
06dd63 |
a chance to run. */
|
|
Packit |
06dd63 |
gl_thread_yield ();
|
|
Packit |
06dd63 |
sleep (1);
|
|
Packit |
06dd63 |
}
|
|
Packit |
06dd63 |
}
|