| /* 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> |