Skip to content

Commit

Permalink
ovs-thread: Add per-thread data support.
Browse files Browse the repository at this point in the history
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 <[email protected]>
Acked-by: Ethan Jackson <[email protected]>
  • Loading branch information
blp committed Jun 25, 2013
1 parent ec68790 commit c22095a
Show file tree
Hide file tree
Showing 3 changed files with 241 additions and 1 deletion.
1 change: 1 addition & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -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])
Expand Down
204 changes: 204 additions & 0 deletions lib/ovs-thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_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 <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_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 */
37 changes: 36 additions & 1 deletion m4/openvswitch.m4
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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])

0 comments on commit c22095a

Please sign in to comment.