I don't really like the names of the functions "NAME_get" and "NAME_get__" could we come up with something that's a bit more specific about the difference between them? Maybe get_init and get? I'm not sure what would be best . . .
Acked-by: Ethan Jackson <et...@nicira.com> On Mon, Jun 24, 2013 at 10:51 AM, Ben Pfaff <b...@nicira.com> wrote: > 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 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev