Skip to content

Commit c22095a

Browse files
committed
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 <blp@nicira.com> Acked-by: Ethan Jackson <ethan@nicira.com>
1 parent ec68790 commit c22095a

3 files changed

Lines changed: 241 additions & 1 deletion

File tree

configure.ac

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ OVS_CHECK_XENSERVER_VERSION
8080
OVS_CHECK_GROFF
8181
OVS_CHECK_GNU_MAKE
8282
OVS_CHECK_CACHE_TIME
83+
OVS_CHECK_TLS
8384

8485
OVS_ENABLE_OPTION([-Wall])
8586
OVS_ENABLE_OPTION([-Wno-sign-compare])

lib/ovs-thread.h

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,5 +85,209 @@ void xpthread_cond_wait(pthread_cond_t *, pthread_mutex_t *mutex)
8585
void xpthread_key_create(pthread_key_t *, void (*destructor)(void *));
8686

8787
void xpthread_create(pthread_t *, pthread_attr_t *, void *(*)(void *), void *);
88+
89+
/* Per-thread data.
90+
*
91+
* Multiple forms of per-thread data exist, each with its own pluses and
92+
* minuses:
93+
*
94+
* - POSIX per-thread data via pthread_key_t is portable to any pthreads
95+
* implementation, and allows a destructor function to be defined. It
96+
* only (directly) supports per-thread pointers, which are always
97+
* initialized to NULL. It requires once-only allocation of a
98+
* pthread_key_t value. It is relatively slow.
99+
*
100+
* - The thread_local feature newly defined in C11 <threads.h> works with
101+
* any data type and initializer, and it is fast. thread_local does not
102+
* require once-only initialization like pthread_key_t. C11 does not
103+
* define what happens if one attempts to access a thread_local object
104+
* from a thread other than the one to which that object belongs. There
105+
* is no provision to call a user-specified destructor when a thread
106+
* ends.
107+
*
108+
* - The __thread keyword is a GCC extension similar to thread_local but
109+
* with a longer history. __thread is not portable to every GCC version
110+
* or environment. __thread does not restrict the use of a thread-local
111+
* object outside its own thread.
112+
*
113+
* Here's a handy summary:
114+
*
115+
* pthread_key_t thread_local __thread
116+
* ------------- ------------ -------------
117+
* portability high low medium
118+
* speed low high high
119+
* supports destructors? yes no no
120+
* needs key allocation? yes no no
121+
* arbitrary initializer? no yes yes
122+
* cross-thread access? yes no yes
123+
*/
124+
125+
/* DEFINE_PER_THREAD_DATA(TYPE, NAME, INITIALIZER).
126+
*
127+
* One should prefer to use POSIX per-thread data, via pthread_key_t, when its
128+
* performance is acceptable, because of its portability (see the table above).
129+
* This macro is an alternatives that takes advantage of thread_local (and
130+
* __thread), for its performance, when it is available, and falls back to
131+
* POSIX per-thread data otherwise.
132+
*
133+
* Defines per-thread variable NAME with the given TYPE, initialized to
134+
* INITIALIZER (which must be valid as an initializer for a variable with
135+
* static lifetime).
136+
*
137+
* The public interface to the variable is:
138+
*
139+
* TYPE *NAME_get(void)
140+
* TYPE *NAME_get_unsafe(void)
141+
*
142+
* Returns the address of this thread's instance of NAME.
143+
*
144+
* Use NAME_get() in a context where this might be the first use of the
145+
* per-thread variable in the program. Use NAME_get_unsafe(), which
146+
* avoids a conditional test and is thus slightly faster, in a context
147+
* where one knows that NAME_get() has already been called previously.
148+
*
149+
* There is no "NAME_set()" (or "NAME_set_unsafe()") function. To set the
150+
* value of the per-thread variable, dereference the pointer returned by
151+
* TYPE_get() or TYPE_get_unsafe(), e.g. *TYPE_get() = 0.
152+
*/
153+
#if HAVE_THREAD_LOCAL || HAVE___THREAD
154+
155+
#if HAVE_THREAD_LOCAL
156+
#include <threads.h>
157+
#elif HAVE___THREAD
158+
#define thread_local __thread
159+
#else
160+
#error
161+
#endif
162+
163+
#define DEFINE_PER_THREAD_DATA(TYPE, NAME, ...) \
164+
typedef TYPE NAME##_type; \
165+
static thread_local NAME##_type NAME##_var = __VA_ARGS__; \
166+
\
167+
static NAME##_type * \
168+
NAME##_get_unsafe(void) \
169+
{ \
170+
return &NAME##_var; \
171+
} \
172+
\
173+
static NAME##_type * \
174+
NAME##_get(void) \
175+
{ \
176+
return NAME##_get_unsafe(); \
177+
}
178+
#else /* no C implementation support for thread-local storage */
179+
#define DEFINE_PER_THREAD_DATA(TYPE, NAME, ...) \
180+
typedef TYPE NAME##_type; \
181+
static pthread_key_t NAME##_key; \
182+
\
183+
static NAME##_type * \
184+
NAME##_get_unsafe(void) \
185+
{ \
186+
return pthread_getspecific(NAME##_key); \
187+
} \
188+
\
189+
static void \
190+
NAME##_once_init(void) \
191+
{ \
192+
if (pthread_key_create(&NAME##_key, free)) { \
193+
abort(); \
194+
} \
195+
} \
196+
\
197+
static NAME##_type * \
198+
NAME##_get(void) \
199+
{ \
200+
static pthread_once_t once = PTHREAD_ONCE_INIT; \
201+
NAME##_type *value; \
202+
\
203+
pthread_once(&once, NAME##_once_init); \
204+
value = NAME##_get_unsafe(); \
205+
if (!value) { \
206+
static const NAME##_type initial_value = __VA_ARGS__; \
207+
\
208+
value = xmalloc(sizeof *value); \
209+
*value = initial_value; \
210+
pthread_setspecific(NAME##_key, value); \
211+
} \
212+
return value; \
213+
}
214+
#endif
215+
216+
/* DEFINE_PER_THREAD_MALLOCED_DATA(TYPE, NAME).
217+
*
218+
* This is a simple wrapper around POSIX per-thread data primitives. It
219+
* defines per-thread variable NAME with the given TYPE, which must be a
220+
* pointer type. In each thread, the per-thread variable is initialized to
221+
* NULL. When a thread terminates, the variable is freed with free().
222+
*
223+
* The public interface to the variable is:
224+
*
225+
* TYPE NAME_get(void)
226+
* TYPE NAME_get_unsafe(void)
227+
*
228+
* Returns the value of per-thread variable NAME in this thread.
229+
*
230+
* Use NAME_get() in a context where this might be the first use of the
231+
* per-thread variable in the program. Use NAME_get_unsafe(), which
232+
* avoids a conditional test and is thus slightly faster, in a context
233+
* where one knows that NAME_get() has already been called previously.
234+
*
235+
* TYPE NAME_set(TYPE new_value)
236+
* TYPE NAME_set_unsafe(TYPE new_value)
237+
*
238+
* Sets the value of per-thread variable NAME to 'new_value' in this
239+
* thread, and returns its previous value.
240+
*
241+
* Use NAME_set() in a context where this might be the first use of the
242+
* per-thread variable in the program. Use NAME_set_unsafe(), which
243+
* avoids a conditional test and is thus slightly faster, in a context
244+
* where one knows that NAME_set() has already been called previously.
245+
*/
246+
#define DEFINE_PER_THREAD_MALLOCED_DATA(TYPE, NAME) \
247+
static pthread_key_t NAME##_key; \
248+
\
249+
static void \
250+
NAME##_once_init(void) \
251+
{ \
252+
if (pthread_key_create(&NAME##_key, free)) { \
253+
abort(); \
254+
} \
255+
} \
256+
\
257+
static void \
258+
NAME##_init(void) \
259+
{ \
260+
static pthread_once_t once = PTHREAD_ONCE_INIT; \
261+
pthread_once(&once, NAME##_once_init); \
262+
} \
263+
\
264+
static TYPE \
265+
NAME##_get_unsafe(void) \
266+
{ \
267+
return pthread_getspecific(NAME##_key); \
268+
} \
269+
\
270+
static OVS_UNUSED TYPE \
271+
NAME##_get(void) \
272+
{ \
273+
NAME##_init(); \
274+
return NAME##_get_unsafe(); \
275+
} \
276+
\
277+
static TYPE \
278+
NAME##_set_unsafe(TYPE value) \
279+
{ \
280+
TYPE old_value = NAME##_get_unsafe(); \
281+
pthread_setspecific(NAME##_key, value); \
282+
return old_value; \
283+
} \
284+
\
285+
static OVS_UNUSED TYPE \
286+
NAME##_set(TYPE value) \
287+
{ \
288+
NAME##_init(); \
289+
return NAME##_set_unsafe(value); \
290+
}
291+
88292

89293
#endif /* ovs-thread.h */

m4/openvswitch.m4

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# -*- autoconf -*-
22

3-
# Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
3+
# Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
44
#
55
# Licensed under the Apache License, Version 2.0 (the "License");
66
# you may not use this file except in compliance with the License.
@@ -390,3 +390,38 @@ AC_DEFUN([OVS_CHECK_GROFF],
390390
ovs_cv_groff=no
391391
fi])
392392
AM_CONDITIONAL([HAVE_GROFF], [test "$ovs_cv_groff" = yes])])
393+
394+
dnl Checks for thread-local storage support.
395+
dnl
396+
dnl Checks whether the compiler and linker support the C11
397+
dnl thread_local macro from <threads.h>, and if so defines
398+
dnl HAVE_THREAD_LOCAL. If not, checks whether the compiler and linker
399+
dnl support the GCC __thread extension, and if so defines
400+
dnl HAVE___THREAD.
401+
AC_DEFUN([OVS_CHECK_TLS],
402+
[AC_CACHE_CHECK(
403+
[whether $CC has <threads.h> that supports thread_local],
404+
[ovs_cv_thread_local],
405+
[AC_LINK_IFELSE(
406+
[AC_LANG_PROGRAM([#include <threads.h>
407+
static thread_local int var;], [return var;])],
408+
[ovs_cv_thread_local=yes],
409+
[ovs_cv_thread_local=no])])
410+
if test $ovs_cv_thread_local = yes; then
411+
AC_DEFINE([HAVE_THREAD_LOCAL], [1],
412+
[Define to 1 if the C compiler and linker supports the C11
413+
thread_local matcro defined in <threads.h>.])
414+
else
415+
AC_CACHE_CHECK(
416+
[whether $CC supports __thread],
417+
[ovs_cv___thread],
418+
[AC_LINK_IFELSE(
419+
[AC_LANG_PROGRAM([static __thread int var;], [return var;])],
420+
[ovs_cv___thread=yes],
421+
[ovs_cv___thread=no])])
422+
if test $ovs_cv___thread = yes; then
423+
AC_DEFINE([HAVE___THREAD], [1],
424+
[Define to 1 if the C compiler and linker supports the
425+
GCC __thread extenions.])
426+
fi
427+
fi])

0 commit comments

Comments
 (0)