@@ -272,7 +272,8 @@ tests = tst-typesizes \
tst-getpid1 tst-getpid2 tst-getpid3 \
tst-setuid3 \
tst-initializers1 $(addprefix tst-initializers1-,c89 gnu89 c99 gnu99) \
- tst-bad-schedattr
+ tst-bad-schedattr \
+ tst-elision1 tst-elision2 tst-elision1b tst-elision2b tst-elision1c tst-elision2c
xtests = tst-setuid1 tst-setuid1-static tst-setuid2 \
tst-mutexpp1 tst-mutexpp6 tst-mutexpp10
test-srcs = tst-oddstacklimit
@@ -469,6 +470,14 @@ tst-umask1-ARGS = $(objpfx)tst-umask1.temp
# resolution. Avoid this race by disabling lazy binding. BZ #11214.
tst-getpid2-ENV = LD_BIND_NOW=1
+# test if disabling elision works through environment arguments
+tst-elision1b-ENV = GLIBC_PTHREAD_MUTEX=none GLIBC_PTHREAD_RWLOCK=none
+tst-elision2b-ENV = GLIBC_PTHREAD_MUTEX=none GLIBC_PTHREAD_RWLOCK=none
+# test if enabling elision works
+# XXX requires env file
+tst-elision1c-ENV = GLIBC_PTHREAD_MUTEX=elision GLIBC_PTHREAD_RWLOCK=elision
+tst-elision2c-ENV = GLIBC_PTHREAD_MUTEX=elision GLIBC_PTHREAD_RWLOCK=elision
+
$(objpfx)tst-atfork2: $(libdl) $(shared-thread-library)
LDFLAGS-tst-atfork2 = -rdynamic
tst-atfork2-ENV = MALLOC_TRACE=$(objpfx)tst-atfork2.mtrace
new file mode 100644
@@ -0,0 +1,264 @@
+/* tst-elision-common: Elision test harness.
+ Copyright (C) 2014 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 <string.h>
+#include <stdlib.h>
+#include "config.h"
+
+#define CPUID_FEATURE_RTM (1U << 11)
+
+static int
+cpu_has_rtm (void)
+{
+ if (__get_cpuid_max (0, NULL) >= 7)
+ {
+ unsigned a, b, c, d;
+
+ __cpuid_count (7, 0, a, b, c, d);
+ if (b & CPUID_FEATURE_RTM)
+ return 1;
+ }
+ return 0;
+}
+
+#define ITER 10
+#define MAXTRY 100
+
+pthread_mutex_t lock;
+
+#ifndef USE_TRYLOCK_ONLY
+static int
+pthread_mutex_timedlock_wrapper(pthread_mutex_t *l)
+{
+ struct timespec wait = { 0, 0 };
+ return pthread_mutex_timedlock (l, &wait);
+}
+#endif
+
+/* Note this test program will fail when single stepped.
+ It also assumes that simple transactions always work. There is no
+ guarantee in the architecture that this is the case. We do some
+ retries to handle random abort cases like interrupts. But it's
+ not fully guaranteed. However when this fails it is somewhat worrying. */
+
+int
+run_mutex (int expected, const char *name, int force)
+{
+ int i;
+ int try = 0;
+ int txn __attribute__((unused));
+ int err;
+
+#ifndef USE_TRYLOCK_ONLY
+ TESTLOCK(lock, pthread_mutex_lock, pthread_mutex_unlock, force);
+ TESTLOCK(lock, pthread_mutex_timedlock_wrapper, pthread_mutex_unlock, force);
+ TESTLOCK(lock, pthread_mutex_trylock, pthread_mutex_unlock, force);
+#else
+ TESTTRYLOCK(lock, pthread_mutex_lock, pthread_mutex_trylock, pthread_mutex_unlock, force);
+#endif
+
+ err = pthread_mutex_destroy (&lock);
+ if (err != 0)
+ {
+ printf ("destroy for %s failed: %d\n", name, err);
+ return 1;
+ }
+ return 0;
+}
+
+static int
+run_mutex_init (int iter, const char *name, int type, int has_type, int force,
+ int elision)
+{
+ pthread_mutexattr_t attr;
+
+ pthread_mutexattr_init (&attr);
+ if (type != -1)
+ pthread_mutexattr_settype (&attr, type);
+
+ pthread_mutex_init (&lock, has_type ? &attr : NULL);
+ return run_mutex (iter, name, force);
+}
+
+static int
+default_elision_enabled (void)
+{
+#ifdef ENABLE_LOCK_ELISION
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+static int
+elision_override (const char *var, int iter)
+{
+ char *s = getenv(var);
+
+ s = getenv(var);
+ if (s && strstr (s, "none"))
+ return 0;
+ if (s && strstr (s, "elision"))
+ return ITER;
+ return iter;
+}
+
+int
+mutex_test (void)
+{
+ int ret = 0;
+ int default_iter = default_elision_enabled () ? ITER : 0;
+
+ default_iter = elision_override ("GLIBC_PTHREAD_MUTEX", default_iter);
+ printf("default_iter %d\n", default_iter);
+
+ lock = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
+ ret += run_mutex (default_iter, "default initializer timed", 0);
+
+#ifdef ADAPTIVE_SUPPORTS_ELISION
+ lock = (pthread_mutex_t) PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP;
+ run_mutex (default_iter, "adaptive initializer default", 0);
+#endif
+
+ ret += run_mutex_init (default_iter, "timed init default", -1, 0, 0, -1);
+ ret += run_mutex_init (0, "normal no elision",
+ PTHREAD_MUTEX_NORMAL, 1, 2, -1);
+
+#ifdef ADAPTIVE_SUPPORTS_ELISION
+ ret += run_mutex_init (default_iter, "adaptive init default",
+ PTHREAD_MUTEX_ADAPTIVE_NP, 1, 0, -1);
+ ret += run_mutex_init (ITER, "adaptive init elision",
+ PTHREAD_MUTEX_ADAPTIVE_NP, 1, 1, 1);
+ ret += run_mutex_init (0, "adaptive init no elision",
+ PTHREAD_MUTEX_ADAPTIVE_NP,
+ 1, 2, 0);
+#endif
+
+ return ret;
+}
+
+pthread_rwlock_t rwlock;
+
+#ifndef USE_TRYLOCK_ONLY
+static int
+pthread_rwlock_timedwrlock_wrapper(pthread_rwlock_t *l)
+{
+ struct timespec wait = { 0, 0 };
+ return pthread_rwlock_timedwrlock (l, &wait);
+}
+
+static int
+pthread_rwlock_timedrdlock_wrapper(pthread_rwlock_t *l)
+{
+ struct timespec wait = { 0, 0 };
+ return pthread_rwlock_timedrdlock (l, &wait);
+}
+#endif
+
+int
+run_rwlock (int expected, const char *name, int force)
+{
+ int i;
+ int try = 0;
+ int txn __attribute__((unused));
+ int err;
+
+#ifndef USE_TRYLOCK_ONLY
+ TESTLOCK(rwlock, pthread_rwlock_rdlock, pthread_rwlock_unlock, force);
+ TESTLOCK(rwlock, pthread_rwlock_wrlock, pthread_rwlock_unlock, force);
+ TESTLOCK(rwlock, pthread_rwlock_rdlock, pthread_rwlock_unlock, force);
+ TESTLOCK(rwlock, pthread_rwlock_tryrdlock, pthread_rwlock_unlock, force);
+ TESTLOCK(rwlock, pthread_rwlock_trywrlock, pthread_rwlock_unlock, force);
+ TESTLOCK(rwlock, pthread_rwlock_timedrdlock_wrapper,
+ pthread_rwlock_unlock, force);
+ TESTLOCK(rwlock, pthread_rwlock_timedwrlock_wrapper,
+ pthread_rwlock_unlock, force);
+#else
+ TESTTRYLOCK(rwlock, pthread_rwlock_wrlock, pthread_rwlock_trywrlock,
+ pthread_rwlock_unlock, force);
+#endif
+
+ err = pthread_rwlock_destroy (&rwlock);
+ if (err != 0)
+ {
+ printf ("pthread_rwlock_destroy for %s failed: %d\n", name, err);
+ return 1;
+ }
+ return 0;
+}
+
+int
+run_rwlock_attr (int iter, const char *name, int type, int el, int force)
+{
+ pthread_rwlockattr_t attr;
+ pthread_rwlockattr_init (&attr);
+ pthread_rwlockattr_setkind_np (&attr, type);
+ pthread_rwlock_init (&rwlock, &attr);
+ return run_rwlock (iter, name, force);
+}
+
+int
+run_rwlock_attr_set (int iter, const char *extra, int flag, int force)
+{
+ char str[100];
+ int ret = 0;
+
+ snprintf(str, sizeof str, "rwlock attr prefer reader %s", extra);
+ ret += run_rwlock_attr (iter, str,
+ PTHREAD_RWLOCK_PREFER_READER_NP, flag, force);
+ snprintf(str, sizeof str, "rwlock attr prefer writer %s", extra);
+ ret += run_rwlock_attr (iter, str,
+ PTHREAD_RWLOCK_PREFER_WRITER_NP, flag, force);
+ snprintf(str, sizeof str, "rwlock attr prefer writer non recursive %s", extra);
+ ret += run_rwlock_attr (iter, str,
+ PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP, flag, force);
+ return ret;
+}
+
+
+int
+rwlock_test (void)
+{
+ int ret = 0;
+ int default_iter = default_elision_enabled () ? ITER : 0;
+
+ default_iter = elision_override ("GLIBC_PTHREAD_RWLOCK", default_iter);
+
+ pthread_rwlock_init (&rwlock, NULL);
+ ret += run_rwlock (default_iter, "rwlock created", 0);
+
+ rwlock = (pthread_rwlock_t)PTHREAD_RWLOCK_INITIALIZER;
+ ret += run_rwlock (default_iter, "rwlock initialized", 0);
+
+ rwlock = (pthread_rwlock_t)PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP;
+ ret += run_rwlock (default_iter, "rwlock initialized writer non recursive", 0);
+
+ rwlock = (pthread_rwlock_t)PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP;
+ ret += run_rwlock (default_iter, "rwlock initialized writer non recursive", 0);
+
+#ifdef PTHREAD_RWLOCK_WRITER_INITIALIZER_NP
+ // XXX includes are missing PTHREAD_RWLOCK_WRITER_INITIALIZER_NP
+ rwlock = (pthread_rwlock_t)PTHREAD_RWLOCK_WRITER_INITIALIZER_NP;
+ ret += run_rwlock (default_iter, "rwlock initialized writer", 0);
+#endif
+
+ ret += run_rwlock_attr_set (default_iter, "", 0, 0);
+
+ return ret;
+}
new file mode 100644
@@ -0,0 +1,133 @@
+/* tst-elision1: Test basic elision success.
+ Copyright (C) 2014 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/>. */
+
+/* To use on other architectures you would need an own version
+ of cpu_has_rtm and _xtest. */
+#if defined(__i386__) || defined(__x86_64__)
+# include <elision-conf.h>
+# include <pthread.h>
+# include <stdlib.h>
+# include <string.h>
+# include <stdio.h>
+# include <hle.h>
+# include <config.h>
+
+int disabled;
+int forced;
+
+int
+check (const char *name, const char *lock, int try, int txn, int max,
+ int override)
+{
+ int should_run = 1;
+
+ if (override == 0)
+ should_run = disabled == 0;
+ else if (override == 1)
+ should_run = 1;
+ else if (override == 2)
+ should_run = 0;
+
+ /* forced noop right now, so not tested. But test if the defaults change. */
+ if (!should_run)
+ {
+ if (txn != 0)
+ {
+ printf ("%s %s transaction run unexpected txn:%d\n", name, lock, txn);
+ return 1;
+ }
+ }
+ else
+ {
+ if (try == max)
+ {
+ printf ("%s %s no transactions when expected\n", name, lock);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+# define TESTLOCK(l, lock, unlock, force)\
+ do \
+ { \
+ txn = 0; \
+ for (i = 0; i < ITER; i++) \
+ { \
+ lock (&l); \
+ if (_xtest ()) \
+ txn++; \
+ unlock (&l); \
+ } \
+ } \
+ while (try++ < MAXTRY && txn != expected); \
+ if (check (name, #lock, try, txn, MAXTRY, force)) \
+ return 1;
+
+# include "tst-elision-common.c"
+
+static void
+set_default (const char *var)
+{
+ char *s = getenv (var);
+
+ if (s != NULL)
+ {
+ if (strstr (s, "elision"))
+ forced = 1;
+ else if (strstr (s, "none"))
+ disabled = 1;
+ }
+ else
+ {
+#ifdef ENABLE_LOCK_ELISION
+ forced = 1;
+#else
+ disabled = 1;
+#endif
+ }
+}
+
+int
+do_test (void)
+{
+ if (cpu_has_rtm () == 0)
+ {
+ printf ("elision test requires RTM capable CPU. not tested\n");
+ return 0;
+ }
+
+ set_default ("GLIBC_PTHREAD_MUTEX");
+ if (mutex_test ())
+ return 1;
+
+ set_default ("GLIBC_PTHREAD_RWLOCK");
+ if (rwlock_test ())
+ return 1;
+
+ return 0;
+}
+#else
+int do_test (void)
+{
+ return 0;
+}
+#endif
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
new file mode 100644
@@ -0,0 +1 @@
+#include "tst-elision1.c"
new file mode 100644
@@ -0,0 +1 @@
+#include "tst-elision1.c"
new file mode 100644
@@ -0,0 +1,84 @@
+/* tst-elision2: Test elided trylock semantics
+ Copyright (C) 2014 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/>. */
+
+/* To use on other architectures you would need an own version of
+ cpu_has_rtm. */
+#if defined(__i386__) || defined(__x86_64__)
+# include <pthread.h>
+# include <stdio.h>
+# include <hle.h>
+# include <cpuid.h>
+
+int check (int success, int force)
+{
+ /* Any nested trylock disabled right now. */
+ return success == 0;
+}
+
+# define TESTTRYLOCK(l, lock, trylock, unlock, force) \
+ do \
+ { \
+ txn = 0; \
+ for (i = 0; i < ITER; i++) \
+ { \
+ lock (&l); \
+ if (trylock (&l) == 0) \
+ { \
+ txn++; \
+ unlock (&l); \
+ } \
+ unlock (&l); \
+ } \
+ } \
+ while (try++ < MAXTRY && txn != ITER); \
+ if (!check (txn == ITER, force)) \
+ { \
+ printf ("%s %s nested trylock check failed txn:%d iter:%d force:%d\n", \
+ name, #lock, txn, ITER, force); \
+ return 1; \
+ }
+
+# define USE_TRYLOCK_ONLY 1
+# include "tst-elision-common.c"
+
+int
+do_test (void)
+{
+ if (cpu_has_rtm () == 0)
+ {
+ printf ("elision test requires RTM capable CPU. not tested\n");
+ return 0;
+ }
+
+ if (mutex_test ())
+ return 1;
+
+ if (rwlock_test ())
+ return 1;
+
+ return 0;
+}
+#else
+int do_test (void)
+{
+ return 0;
+}
+#endif
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
new file mode 100644
@@ -0,0 +1 @@
+#include "tst-elision2.c"
new file mode 100644
@@ -0,0 +1 @@
+#include "tst-elision2.c"