blob: ae5706f528dfe9a277d80f7f0f37ce5410ab7f7d [file] [log] [blame]
/* Test the interaction of fork and robust mutexes.
Copyright (C) 2017-2018 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <support/check.h>
#include <support/test-driver.h>
#include <support/xthread.h>
#include <support/xunistd.h>
#include <sys/mman.h>
/* Data shared between processes. */
struct shared
{
pthread_mutex_t parent_mutex;
pthread_mutex_t child_mutex;
};
/* These flags control which mutex settings are enabled in the parent
and child (separately). */
enum mutex_bits
{
mutex_pshared = 1,
mutex_robust = 2,
mutex_pi = 4,
mutex_check = 8,
/* All bits combined. */
mutex_all_bits = 15,
};
static void
mutex_init (pthread_mutex_t *mutex, int bits)
{
pthread_mutexattr_t attr;
xpthread_mutexattr_init (&attr);
if (bits & mutex_pshared)
xpthread_mutexattr_setpshared (&attr, PTHREAD_PROCESS_SHARED);
if (bits & mutex_robust)
xpthread_mutexattr_setrobust (&attr, PTHREAD_MUTEX_ROBUST);
if (bits & mutex_pi)
xpthread_mutexattr_setprotocol (&attr, PTHREAD_PRIO_INHERIT);
if (bits & mutex_check)
xpthread_mutexattr_settype (&attr, PTHREAD_MUTEX_ERRORCHECK);
xpthread_mutex_init (mutex, &attr);
xpthread_mutexattr_destroy (&attr);
}
static void
one_test (int parent_bits, int child_bits, int nonshared_bits,
bool lock_nonshared, bool lock_child)
{
struct shared *shared = xmmap (NULL, sizeof (*shared),
PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_SHARED, -1);
mutex_init (&shared->parent_mutex, parent_bits);
mutex_init (&shared->child_mutex, child_bits);
/* Acquire the parent mutex in the parent. */
xpthread_mutex_lock (&shared->parent_mutex);
pthread_mutex_t nonshared_mutex;
mutex_init (&nonshared_mutex, nonshared_bits);
if (lock_nonshared)
xpthread_mutex_lock (&nonshared_mutex);
pid_t pid = xfork ();
if (pid == 0)
{
/* Child process. */
if (lock_child)
xpthread_mutex_lock (&shared->child_mutex);
else
xmunmap (shared, sizeof (*shared));
if (lock_nonshared)
/* Reinitialize the non-shared mutex if it was locked in the
parent. */
mutex_init (&nonshared_mutex, nonshared_bits);
xpthread_mutex_lock (&nonshared_mutex);
/* For robust mutexes, the _exit call will perform the unlock
instead. */
if (lock_child && !(child_bits & mutex_robust))
xpthread_mutex_unlock (&shared->child_mutex);
_exit (0);
}
/* Parent process. */
{
int status;
xwaitpid (pid, &status, 0);
TEST_VERIFY (status == 0);
}
if (parent_bits & mutex_check)
/* Test for expected self-deadlock. This is only possible to
detect if the mutex is error-checking. */
TEST_VERIFY_EXIT (pthread_mutex_lock (&shared->parent_mutex) == EDEADLK);
pid = xfork ();
if (pid == 0)
{
/* Child process. We can perform some checks only if we are
dealing with process-shared mutexes. */
if (parent_bits & mutex_pshared)
/* It must not be possible to acquire the parent mutex.
NB: This check touches a mutex which has been acquired in
the parent at fork time, so it might be deemed undefined
behavior, pending the resolution of Austin Groups issue
1112. */
TEST_VERIFY_EXIT (pthread_mutex_trylock (&shared->parent_mutex)
== EBUSY);
if (lock_child && (child_bits & mutex_robust))
{
if (!(child_bits & mutex_pshared))
/* No further tests possible. */
_exit (0);
TEST_VERIFY_EXIT (pthread_mutex_lock (&shared->child_mutex)
== EOWNERDEAD);
xpthread_mutex_consistent (&shared->child_mutex);
}
else
/* We did not acquire the lock in the first child process, or
we unlocked the mutex again because the mutex is not a
robust mutex. */
xpthread_mutex_lock (&shared->child_mutex);
xpthread_mutex_unlock (&shared->child_mutex);
_exit (0);
}
/* Parent process. */
{
int status;
xwaitpid (pid, &status, 0);
TEST_VERIFY (status == 0);
}
if (lock_nonshared)
xpthread_mutex_unlock (&nonshared_mutex);
xpthread_mutex_unlock (&shared->parent_mutex);
xpthread_mutex_destroy (&shared->parent_mutex);
xpthread_mutex_destroy (&shared->child_mutex);
xpthread_mutex_destroy (&nonshared_mutex);
xmunmap (shared, sizeof (*shared));
}
static int
do_test (void)
{
for (int parent_bits = 0; parent_bits <= mutex_all_bits; ++parent_bits)
for (int child_bits = 0; child_bits <= mutex_all_bits; ++child_bits)
for (int nonshared_bits = 0; nonshared_bits <= mutex_all_bits;
++nonshared_bits)
for (int lock_nonshared = 0; lock_nonshared < 2; ++lock_nonshared)
for (int lock_child = 0; lock_child < 2; ++lock_child)
{
if (test_verbose)
printf ("info: parent_bits=0x%x child_bits=0x%x"
" nonshared_bits=0x%x%s%s\n",
parent_bits, child_bits, nonshared_bits,
lock_nonshared ? " lock_nonshared" : "",
lock_child ? " lock_child" : "");
one_test (parent_bits, child_bits, nonshared_bits,
lock_nonshared, lock_child);
}
return 0;
}
#define TIMEOUT 100
#include <support/test-driver.c>