X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fovs-thread.h;h=0a5a4337293b1100c9c77a52d4c8a3ab3cda653d;hb=c22095a5fb199723cbccc7156cc345260fe99731;hp=cafeedf0243fbc765ccdc172c5d2c4044c76c349;hpb=ec68790f6d522aae2a3d5d97f1974915a76e23cf;p=sliver-openvswitch.git 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 */