On Fri, Jun 21, 2013 at 09:40:32AM -0400, Ed Maste wrote: > On 20 June 2013 20:10, Ben Pfaff <b...@nicira.com> wrote: > > Can you confirm that the autoconf test needs #include <threads.h> but > > the actual program doesn't? It looks funny. > > Yes. It looks like it should be added in the actual program; right > wow we end up with the definition leaking from another header.
OK. _Thread_local is supposed to be a keyword, so it seems weird to #include a header to use it. But <threads.h> is supposed to define a macro 'thread_local' that expands to _Thread_local. It makes a little more sense to me to #include <threads.h> and then use the thread_local macro, so I modified the patch to do that. --8<--------------------------cut here-------------------------->8-- From: Ben Pfaff <b...@nicira.com> Date: Mon, 24 Jun 2013 10:51:24 -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 <threads.h>. This commit also adds a convenience wrapper for the POSIX API, via the DEFINE_PER_THREAD_MALLOCED_DATA macro. Signed-off-by: Ben Pfaff <b...@nicira.com> --- configure.ac | 1 + lib/ovs-thread.h | 204 +++++++++++++++++++++++++++++++++++++++++++++++++++++ m4/openvswitch.m4 | 37 +++++++++- 3 files changed, 241 insertions(+), 1 deletions(-) diff --git a/configure.ac b/configure.ac index a691963..52e8ce6 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 cafeedf..e32d74e 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 <threads.h> 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__(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__(), 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 are no "NAME_set()" or "NAME_set__()" functions. To set the value of + * the per-thread variable, dereference the pointer returned by TYPE_get() or + * TYPE_get__(), e.g. *TYPE_get() = 0. + */ +#if HAVE_THREAD_LOCAL || HAVE___THREAD + +#if HAVE_THREAD_LOCAL +#include <threads.h> +#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__(void) \ + { \ + return &NAME##_var; \ + } \ + \ + static NAME##_type * \ + NAME##_get(void) \ + { \ + return NAME##_get__(); \ + } +#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__(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__(); \ + 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__(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__(), 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__(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__(), 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__(void) \ + { \ + return pthread_getspecific(NAME##_key); \ + } \ + \ + static OVS_UNUSED TYPE \ + NAME##_get(void) \ + { \ + NAME##_init(); \ + return NAME##_get__(); \ + } \ + \ + static TYPE \ + NAME##_set__(TYPE value) \ + { \ + TYPE old_value = NAME##_get__(); \ + pthread_setspecific(NAME##_key, value); \ + return old_value; \ + } \ + \ + static OVS_UNUSED TYPE \ + NAME##_set(TYPE value) \ + { \ + NAME##_init(); \ + return NAME##_set__(value); \ + } + #endif /* ovs-thread.h */ diff --git a/m4/openvswitch.m4 b/m4/openvswitch.m4 index 12c02c0..57c71e0 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 <threads.h>, 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 <threads.h> that supports thread_local], + [ovs_cv_thread_local], + [AC_LINK_IFELSE( + [AC_LANG_PROGRAM([#include <threads.h> +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 <threads.h>.]) + 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]) -- 1.7.2.5 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev