From: Paolo Bonzini <pbonz...@redhat.com> Fast TLS is not available on some platforms, but it is always nice to use it. This wrapper implementation falls back to pthread_get/setspecific on POSIX systems that lack __thread, but uses the dynamic linker's TLS support on Linux and Windows.
The user shall call alloc_foo() in every thread that needs to access the variable---exactly once and before any access. foo is the name of the variable as passed to DECLARE_TLS and DEFINE_TLS. Then, get_foo() will return the address of the variable. It is guaranteed to remain the same across the lifetime of a thread, so you can cache it. Signed-off-by: Paolo Bonzini <pbonz...@redhat.com> --- configure | 21 +++++++ include/qemu/tls.h | 139 +++++++++++++++++++++++++++++++++++++++++++++++ tests/Makefile | 3 + tests/test-tls.c | 87 +++++++++++++++++++++++++++++ util/qemu-thread-win32.c | 17 ++++++ 5 files changed, 267 insertions(+) create mode 100644 include/qemu/tls.h create mode 100644 tests/test-tls.c diff --git a/configure b/configure index 1654413..c679386 100755 --- a/configure +++ b/configure @@ -284,6 +284,7 @@ fi ar="${AR-${cross_prefix}ar}" as="${AS-${cross_prefix}as}" cpp="${CPP-$cc -E}" +nm="${NM-${cross_prefix}nm}" objcopy="${OBJCOPY-${cross_prefix}objcopy}" ld="${LD-${cross_prefix}ld}" libtool="${LIBTOOL-${cross_prefix}libtool}" @@ -3157,6 +3158,22 @@ if test "$trace_backend" = "dtrace"; then fi ########################################## +# check for TLS runtime + +# Some versions of mingw include the "magic" definitions that make +# TLS work, some don't. Check for it. + +if test "$mingw32" = yes; then + cat > $TMPC << EOF +int main(void) {} +EOF + compile_prog "" "" + if $nm $TMPE | grep _tls_used > /dev/null 2>&1; then + mingw32_tls_runtime=yes + fi +fi + +########################################## # check and set a backend for coroutine # We prefer ucontext, but it's not always possible. The fallback @@ -3615,6 +3632,9 @@ if test "$mingw32" = "yes" ; then version_micro=0 echo "CONFIG_FILEVERSION=$version_major,$version_minor,$version_subminor,$version_micro" >> $config_host_mak echo "CONFIG_PRODUCTVERSION=$version_major,$version_minor,$version_subminor,$version_micro" >> $config_host_mak + if test "$mingw32_tls_runtime" = yes; then + echo "CONFIG_MINGW32_TLS_RUNTIME=y" >> $config_host_mak + fi else echo "CONFIG_POSIX=y" >> $config_host_mak fi @@ -4040,6 +4060,7 @@ echo "OBJCC=$objcc" >> $config_host_mak echo "AR=$ar" >> $config_host_mak echo "AS=$as" >> $config_host_mak echo "CPP=$cpp" >> $config_host_mak +echo "NM=$nm" >> $config_host_mak echo "OBJCOPY=$objcopy" >> $config_host_mak echo "LD=$ld" >> $config_host_mak echo "WINDRES=$windres" >> $config_host_mak diff --git a/include/qemu/tls.h b/include/qemu/tls.h new file mode 100644 index 0000000..7fd0632 --- /dev/null +++ b/include/qemu/tls.h @@ -0,0 +1,139 @@ +/* + * Abstraction layer for defining and using TLS variables + * + * Copyright (c) 2011, 2013 Red Hat, Inc + * Copyright (c) 2011 Linaro Limited + * + * Authors: + * Paolo Bonzini <pbonz...@redhat.com> + * Peter Maydell <peter.mayd...@linaro.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef QEMU_TLS_H +#define QEMU_TLS_H + +#if defined __linux__ +#define DECLARE_TLS(type, x) \ +extern __thread typeof(type) x; \ + \ +static inline typeof(type) *get_##x(void) \ +{ \ + return &x; \ +} \ + \ +static inline typeof(type) *alloc_##x(void) \ +{ \ + return &x; \ +} \ + \ +extern int dummy_##__LINE__ + +#define DEFINE_TLS(type, x) \ +__thread typeof(type) x + +#elif defined CONFIG_POSIX +typedef struct QEMUTLSValue { + pthread_key_t k; + pthread_once_t o; +} QEMUTLSValue; + +#define DECLARE_TLS(type, x) \ +extern QEMUTLSValue x; \ +extern void init_##x(void); \ + \ +static inline typeof(type) *get_##x(void) \ +{ \ + return pthread_getspecific(x.k); \ +} \ + \ +static inline typeof(type) *alloc_##x(void) \ +{ \ + void *datum = g_malloc0(sizeof(type)); \ + pthread_once(&x.o, init_##x); \ + pthread_setspecific(x.k, datum); \ + return datum; \ +} \ + \ +extern int dummy_##__LINE__ + +#define DEFINE_TLS(type, x) \ +void init_##x(void) { \ + pthread_key_create(&x.k, g_free); \ +} \ + \ +QEMUTLSValue x = { .o = PTHREAD_ONCE_INIT } + +#elif defined CONFIG_WIN32 + +/* The initial contents of TLS variables are placed in the .tls section. + * The linker takes all section starting with ".tls$", sorts them and puts + * the contents in a single ".tls" section. qemu-thread-win32.c defines + * special symbols in .tls$000 and .tls$ZZZ that represent the beginning + * and end of TLS memory. The linker and run-time library then cooperate + * to copy memory between those symbols in the TLS area of new threads. + * + * _tls_index holds the number of our module. The executable should be + * zero, DLLs are numbered 1 and up. The loader fills it in for us. + * + * Thus, Teb->ThreadLocalStoragePointer[_tls_index] is the base of + * the TLS segment for this (thread, module) pair. Each segment has + * the same layout as this module's .tls segment and is initialized + * with the content of the .tls segment; 0 is the _tls_start variable. + * So, get_##x passes us the offset of the passed variable relative to + * _tls_start, and we return that same offset plus the base of segment. + */ + +typedef struct _TEB { + NT_TIB NtTib; + void *EnvironmentPointer; + void *x[3]; + char **ThreadLocalStoragePointer; +} TEB, *PTEB; + +extern int _tls_index; +extern int _tls_start; + +static inline void *tls_var(size_t offset) +{ + PTEB Teb = NtCurrentTeb(); + return (char *)(Teb->ThreadLocalStoragePointer[_tls_index]) + offset; +} + +#define DECLARE_TLS(type, x) \ +extern typeof(type) tls_##x __attribute__((section(".tls$QEMU"))); \ + \ +static inline typeof(type) *get_##x(void) \ +{ \ + return tls_var((ULONG_PTR)&(tls_##x) - (ULONG_PTR)&_tls_start); \ +} \ + \ +static inline typeof(type) *alloc_##x(void) \ +{ \ + typeof(type) *addr = get_##x(); \ + memset((void *)addr, 0, sizeof(type)); \ + return addr; \ +} \ + \ +extern int dummy_##__LINE__ + +#define DEFINE_TLS(type, x) \ +typeof(type) tls_##x __attribute__((section(".tls$QEMU"))) + +#else +#error No TLS abstraction available on this platform +#endif + +#endif diff --git a/tests/Makefile b/tests/Makefile index c107489..1f5156a 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -42,6 +42,8 @@ check-unit-y += tests/test-xbzrle$(EXESUF) gcov-files-test-xbzrle-y = xbzrle.c check-unit-y += tests/test-cutils$(EXESUF) gcov-files-test-cutils-y += util/cutils.c +check-unit-y += tests/test-tls$(EXESUF) +gcov-files-test-tls-y += check-unit-y += tests/test-mul64$(EXESUF) gcov-files-test-mul64-y = util/host-utils.c @@ -98,6 +100,7 @@ tests/test-hbitmap$(EXESUF): tests/test-hbitmap.o libqemuutil.a libqemustub.a tests/test-x86-cpuid$(EXESUF): tests/test-x86-cpuid.o tests/test-xbzrle$(EXESUF): tests/test-xbzrle.o xbzrle.o page_cache.o libqemuutil.a tests/test-cutils$(EXESUF): tests/test-cutils.o util/cutils.o +tests/test-tls$(EXESUF): tests/test-tls.o libqemuutil.a tests/test-qapi-types.c tests/test-qapi-types.h :\ $(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py diff --git a/tests/test-tls.c b/tests/test-tls.c new file mode 100644 index 0000000..54e981d --- /dev/null +++ b/tests/test-tls.c @@ -0,0 +1,87 @@ +/* + * Unit-tests for TLS wrappers + * + * Copyright (C) 2013 Red Hat Inc. + * + * Authors: + * Paolo Bonzini <pbonz...@redhat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <glib.h> +#include <errno.h> +#include <string.h> + +#include "qemu-common.h" +#include "qemu/atomic.h" +#include "qemu/thread.h" +#include "qemu/tls.h" + +DECLARE_TLS(volatile long long, cnt); +DEFINE_TLS(volatile long long, cnt); + +#define NUM_THREADS 10 + +int stop; + +static void *test_thread(void *arg) +{ + volatile long long *p_cnt = alloc_cnt(); + volatile long long **p_ret = arg; + long long exp = 0; + + g_assert(get_cnt() == p_cnt); + *p_ret = p_cnt; + g_assert(*p_cnt == 0); + while (atomic_mb_read(&stop) == 0) { + exp++; + (*p_cnt)++; + g_assert(*get_cnt() == exp); + } + + return NULL; +} + +static void test_tls(void) +{ + volatile long long *addr[NUM_THREADS]; + QemuThread t[NUM_THREADS]; + int i; + + for (i = 0; i < NUM_THREADS; i++) { + qemu_thread_create(&t[i], test_thread, &addr[i], QEMU_THREAD_JOINABLE); + } + g_usleep(1000000); + atomic_mb_set(&stop, 1); + for (i = 0; i < NUM_THREADS; i++) { + qemu_thread_join(&t[i]); + } + for (i = 1; i < NUM_THREADS; i++) { + g_assert(addr[i] != addr[i - 1]); + } +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/tls", test_tls); + return g_test_run(); +} diff --git a/util/qemu-thread-win32.c b/util/qemu-thread-win32.c index 517878d..f75e404 100644 --- a/util/qemu-thread-win32.c +++ b/util/qemu-thread-win32.c @@ -16,6 +16,23 @@ #include <assert.h> #include <limits.h> +/* TLS support. Some versions of mingw32 provide it, others do not. */ + +#ifndef CONFIG_MINGW32_TLS_RUNTIME +int __attribute__((section(".tls$AAA"))) _tls_start = 0; +int __attribute__((section(".tls$ZZZ"))) _tls_end = 0; +int _tls_index = 0; + +const IMAGE_TLS_DIRECTORY _tls_used __attribute__((used, section(".rdata$T"))) = { + (ULONG)(ULONG_PTR) &_tls_start, /* start of tls data */ + (ULONG)(ULONG_PTR) &_tls_end, /* end of tls data */ + (ULONG)(ULONG_PTR) &_tls_index, /* address of tls_index */ + (ULONG) 0, /* pointer to callbacks */ + (ULONG) 0, /* size of tls zero fill */ + (ULONG) 0 /* characteristics */ +}; +#endif + static void error_exit(int err, const char *msg) { char *pstr; -- 1.8.1.4