From c22095a5fb199723cbccc7156cc345260fe99731 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Tue, 25 Jun 2013 13:50:26 -0700 Subject: [PATCH] ovs-thread: Add per-thread data support. POSIX defines a portable pthread_key_t API for per-thread data. GCC and C11 have two different forms of per-thread data that are generally faster than the POSIX API, where they are available. This commit adds a macro-based wrapper, DEFINE_PER_THREAD_DATA, that takes advantage of these features where they are available and falls back to the POSIX API otherwise. The Clang compiler implements C11 thread_local in its . This commit also adds a convenience wrapper for the POSIX API, via the DEFINE_PER_THREAD_MALLOCED_DATA macro. Signed-off-by: Ben Pfaff Acked-by: Ethan Jackson --- configure.ac | 1 + lib/ovs-thread.h | 204 ++++++++++++++++++++++++++++++++++++++++++++++ m4/openvswitch.m4 | 37 ++++++++- 3 files changed, 241 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index a691963c0..52e8ce657 100644 --- a/configure.ac +++ b/configure.ac @@ -80,6 +80,7 @@ OVS_CHECK_XENSERVER_VERSION OVS_CHECK_GROFF OVS_CHECK_GNU_MAKE OVS_CHECK_CACHE_TIME +OVS_CHECK_TLS OVS_ENABLE_OPTION([-Wall]) OVS_ENABLE_OPTION([-Wno-sign-compare]) diff --git a/lib/ovs-thread.h b/lib/ovs-thread.h index cafeedf02..0a5a43372 100644 --- a/lib/ovs-thread.h +++ b/lib/ovs-thread.h @@ -85,5 +85,209 @@ void xpthread_cond_wait(pthread_cond_t *, pthread_mutex_t *mutex) void xpthread_key_create(pthread_key_t *, void (*destructor)(void *)); void xpthread_create(pthread_t *, pthread_attr_t *, void *(*)(void *), void *); + +/* Per-thread data. + * + * Multiple forms of per-thread data exist, each with its own pluses and + * minuses: + * + * - POSIX per-thread data via pthread_key_t is portable to any pthreads + * implementation, and allows a destructor function to be defined. It + * only (directly) supports per-thread pointers, which are always + * initialized to NULL. It requires once-only allocation of a + * pthread_key_t value. It is relatively slow. + * + * - The thread_local feature newly defined in C11 works with + * any data type and initializer, and it is fast. thread_local does not + * require once-only initialization like pthread_key_t. C11 does not + * define what happens if one attempts to access a thread_local object + * from a thread other than the one to which that object belongs. There + * is no provision to call a user-specified destructor when a thread + * ends. + * + * - The __thread keyword is a GCC extension similar to thread_local but + * with a longer history. __thread is not portable to every GCC version + * or environment. __thread does not restrict the use of a thread-local + * object outside its own thread. + * + * Here's a handy summary: + * + * pthread_key_t thread_local __thread + * ------------- ------------ ------------- + * portability high low medium + * speed low high high + * supports destructors? yes no no + * needs key allocation? yes no no + * arbitrary initializer? no yes yes + * cross-thread access? yes no yes + */ + +/* DEFINE_PER_THREAD_DATA(TYPE, NAME, INITIALIZER). + * + * One should prefer to use POSIX per-thread data, via pthread_key_t, when its + * performance is acceptable, because of its portability (see the table above). + * This macro is an alternatives that takes advantage of thread_local (and + * __thread), for its performance, when it is available, and falls back to + * POSIX per-thread data otherwise. + * + * Defines per-thread variable NAME with the given TYPE, initialized to + * INITIALIZER (which must be valid as an initializer for a variable with + * static lifetime). + * + * The public interface to the variable is: + * + * TYPE *NAME_get(void) + * TYPE *NAME_get_unsafe(void) + * + * Returns the address of this thread's instance of NAME. + * + * Use NAME_get() in a context where this might be the first use of the + * per-thread variable in the program. Use NAME_get_unsafe(), which + * avoids a conditional test and is thus slightly faster, in a context + * where one knows that NAME_get() has already been called previously. + * + * There is no "NAME_set()" (or "NAME_set_unsafe()") function. To set the + * value of the per-thread variable, dereference the pointer returned by + * TYPE_get() or TYPE_get_unsafe(), e.g. *TYPE_get() = 0. + */ +#if HAVE_THREAD_LOCAL || HAVE___THREAD + +#if HAVE_THREAD_LOCAL +#include +#elif HAVE___THREAD +#define thread_local __thread +#else +#error +#endif + +#define DEFINE_PER_THREAD_DATA(TYPE, NAME, ...) \ + typedef TYPE NAME##_type; \ + static thread_local NAME##_type NAME##_var = __VA_ARGS__; \ + \ + static NAME##_type * \ + NAME##_get_unsafe(void) \ + { \ + return &NAME##_var; \ + } \ + \ + static NAME##_type * \ + NAME##_get(void) \ + { \ + return NAME##_get_unsafe(); \ + } +#else /* no C implementation support for thread-local storage */ +#define DEFINE_PER_THREAD_DATA(TYPE, NAME, ...) \ + typedef TYPE NAME##_type; \ + static pthread_key_t NAME##_key; \ + \ + static NAME##_type * \ + NAME##_get_unsafe(void) \ + { \ + return pthread_getspecific(NAME##_key); \ + } \ + \ + static void \ + NAME##_once_init(void) \ + { \ + if (pthread_key_create(&NAME##_key, free)) { \ + abort(); \ + } \ + } \ + \ + static NAME##_type * \ + NAME##_get(void) \ + { \ + static pthread_once_t once = PTHREAD_ONCE_INIT; \ + NAME##_type *value; \ + \ + pthread_once(&once, NAME##_once_init); \ + value = NAME##_get_unsafe(); \ + if (!value) { \ + static const NAME##_type initial_value = __VA_ARGS__; \ + \ + value = xmalloc(sizeof *value); \ + *value = initial_value; \ + pthread_setspecific(NAME##_key, value); \ + } \ + return value; \ + } +#endif + +/* DEFINE_PER_THREAD_MALLOCED_DATA(TYPE, NAME). + * + * This is a simple wrapper around POSIX per-thread data primitives. It + * defines per-thread variable NAME with the given TYPE, which must be a + * pointer type. In each thread, the per-thread variable is initialized to + * NULL. When a thread terminates, the variable is freed with free(). + * + * The public interface to the variable is: + * + * TYPE NAME_get(void) + * TYPE NAME_get_unsafe(void) + * + * Returns the value of per-thread variable NAME in this thread. + * + * Use NAME_get() in a context where this might be the first use of the + * per-thread variable in the program. Use NAME_get_unsafe(), which + * avoids a conditional test and is thus slightly faster, in a context + * where one knows that NAME_get() has already been called previously. + * + * TYPE NAME_set(TYPE new_value) + * TYPE NAME_set_unsafe(TYPE new_value) + * + * Sets the value of per-thread variable NAME to 'new_value' in this + * thread, and returns its previous value. + * + * Use NAME_set() in a context where this might be the first use of the + * per-thread variable in the program. Use NAME_set_unsafe(), which + * avoids a conditional test and is thus slightly faster, in a context + * where one knows that NAME_set() has already been called previously. + */ +#define DEFINE_PER_THREAD_MALLOCED_DATA(TYPE, NAME) \ + static pthread_key_t NAME##_key; \ + \ + static void \ + NAME##_once_init(void) \ + { \ + if (pthread_key_create(&NAME##_key, free)) { \ + abort(); \ + } \ + } \ + \ + static void \ + NAME##_init(void) \ + { \ + static pthread_once_t once = PTHREAD_ONCE_INIT; \ + pthread_once(&once, NAME##_once_init); \ + } \ + \ + static TYPE \ + NAME##_get_unsafe(void) \ + { \ + return pthread_getspecific(NAME##_key); \ + } \ + \ + static OVS_UNUSED TYPE \ + NAME##_get(void) \ + { \ + NAME##_init(); \ + return NAME##_get_unsafe(); \ + } \ + \ + static TYPE \ + NAME##_set_unsafe(TYPE value) \ + { \ + TYPE old_value = NAME##_get_unsafe(); \ + pthread_setspecific(NAME##_key, value); \ + return old_value; \ + } \ + \ + static OVS_UNUSED TYPE \ + NAME##_set(TYPE value) \ + { \ + NAME##_init(); \ + return NAME##_set_unsafe(value); \ + } + #endif /* ovs-thread.h */ diff --git a/m4/openvswitch.m4 b/m4/openvswitch.m4 index 12c02c08e..57c71e029 100644 --- a/m4/openvswitch.m4 +++ b/m4/openvswitch.m4 @@ -1,6 +1,6 @@ # -*- autoconf -*- -# Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc. +# Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -390,3 +390,38 @@ AC_DEFUN([OVS_CHECK_GROFF], ovs_cv_groff=no fi]) AM_CONDITIONAL([HAVE_GROFF], [test "$ovs_cv_groff" = yes])]) + +dnl Checks for thread-local storage support. +dnl +dnl Checks whether the compiler and linker support the C11 +dnl thread_local macro from , and if so defines +dnl HAVE_THREAD_LOCAL. If not, checks whether the compiler and linker +dnl support the GCC __thread extension, and if so defines +dnl HAVE___THREAD. +AC_DEFUN([OVS_CHECK_TLS], + [AC_CACHE_CHECK( + [whether $CC has that supports thread_local], + [ovs_cv_thread_local], + [AC_LINK_IFELSE( + [AC_LANG_PROGRAM([#include +static thread_local int var;], [return var;])], + [ovs_cv_thread_local=yes], + [ovs_cv_thread_local=no])]) + if test $ovs_cv_thread_local = yes; then + AC_DEFINE([HAVE_THREAD_LOCAL], [1], + [Define to 1 if the C compiler and linker supports the C11 + thread_local matcro defined in .]) + else + AC_CACHE_CHECK( + [whether $CC supports __thread], + [ovs_cv___thread], + [AC_LINK_IFELSE( + [AC_LANG_PROGRAM([static __thread int var;], [return var;])], + [ovs_cv___thread=yes], + [ovs_cv___thread=no])]) + if test $ovs_cv___thread = yes; then + AC_DEFINE([HAVE___THREAD], [1], + [Define to 1 if the C compiler and linker supports the + GCC __thread extenions.]) + fi + fi]) -- 2.43.0