glibc/nptl/tst-mutex10.c
Stefan Liebler 403b4feb22 Fix race in pthread_mutex_lock while promoting to PTHREAD_MUTEX_ELISION_NP [BZ #23275]
The race leads either to pthread_mutex_destroy returning EBUSY
or triggering an assertion (See description in bugzilla).

This patch is fixing the race by ensuring that the elision path is
used in all cases if elision is enabled by the GLIBC_TUNABLES framework.

The __kind variable in struct __pthread_mutex_s is accessed concurrently.
Therefore we are now using the atomic macros.

The new testcase tst-mutex10 is triggering the race on s390x and intel.
Presumably also on power, but I don't have access to a power machine
with lock-elision. At least the code for power is the same as on the other
two architectures.

ChangeLog:

	[BZ #23275]
	* nptl/tst-mutex10.c: New File.
	* nptl/Makefile (tests): Add tst-mutex10.
	(tst-mutex10-ENV): New variable.
	* sysdeps/unix/sysv/linux/s390/force-elision.h: (FORCE_ELISION):
	Ensure that elision path is used if elision is available.
	* sysdeps/unix/sysv/linux/powerpc/force-elision.h (FORCE_ELISION):
	Likewise.
	* sysdeps/unix/sysv/linux/x86/force-elision.h: (FORCE_ELISION):
	Likewise.
	* nptl/pthreadP.h (PTHREAD_MUTEX_TYPE, PTHREAD_MUTEX_TYPE_ELISION)
	(PTHREAD_MUTEX_PSHARED): Use atomic_load_relaxed.
	* nptl/pthread_mutex_consistent.c (pthread_mutex_consistent): Likewise.
	* nptl/pthread_mutex_getprioceiling.c (pthread_mutex_getprioceiling):
	Likewise.
	* nptl/pthread_mutex_lock.c (__pthread_mutex_lock_full)
	(__pthread_mutex_cond_lock_adjust): Likewise.
	* nptl/pthread_mutex_setprioceiling.c (pthread_mutex_setprioceiling):
	Likewise.
	* nptl/pthread_mutex_timedlock.c (__pthread_mutex_timedlock): Likewise.
	* nptl/pthread_mutex_trylock.c (__pthread_mutex_trylock): Likewise.
	* nptl/pthread_mutex_unlock.c (__pthread_mutex_unlock_full): Likewise.
	* sysdeps/nptl/bits/thread-shared-types.h (struct __pthread_mutex_s):
	Add comments.
	* nptl/pthread_mutex_destroy.c (__pthread_mutex_destroy):
	Use atomic_load_relaxed and atomic_store_relaxed.
	* nptl/pthread_mutex_init.c (__pthread_mutex_init):
	Use atomic_store_relaxed.
2018-10-17 12:23:04 +02:00

110 lines
3.2 KiB
C

/* Testing race while enabling lock elision.
Copyright (C) 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 <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <pthread.h>
#include <unistd.h>
#include <getopt.h>
#include <support/support.h>
#include <support/xthread.h>
static pthread_barrier_t barrier;
static pthread_mutex_t mutex;
static long long int iteration_count = 1000000;
static unsigned int thread_count = 3;
static void *
thr_func (void *arg)
{
long long int i;
for (i = 0; i < iteration_count; i++)
{
if ((uintptr_t) arg == 0)
{
xpthread_mutex_destroy (&mutex);
xpthread_mutex_init (&mutex, NULL);
}
xpthread_barrier_wait (&barrier);
/* Test if enabling lock elision works if it is enabled concurrently.
There was a race in FORCE_ELISION macro which leads to either
pthread_mutex_destroy returning EBUSY as the owner was recorded
by pthread_mutex_lock - in "normal mutex" code path - but was not
resetted in pthread_mutex_unlock - in "elision" code path.
Or it leads to the assertion in nptl/pthread_mutex_lock.c:
assert (mutex->__data.__owner == 0);
Please ensure that the test is run with lock elision:
export GLIBC_TUNABLES=glibc.elision.enable=1 */
xpthread_mutex_lock (&mutex);
xpthread_mutex_unlock (&mutex);
xpthread_barrier_wait (&barrier);
}
return NULL;
}
static int
do_test (void)
{
unsigned int i;
printf ("Starting %d threads to run %lld iterations.\n",
thread_count, iteration_count);
pthread_t *threads = xmalloc (thread_count * sizeof (pthread_t));
xpthread_barrier_init (&barrier, NULL, thread_count);
xpthread_mutex_init (&mutex, NULL);
for (i = 0; i < thread_count; i++)
threads[i] = xpthread_create (NULL, thr_func, (void *) (uintptr_t) i);
for (i = 0; i < thread_count; i++)
xpthread_join (threads[i]);
xpthread_barrier_destroy (&barrier);
free (threads);
return EXIT_SUCCESS;
}
#define OPT_ITERATIONS 10000
#define OPT_THREADS 10001
#define CMDLINE_OPTIONS \
{ "iterations", required_argument, NULL, OPT_ITERATIONS }, \
{ "threads", required_argument, NULL, OPT_THREADS },
static void
cmdline_process (int c)
{
long long int arg = strtoll (optarg, NULL, 0);
switch (c)
{
case OPT_ITERATIONS:
if (arg > 0)
iteration_count = arg;
break;
case OPT_THREADS:
if (arg > 0 && arg < 100)
thread_count = arg;
break;
}
}
#define CMDLINE_PROCESS cmdline_process
#define TIMEOUT 50
#include <support/test-driver.c>