From: Ben Pfaff Date: Fri, 28 Jun 2013 22:54:40 +0000 (-0700) Subject: ovs-atomic: New library for atomic operations. X-Git-Tag: sliver-openvswitch-1.10.90-3~6^2~4 X-Git-Url: http://git.onelab.eu/?p=sliver-openvswitch.git;a=commitdiff_plain;h=31a3fc6e3e9ce68d8bfebf65150d9455b9334dda ovs-atomic: New library for atomic operations. This library should prove useful for the threading changes coming up. The following commit introduces one (very simple) user. Signed-off-by: Ben Pfaff Acked-by: Ethan Jackson --- diff --git a/configure.ac b/configure.ac index 52e8ce657..78ac593af 100644 --- a/configure.ac +++ b/configure.ac @@ -64,7 +64,7 @@ AC_CHECK_MEMBERS([struct stat.st_mtim.tv_nsec, struct stat.st_mtimensec], [], [], [[#include ]]) AC_CHECK_MEMBERS([struct ifreq.ifr_flagshigh], [], [], [[#include ]]) AC_CHECK_FUNCS([mlockall strnlen getloadavg statvfs getmntent_r]) -AC_CHECK_HEADERS([mntent.h sys/statvfs.h linux/types.h linux/if_ether.h]) +AC_CHECK_HEADERS([mntent.h sys/statvfs.h linux/types.h linux/if_ether.h stdatomic.h]) AC_CHECK_HEADERS([net/if_mib.h], [], [], [[#include #include ]]) @@ -81,6 +81,10 @@ OVS_CHECK_GROFF OVS_CHECK_GNU_MAKE OVS_CHECK_CACHE_TIME OVS_CHECK_TLS +OVS_CHECK_ATOMIC_ALWAYS_LOCK_FREE(1) +OVS_CHECK_ATOMIC_ALWAYS_LOCK_FREE(2) +OVS_CHECK_ATOMIC_ALWAYS_LOCK_FREE(4) +OVS_CHECK_ATOMIC_ALWAYS_LOCK_FREE(8) OVS_ENABLE_OPTION([-Wall]) OVS_ENABLE_OPTION([-Wno-sign-compare]) diff --git a/lib/automake.mk b/lib/automake.mk index 73234bd03..cdff71673 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -121,6 +121,13 @@ lib_libopenvswitch_a_SOURCES = \ lib/ofp-version-opt.c \ lib/ofpbuf.c \ lib/ofpbuf.h \ + lib/ovs-atomic-c11.h \ + lib/ovs-atomic-gcc4+.c \ + lib/ovs-atomic-gcc4+.h \ + lib/ovs-atomic-gcc4.7+.h \ + lib/ovs-atomic-pthreads.c \ + lib/ovs-atomic-pthreads.h \ + lib/ovs-atomic.h \ lib/ovs-thread.c \ lib/ovs-thread.h \ lib/ovsdb-data.c \ diff --git a/lib/ovs-atomic-c11.h b/lib/ovs-atomic-c11.h new file mode 100644 index 000000000..9dc687c0d --- /dev/null +++ b/lib/ovs-atomic-c11.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 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. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This header implements atomic operation primitives on compilers that + * have built-in support for C11 */ +#ifndef IN_OVS_ATOMIC_H +#error "This header should only be included indirectly via ovs-atomic.h." +#endif + +#include + +/* Nonstandard atomic types. */ +typedef _Atomic(uint8_t) atomic_uint8_t; +typedef _Atomic(uint16_t) atomic_uint16_t; +typedef _Atomic(uint32_t) atomic_uint32_t; +typedef _Atomic(uint64_t) atomic_uint64_t; + +typedef _Atomic(int8_t) atomic_int8_t; +typedef _Atomic(int16_t) atomic_int16_t; +typedef _Atomic(int32_t) atomic_int32_t; +typedef _Atomic(int64_t) atomic_int64_t; + +#define atomic_read(SRC, DST) \ + atomic_read_explicit(SRC, DST, memory_order_seq_cst) +#define atomic_read_explicit(SRC, DST, ORDER) \ + (*(DST) = atomic_load_explicit(SRC, ORDER), \ + (void) 0) + +#define atomic_add(RMW, ARG, ORIG) \ + atomic_add_explicit(RMW, ARG, ORIG, memory_order_seq_cst) +#define atomic_sub(RMW, ARG, ORIG) \ + atomic_sub_explicit(RMW, ARG, ORIG, memory_order_seq_cst) +#define atomic_or(RMW, ARG, ORIG) \ + atomic_or_explicit(RMW, ARG, ORIG, memory_order_seq_cst) +#define atomic_xor(RMW, ARG, ORIG) \ + atomic_xor_explicit(RMW, ARG, ORIG, memory_order_seq_cst) +#define atomic_and(RMW, ARG, ORIG) \ + atomic_and_explicit(RMW, ARG, ORIG, memory_order_seq_cst) + +#define atomic_add_explicit(RMW, ARG, ORIG, ORDER) \ + (*(ORIG) = atomic_fetch_add_explicit(RMW, ARG, ORDER), (void) 0) +#define atomic_sub_explicit(RMW, ARG, ORIG, ORDER) \ + (*(ORIG) = atomic_fetch_sub_explicit(RMW, ARG, ORDER), (void) 0) +#define atomic_or_explicit(RMW, ARG, ORIG, ORDER) \ + (*(ORIG) = atomic_fetch_or_explicit(RMW, ARG, ORDER), (void) 0) +#define atomic_xor_explicit(RMW, ARG, ORIG, ORDER) \ + (*(ORIG) = atomic_fetch_xor_explicit(RMW, ARG, ORDER), (void) 0) +#define atomic_and_explicit(RMW, ARG, ORIG, ORDER) \ + (*(ORIG) = atomic_fetch_and_explicit(RMW, ARG, ORDER), (void) 0) diff --git a/lib/ovs-atomic-gcc4+.c b/lib/ovs-atomic-gcc4+.c new file mode 100644 index 000000000..aeff84579 --- /dev/null +++ b/lib/ovs-atomic-gcc4+.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 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. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "ovs-atomic.h" +#include "ovs-thread.h" + +#if OVS_ATOMIC_GCC4P_IMPL +static pthread_mutex_t mutex = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER; + +#define DEFINE_LOCKED_OP(TYPE, NAME, OPERATOR) \ + TYPE##_t \ + locked_##TYPE##_##NAME(struct locked_##TYPE *u, TYPE##_t arg) \ + { \ + TYPE##_t old_value; \ + \ + xpthread_mutex_lock(&mutex); \ + old_value = u->value; \ + u->value OPERATOR arg; \ + xpthread_mutex_unlock(&mutex); \ + \ + return old_value; \ + } + +#define DEFINE_LOCKED_TYPE(TYPE) \ + TYPE##_t \ + locked_##TYPE##_load(const struct locked_##TYPE *u) \ + { \ + TYPE##_t value; \ + \ + xpthread_mutex_lock(&mutex); \ + value = u->value; \ + xpthread_mutex_unlock(&mutex); \ + \ + return value; \ + } \ + \ + void \ + locked_##TYPE##_store(struct locked_##TYPE *u, TYPE##_t value) \ + { \ + xpthread_mutex_lock(&mutex); \ + u->value = value; \ + xpthread_mutex_unlock(&mutex); \ + } \ + DEFINE_LOCKED_OP(TYPE, add, +=); \ + DEFINE_LOCKED_OP(TYPE, sub, -=); \ + DEFINE_LOCKED_OP(TYPE, or, |=); \ + DEFINE_LOCKED_OP(TYPE, xor, ^=); \ + DEFINE_LOCKED_OP(TYPE, and, &=) + +DEFINE_LOCKED_TYPE(uint64); +DEFINE_LOCKED_TYPE(int64); + +#endif /* OVS_ATOMIC_GCC4P_IMPL */ diff --git a/lib/ovs-atomic-gcc4+.h b/lib/ovs-atomic-gcc4+.h new file mode 100644 index 000000000..b8649ede9 --- /dev/null +++ b/lib/ovs-atomic-gcc4+.h @@ -0,0 +1,265 @@ +/* + * Copyright (c) 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. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This header implements atomic operation primitives on GCC 4.x. */ +#ifndef IN_OVS_ATOMIC_H +#error "This header should only be included indirectly via ovs-atomic.h." +#endif + +#define OVS_ATOMIC_GCC4P_IMPL 1 + +#define DEFINE_LOCKLESS_ATOMIC(TYPE, NAME) typedef struct { TYPE value; } NAME + +#define ATOMIC_BOOL_LOCK_FREE 2 +DEFINE_LOCKLESS_ATOMIC(bool, atomic_bool); + +#define ATOMIC_CHAR_LOCK_FREE 2 +DEFINE_LOCKLESS_ATOMIC(char, atomic_char); +DEFINE_LOCKLESS_ATOMIC(signed char, atomic_schar); +DEFINE_LOCKLESS_ATOMIC(unsigned char, atomic_uchar); + +#define ATOMIC_SHORT_LOCK_FREE 2 +DEFINE_LOCKLESS_ATOMIC(short, atomic_short); +DEFINE_LOCKLESS_ATOMIC(unsigned short, atomic_ushort); + +#define ATOMIC_INT_LOCK_FREE 2 +DEFINE_LOCKLESS_ATOMIC(int, atomic_int); +DEFINE_LOCKLESS_ATOMIC(unsigned int, atomic_uint); + +#if ULONG_MAX <= UINTPTR_MAX + #define ATOMIC_LONG_LOCK_FREE 2 + DEFINE_LOCKLESS_ATOMIC(long, atomic_long); + DEFINE_LOCKLESS_ATOMIC(unsigned long, atomic_ulong); +#elif ULONG_MAX == UINT64_MAX + #define ATOMIC_LONG_LOCK_FREE 0 + typedef struct locked_int64 atomic_long; + typedef struct locked_uint64 atomic_ulong; +#else + #error "not implemented" +#endif + +#if ULLONG_MAX <= UINTPTR_MAX + #define ATOMIC_LLONG_LOCK_FREE 2 + DEFINE_LOCKLESS_ATOMIC(long long, atomic_llong); + DEFINE_LOCKLESS_ATOMIC(unsigned long long, atomic_ullong); +#elif ULLONG_MAX == UINT64_MAX + #define ATOMIC_LLONG_LOCK_FREE 0 + typedef struct locked_int64 atomic_llong; + typedef struct locked_uint64 atomic_ullong; +#else + #error "not implemented" +#endif + +#if SIZE_MAX <= UINTPTR_MAX + DEFINE_LOCKLESS_ATOMIC(size_t, atomic_size_t); + DEFINE_LOCKLESS_ATOMIC(ptrdiff_t, atomic_ptrdiff_t); +#elif SIZE_MAX == UINT64_MAX + typedef struct locked_uint64 atomic_size_t; + typedef struct locked_int64 atomic_ptrdiff_t; +#else + #error "not implemented" +#endif + +#if UINTMAX_MAX <= UINTPTR_MAX + DEFINE_LOCKLESS_ATOMIC(intmax_t, atomic_intmax_t); + DEFINE_LOCKLESS_ATOMIC(uintmax_t, atomic_uintmax_t); +#elif UINTMAX_MAX == UINT64_MAX + typedef struct locked_int64 atomic_intmax_t; + typedef struct locked_uint64 atomic_uintmax_t; +#else + #error "not implemented" +#endif + +#define ATOMIC_POINTER_LOCK_FREE 2 +DEFINE_LOCKLESS_ATOMIC(intptr_t, atomic_intptr_t); +DEFINE_LOCKLESS_ATOMIC(uintptr_t, atomic_uintptr_t); + +/* Nonstandard atomic types. */ +DEFINE_LOCKLESS_ATOMIC(uint8_t, atomic_uint8_t); +DEFINE_LOCKLESS_ATOMIC(uint16_t, atomic_uint16_t); +DEFINE_LOCKLESS_ATOMIC(uint32_t, atomic_uint32_t); +DEFINE_LOCKLESS_ATOMIC(int8_t, atomic_int8_t); +DEFINE_LOCKLESS_ATOMIC(int16_t, atomic_int16_t); +DEFINE_LOCKLESS_ATOMIC(int32_t, atomic_int32_t); +#if UINT64_MAX <= UINTPTR_MAX + DEFINE_LOCKLESS_ATOMIC(uint64_t, atomic_uint64_t); + DEFINE_LOCKLESS_ATOMIC(int64_t, atomic_int64_t); +#else + typedef struct locked_uint64 atomic_uint64_t; + typedef struct locked_int64 atomic_int64_t; +#endif + +typedef enum { + memory_order_relaxed, + memory_order_consume, + memory_order_acquire, + memory_order_release, + memory_order_acq_rel, + memory_order_seq_cst +} memory_order; + +/* locked_uint64. */ + +#define IF_LOCKED_UINT64(OBJECT, THEN, ELSE) \ + __builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(OBJECT), struct locked_uint64), \ + (THEN), (ELSE)) +#define AS_LOCKED_UINT64(OBJECT) ((struct locked_uint64 *) (OBJECT)) +#define AS_UINT64(OBJECT) ((uint64_t *) (OBJECT)) +struct locked_uint64 { + uint64_t value; +}; + +uint64_t locked_uint64_load(const struct locked_uint64 *); +void locked_uint64_store(struct locked_uint64 *, uint64_t); +uint64_t locked_uint64_add(struct locked_uint64 *, uint64_t arg); +uint64_t locked_uint64_sub(struct locked_uint64 *, uint64_t arg); +uint64_t locked_uint64_or(struct locked_uint64 *, uint64_t arg); +uint64_t locked_uint64_xor(struct locked_uint64 *, uint64_t arg); +uint64_t locked_uint64_and(struct locked_uint64 *, uint64_t arg); + +#define IF_LOCKED_INT64(OBJECT, THEN, ELSE) \ + __builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(OBJECT), struct locked_int64), \ + (THEN), (ELSE)) +#define AS_LOCKED_INT64(OBJECT) ((struct locked_int64 *) (OBJECT)) +#define AS_INT64(OBJECT) ((int64_t *) (OBJECT)) +struct locked_int64 { + int64_t value; +}; +int64_t locked_int64_load(const struct locked_int64 *); +void locked_int64_store(struct locked_int64 *, int64_t); +int64_t locked_int64_add(struct locked_int64 *, int64_t arg); +int64_t locked_int64_sub(struct locked_int64 *, int64_t arg); +int64_t locked_int64_or(struct locked_int64 *, int64_t arg); +int64_t locked_int64_xor(struct locked_int64 *, int64_t arg); +int64_t locked_int64_and(struct locked_int64 *, int64_t arg); + +#define ATOMIC_VAR_INIT(VALUE) { .value = (VALUE) } +#define atomic_init(OBJECT, VALUE) ((OBJECT)->value = (VALUE), (void) 0) + +static inline void +atomic_thread_fence(memory_order order) +{ + if (order != memory_order_relaxed) { + __sync_synchronize(); + } +} + +static inline void +atomic_thread_fence_if_seq_cst(memory_order order) +{ + if (order == memory_order_seq_cst) { + __sync_synchronize(); + } +} + +static inline void +atomic_signal_fence(memory_order order OVS_UNUSED) +{ + if (order != memory_order_relaxed) { + asm volatile("" : : : "memory"); + } +} + +#define ATOMIC_SWITCH(OBJECT, LOCKLESS_CASE, \ + LOCKED_UINT64_CASE, LOCKED_INT64_CASE) \ + IF_LOCKED_UINT64(OBJECT, LOCKED_UINT64_CASE, \ + IF_LOCKED_INT64(OBJECT, LOCKED_INT64_CASE, \ + LOCKLESS_CASE)) + +#define atomic_is_lock_free(OBJ) \ + ((void) (OBJ)->value, \ + ATOMIC_SWITCH(OBJ, true, false, false)) + +#define atomic_store(DST, SRC) \ + atomic_store_explicit(DST, SRC, memory_order_seq_cst) +#define atomic_store_explicit(DST, SRC, ORDER) \ + (ATOMIC_SWITCH(DST, \ + (atomic_thread_fence(ORDER), \ + (DST)->value = (SRC), \ + atomic_thread_fence_if_seq_cst(ORDER)), \ + locked_uint64_store(AS_LOCKED_UINT64(DST), SRC), \ + locked_int64_store(AS_LOCKED_INT64(DST), SRC)), \ + (void) 0) + +#define atomic_read(SRC, DST) \ + atomic_read_explicit(SRC, DST, memory_order_seq_cst) +#define atomic_read_explicit(SRC, DST, ORDER) \ + (ATOMIC_SWITCH(SRC, \ + (atomic_thread_fence_if_seq_cst(ORDER), \ + (*DST) = (SRC)->value, \ + atomic_thread_fence(ORDER)), \ + *(DST) = locked_uint64_load(AS_LOCKED_UINT64(SRC)), \ + *(DST) = locked_int64_load(AS_LOCKED_INT64(SRC))), \ + (void) 0) + +#define atomic_op__(RMW, OP, ARG, ORIG) \ + (ATOMIC_SWITCH(RMW, \ + *(ORIG) = __sync_fetch_and_##OP(&(RMW)->value, ARG), \ + *(ORIG) = locked_uint64_##OP(AS_LOCKED_UINT64(RMW), ARG), \ + *(ORIG) = locked_int64_##OP(AS_LOCKED_INT64(RMW), ARG)), \ + (void) 0) + +#define atomic_add(RMW, ARG, ORIG) atomic_op__(RMW, add, ARG, ORIG) +#define atomic_sub(RMW, ARG, ORIG) atomic_op__(RMW, sub, ARG, ORIG) +#define atomic_or( RMW, ARG, ORIG) atomic_op__(RMW, or, ARG, ORIG) +#define atomic_xor(RMW, ARG, ORIG) atomic_op__(RMW, xor, ARG, ORIG) +#define atomic_and(RMW, ARG, ORIG) atomic_op__(RMW, and, ARG, ORIG) + +#define atomic_add_explicit(RMW, OPERAND, ORIG, ORDER) \ + ((void) (ORDER), atomic_add(RMW, OPERAND, ORIG)) +#define atomic_sub_explicit(RMW, OPERAND, ORIG, ORDER) \ + ((void) (ORDER), atomic_sub(RMW, OPERAND, ORIG)) +#define atomic_or_explicit(RMW, OPERAND, ORIG, ORDER) \ + ((void) (ORDER), atomic_or(RMW, OPERAND, ORIG)) +#define atomic_xor_explicit(RMW, OPERAND, ORIG, ORDER) \ + ((void) (ORDER), atomic_xor(RMW, OPERAND, ORIG)) +#define atomic_and_explicit(RMW, OPERAND, ORIG, ORDER) \ + ((void) (ORDER), atomic_and(RMW, OPERAND, ORIG)) + +/* atomic_flag */ + +typedef struct { + int b; +} atomic_flag; +#define ATOMIC_FLAG_INIT { false } + +static inline bool +atomic_flag_test_and_set(volatile atomic_flag *object) +{ + return __sync_lock_test_and_set(&object->b, 1); +} + +static inline bool +atomic_flag_test_and_set_explicit(volatile atomic_flag *object, + memory_order order OVS_UNUSED) +{ + return atomic_flag_test_and_set(object); +} + +static inline void +atomic_flag_clear(volatile atomic_flag *object) +{ + __sync_lock_release(&object->b); +} + +static inline void +atomic_flag_clear_explicit(volatile atomic_flag *object, + memory_order order OVS_UNUSED) +{ + atomic_flag_clear(object); +} diff --git a/lib/ovs-atomic-gcc4.7+.h b/lib/ovs-atomic-gcc4.7+.h new file mode 100644 index 000000000..07bef2a53 --- /dev/null +++ b/lib/ovs-atomic-gcc4.7+.h @@ -0,0 +1,141 @@ +/* + * Copyright (c) 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. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This header implements atomic operation primitives on GCC 4.7 and later. */ +#ifndef IN_OVS_ATOMIC_H +#error "This header should only be included indirectly via ovs-atomic.h." +#endif + +/* C11 standardized atomic type. */ +typedef bool atomic_bool; + +typedef char atomic_char; +typedef signed char atomic_schar; +typedef unsigned char atomic_uchar; + +typedef short atomic_short; +typedef unsigned short atomic_ushort; + +typedef int atomic_int; +typedef unsigned int atomic_uint; + +typedef long atomic_long; +typedef unsigned long atomic_ulong; + +typedef long long atomic_llong; +typedef unsigned long long atomic_ullong; + +typedef size_t atomic_size_t; +typedef ptrdiff_t atomic_ptrdiff_t; + +typedef intmax_t atomic_intmax_t; +typedef uintmax_t atomic_uintmax_t; + +typedef intptr_t atomic_intptr_t; +typedef uintptr_t atomic_uintptr_t; + +/* Nonstandard atomic types. */ +typedef int8_t atomic_int8_t; +typedef uint8_t atomic_uint8_t; + +typedef int16_t atomic_int16_t; +typedef uint16_t atomic_uint16_t; + +typedef int32_t atomic_int32_t; +typedef uint32_t atomic_uint32_t; + +typedef int64_t atomic_int64_t; +typedef uint64_t atomic_uint64_t; + +typedef enum { + memory_order_relaxed = __ATOMIC_RELAXED, + memory_order_consume = __ATOMIC_CONSUME, + memory_order_acquire = __ATOMIC_ACQUIRE, + memory_order_release = __ATOMIC_RELEASE, + memory_order_acq_rel = __ATOMIC_ACQ_REL, + memory_order_seq_cst = __ATOMIC_SEQ_CST +} memory_order; + +#define ATOMIC_VAR_INIT(VALUE) (VALUE) +#define atomic_init(OBJECT, VALUE) (*(OBJECT) = (VALUE), (void) 0) + +#define atomic_thread_fence __atomic_thread_fence +#define atomic_signal_fence __atomic_signal_fence +#define atomic_is_lock_free __atomic_is_lock_free + +#define atomic_store(DST, SRC) \ + atomic_store_explicit(DST, SRC, memory_order_seq_cst) +#define atomic_store_explicit __atomic_store_n + +#define atomic_read(SRC, DST) \ + atomic_read_explicit(SRC, DST, memory_order_seq_cst) +#define atomic_read_explicit(SRC, DST, ORDER) \ + (*(DST) = __atomic_load_n(SRC, ORDER), \ + (void) 0) + +#define atomic_add(RMW, OPERAND, ORIG) \ + atomic_add_explicit(RMW, OPERAND, ORIG, memory_order_seq_cst) +#define atomic_sub(RMW, OPERAND, ORIG) \ + atomic_sub_explicit(RMW, OPERAND, ORIG, memory_order_seq_cst) +#define atomic_or(RMW, OPERAND, ORIG) \ + atomic_or_explicit(RMW, OPERAND, ORIG, memory_order_seq_cst) +#define atomic_xor(RMW, OPERAND, ORIG) \ + atomic_xor_explicit(RMW, OPERAND, ORIG, memory_order_seq_cst) +#define atomic_and(RMW, OPERAND, ORIG) \ + atomic_and_explicit(RMW, OPERAND, ORIG, memory_order_seq_cst) + +#define atomic_add_explicit(RMW, OPERAND, ORIG, ORDER) \ + (*(ORIG) = __atomic_fetch_add(RMW, OPERAND, ORDER), (void) 0) +#define atomic_sub_explicit(RMW, OPERAND, ORIG, ORDER) \ + (*(ORIG) = __atomic_fetch_sub(RMW, OPERAND, ORDER), (void) 0) +#define atomic_or_explicit(RMW, OPERAND, ORIG, ORDER) \ + (*(ORIG) = __atomic_fetch_or(RMW, OPERAND, ORDER), (void) 0) +#define atomic_xor_explicit(RMW, OPERAND, ORIG, ORDER) \ + (*(ORIG) = __atomic_fetch_xor(RMW, OPERAND, ORDER), (void) 0) +#define atomic_and_explicit(RMW, OPERAND, ORIG, ORDER) \ + (*(ORIG) = __atomic_fetch_and(RMW, OPERAND, ORDER), (void) 0) + +/* atomic_flag */ + +typedef struct { + unsigned char b; +} atomic_flag; +#define ATOMIC_FLAG_INIT { .b = false } + +static inline bool +atomic_flag_test_and_set_explicit(volatile atomic_flag *object, + memory_order order) +{ + return __atomic_test_and_set(&object->b, order); +} + +static inline bool +atomic_flag_test_and_set(volatile atomic_flag *object) +{ + return atomic_flag_test_and_set_explicit(object, memory_order_seq_cst); +} + +static inline void +atomic_flag_clear_explicit(volatile atomic_flag *object, memory_order order) +{ + __atomic_clear(object, order); +} + +static inline void +atomic_flag_clear(volatile atomic_flag *object) +{ + atomic_flag_clear_explicit(object, memory_order_seq_cst); +} diff --git a/lib/ovs-atomic-pthreads.c b/lib/ovs-atomic-pthreads.c new file mode 100644 index 000000000..a501b8256 --- /dev/null +++ b/lib/ovs-atomic-pthreads.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 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. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "ovs-atomic.h" +#include "ovs-thread.h" + +#if OVS_ATOMIC_PTHREADS_IMPL +bool +atomic_flag_test_and_set(volatile atomic_flag *flag_) +{ + atomic_flag *flag = CONST_CAST(atomic_flag *, flag_); + bool old_value; + + xpthread_mutex_lock(&flag->mutex); + old_value = flag->b; + flag->b = true; + xpthread_mutex_unlock(&flag->mutex); + + return old_value; +} + +bool +atomic_flag_test_and_set_explicit(volatile atomic_flag *flag, + memory_order order OVS_UNUSED) +{ + return atomic_flag_test_and_set(flag); +} + +void +atomic_flag_clear(volatile atomic_flag *flag_) +{ + atomic_flag *flag = CONST_CAST(atomic_flag *, flag_); + + xpthread_mutex_lock(&flag->mutex); + flag->b = false; + xpthread_mutex_unlock(&flag->mutex); +} + +void +atomic_flag_clear_explicit(volatile atomic_flag *flag, + memory_order order OVS_UNUSED) +{ + return atomic_flag_clear(flag); +} + +#endif /* OVS_ATOMIC_PTHREADS_IMPL */ diff --git a/lib/ovs-atomic-pthreads.h b/lib/ovs-atomic-pthreads.h new file mode 100644 index 000000000..2f47a9c97 --- /dev/null +++ b/lib/ovs-atomic-pthreads.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) 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. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This header implements atomic operation primitives using pthreads. */ +#ifndef IN_OVS_ATOMIC_H +#error "This header should only be included indirectly via ovs-atomic.h." +#endif + +#define OVS_ATOMIC_PTHREADS_IMPL 1 + +#define DEFINE_PTHREAD_ATOMIC(TYPE, NAME) \ + typedef struct { \ + TYPE value; \ + pthread_mutex_t mutex; \ + } NAME; + +#define ATOMIC_BOOL_LOCK_FREE 0 +DEFINE_PTHREAD_ATOMIC(bool, atomic_bool); + +#define ATOMIC_CHAR_LOCK_FREE 0 +DEFINE_PTHREAD_ATOMIC(char, atomic_char); +DEFINE_PTHREAD_ATOMIC(signed char, atomic_schar); +DEFINE_PTHREAD_ATOMIC(unsigned char, atomic_uchar); + +#define ATOMIC_SHORT_LOCK_FREE 0 +DEFINE_PTHREAD_ATOMIC(short, atomic_short); +DEFINE_PTHREAD_ATOMIC(unsigned short, atomic_ushort); + +#define ATOMIC_INT_LOCK_FREE 0 +DEFINE_PTHREAD_ATOMIC(int, atomic_int); +DEFINE_PTHREAD_ATOMIC(unsigned int, atomic_uint); + +#define ATOMIC_LONG_LOCK_FREE 0 +DEFINE_PTHREAD_ATOMIC(long, atomic_long); +DEFINE_PTHREAD_ATOMIC(unsigned long, atomic_ulong); + +#define ATOMIC_LLONG_LOCK_FREE 0 +DEFINE_PTHREAD_ATOMIC(long long, atomic_llong); +DEFINE_PTHREAD_ATOMIC(unsigned long long, atomic_ullong); + +DEFINE_PTHREAD_ATOMIC(size_t, atomic_size_t); +DEFINE_PTHREAD_ATOMIC(ptrdiff_t, atomic_ptrdiff_t); + +DEFINE_PTHREAD_ATOMIC(intmax_t, atomic_intmax_t); +DEFINE_PTHREAD_ATOMIC(uintmax_t, atomic_uintmax_t); + +#define ATOMIC_POINTER_LOCK_FREE 0 +DEFINE_PTHREAD_ATOMIC(intptr_t, atomic_intptr_t); +DEFINE_PTHREAD_ATOMIC(uintptr_t, atomic_uintptr_t); + +/* Nonstandard atomic types. */ +DEFINE_PTHREAD_ATOMIC(uint8_t, atomic_uint8_t); +DEFINE_PTHREAD_ATOMIC(uint16_t, atomic_uint16_t); +DEFINE_PTHREAD_ATOMIC(uint32_t, atomic_uint32_t); +DEFINE_PTHREAD_ATOMIC(int8_t, atomic_int8_t); +DEFINE_PTHREAD_ATOMIC(int16_t, atomic_int16_t); +DEFINE_PTHREAD_ATOMIC(int32_t, atomic_int32_t); +DEFINE_PTHREAD_ATOMIC(uint64_t, atomic_uint64_t); +DEFINE_PTHREAD_ATOMIC(int64_t, atomic_int64_t); + +typedef enum { + memory_order_relaxed, + memory_order_consume, + memory_order_acquire, + memory_order_release, + memory_order_acq_rel, + memory_order_seq_cst +} memory_order; + +#define ATOMIC_VAR_INIT(VALUE) { VALUE, PTHREAD_MUTEX_INITIALIZER } +#define atomic_init(OBJECT, VALUE) \ + ((OBJECT)->value = (VALUE), \ + pthread_mutex_init(&(OBJECT)->mutex, NULL), \ + (void) 0) + +static inline void +atomic_thread_fence(memory_order order OVS_UNUSED) +{ + /* Nothing to do. */ +} + +static inline void +atomic_signal_fence(memory_order order OVS_UNUSED) +{ + /* Nothing to do. */ +} + +#define atomic_is_lock_free(OBJ) false + +#define atomic_store(DST, SRC) \ + (pthread_mutex_lock(&(DST)->mutex), \ + (DST)->value = (SRC), \ + pthread_mutex_unlock(&(DST)->mutex), \ + (void) 0) +#define atomic_store_explicit(DST, SRC, ORDER) \ + ((void) (ORDER), atomic_store(DST, SRC)) + +#define atomic_read(SRC, DST) \ + (pthread_mutex_lock(CONST_CAST(pthread_mutex_t *, &(SRC)->mutex)), \ + *(DST) = (SRC)->value, \ + pthread_mutex_unlock(CONST_CAST(pthread_mutex_t *, &(SRC)->mutex)), \ + (void) 0) +#define atomic_read_explicit(SRC, DST, ORDER) \ + ((void) (ORDER), atomic_read(SRC, DST)) + +#define atomic_op__(RMW, OPERATOR, OPERAND, ORIG) \ + (pthread_mutex_lock(&(RMW)->mutex), \ + *(ORIG) = (RMW)->value, \ + (RMW)->value OPERATOR (OPERAND), \ + pthread_mutex_unlock(&(RMW)->mutex), \ + (void) 0) + +#define atomic_add(RMW, OPERAND, ORIG) atomic_op__(RMW, +=, OPERAND, ORIG) +#define atomic_sub(RMW, OPERAND, ORIG) atomic_op__(RMW, -=, OPERAND, ORIG) +#define atomic_or( RMW, OPERAND, ORIG) atomic_op__(RMW, |=, OPERAND, ORIG) +#define atomic_xor(RMW, OPERAND, ORIG) atomic_op__(RMW, ^=, OPERAND, ORIG) +#define atomic_and(RMW, OPERAND, ORIG) atomic_op__(RMW, &=, OPERAND, ORIG) + +#define atomic_add_explicit(RMW, OPERAND, ORIG, ORDER) \ + ((void) (ORDER), atomic_add(RMW, OPERAND, ORIG)) +#define atomic_sub_explicit(RMW, OPERAND, ORIG, ORDER) \ + ((void) (ORDER), atomic_sub(RMW, OPERAND, ORIG)) +#define atomic_or_explicit(RMW, OPERAND, ORIG, ORDER) \ + ((void) (ORDER), atomic_or(RMW, OPERAND, ORIG)) +#define atomic_xor_explicit(RMW, OPERAND, ORIG, ORDER) \ + ((void) (ORDER), atomic_xor(RMW, OPERAND, ORIG)) +#define atomic_and_explicit(RMW, OPERAND, ORIG, ORDER) \ + ((void) (ORDER), atomic_and(RMW, OPERAND, ORIG)) + +/* atomic_flag */ + +typedef struct { + bool b; + pthread_mutex_t mutex; +} atomic_flag; +#define ATOMIC_FLAG_INIT { false, PTHREAD_MUTEX_INITIALIZER } + +bool atomic_flag_test_and_set(volatile atomic_flag *); +bool atomic_flag_test_and_set_explicit(volatile atomic_flag *, memory_order); + +void atomic_flag_clear(volatile atomic_flag *); +void atomic_flag_clear_explicit(volatile atomic_flag *, memory_order); diff --git a/lib/ovs-atomic.h b/lib/ovs-atomic.h new file mode 100644 index 000000000..a0a34f30f --- /dev/null +++ b/lib/ovs-atomic.h @@ -0,0 +1,250 @@ +/* + * Copyright (c) 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. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OVS_ATOMIC_H +#define OVS_ATOMIC_H 1 + +/* Atomic operations. + * + * This library implements atomic operations with an API based on the one + * defined in C11. It includes multiple implementations for compilers and + * libraries with varying degrees of built-in support for C11, including an + * fallback implementation for systems that have pthreads but no other support + * for atomics. + * + * This comment describes the common features of all the implementations. + * + * + * Types + * ===== + * + * The following atomic types are supported as typedefs for atomic versions of + * the listed ordinary types: + * + * ordinary type atomic version + * ------------------- ---------------------- + * bool atomic_bool + * + * char atomic_char + * signed char atomic_schar + * unsigned char atomic_uchar + * + * short atomic_short + * unsigned short atomic_ushort + * + * int atomic_int + * unsigned int atomic_uint + * + * long atomic_long + * unsigned long atomic_ulong + * + * long long atomic_llong + * unsigned long long atomic_ullong + * + * size_t atomic_size_t + * ptrdiff_t atomic_ptrdiff_t + * + * intmax_t atomic_intmax_t + * uintmax_t atomic_uintmax_t + * + * intptr_t atomic_intptr_t + * uintptr_t atomic_uintptr_t + * + * uint8_t atomic_uint8_t (*) + * uint16_t atomic_uint16_t (*) + * uint32_t atomic_uint32_t (*) + * int8_t atomic_int8_t (*) + * int16_t atomic_int16_t (*) + * int32_t atomic_int32_t (*) + * uint64_t atomic_uint64_t (*) + * int64_t atomic_int64_t (*) + * + * (*) Not specified by C11. + * + * The atomic version of a type doesn't necessarily have the same size or + * representation as the ordinary version; for example, atomic_int might be a + * typedef for a struct that also includes a mutex. The range of an atomic + * type does match the range of the corresponding ordinary type. + * + * C11 says that one may use the _Atomic keyword in place of the typedef name, + * e.g. "_Atomic int" instead of "atomic_int". This library doesn't support + * that. + * + * + * Initialization + * ============== + * + * To initialize an atomic variable at its point of definition, use + * ATOMIC_VAR_INIT: + * + * static atomic_int ai = ATOMIC_VAR_INIT(123); + * + * To initialize an atomic variable in code, use atomic_init(): + * + * static atomic_int ai; + * ... + * atomic_init(&ai, 123); + * + * + * Barriers + * ======== + * + * enum memory_order specifies the strictness of a memory barrier. It has the + * following values: + * + * memory_order_relaxed: + * + * Compiler barrier only. Does not imply any CPU memory ordering. + * + * memory_order_acquire: + * + * Memory accesses after an acquire barrier cannot be moved before the + * barrier. Memory accesses before an acquire barrier *can* be moved + * after it. + * + * memory_order_release: + * + * Memory accesses before a release barrier cannot be moved after the + * barrier. Memory accesses after a release barrier *can* be moved + * before it. + * + * memory_order_acq_rel: + * + * Memory accesses cannot be moved across an acquire-release barrier in + * either direction. + * + * memory_order_seq_cst: + * + * Prevents movement of memory accesses like an acquire-release barrier, + * but whereas acquire-release synchronizes cooperating threads, + * sequential-consistency synchronizes the whole system. + * + * memory_order_consume: + * + * A slight relaxation of memory_order_acquire. + * + * The following functions insert explicit barriers. Most of the other atomic + * functions also include barriers. + * + * void atomic_thread_fence(memory_order order); + * + * Inserts a barrier of the specified type. + * + * For memory_order_relaxed, this is a no-op. + * + * void atomic_signal_fence(memory_order order); + * + * Inserts a barrier of the specified type, but only with respect to + * signal handlers in the same thread as the barrier. This is + * basically a compiler optimization barrier, except for + * memory_order_relaxed, which is a no-op. + * + * + * Atomic Operations + * ================= + * + * In this section, A is an atomic type and C is the corresponding non-atomic + * type. + * + * The "store" primitives match C11: + * + * void atomic_store(A *object, C value); + * void atomic_store_explicit(A *object, C value, memory_order); + * + * Atomically stores 'value' into '*object', respecting the given + * memory order (or memory_order_seq_cst for atomic_store()). + * + * The following primitives differ from the C11 ones (and have different names) + * because there does not appear to be a way to implement the standard + * primitives in standard C: + * + * void atomic_read(A *src, C *dst); + * void atomic_read_explicit(A *src, C *dst, memory_order); + * + * Atomically loads a value from 'src', writing the value read into + * '*dst', respecting the given memory order (or memory_order_seq_cst + * for atomic_read()). + * + * void atomic_add(A *rmw, C arg, C *orig); + * void atomic_sub(A *rmw, C arg, C *orig); + * void atomic_or(A *rmw, C arg, C *orig); + * void atomic_xor(A *rmw, C arg, C *orig); + * void atomic_and(A *rmw, C arg, C *orig); + * void atomic_add_explicit(A *rmw, C arg, C *orig, memory_order); + * void atomic_sub_explicit(A *rmw, C arg, C *orig, memory_order); + * void atomic_or_explicit(A *rmw, C arg, C *orig, memory_order); + * void atomic_xor_explicit(A *rmw, C arg, C *orig, memory_order); + * void atomic_and_explicit(A *rmw, C arg, C *orig, memory_order); + * + * Atomically applies the given operation, with 'arg' as the second + * operand, to '*rmw', and stores the original value of '*rmw' into + * '*orig', respecting the given memory order (or memory_order_seq_cst + * if none is specified). + * + * The results are similar to those that would be obtained with +=, -=, + * |=, ^=, or |= on non-atomic types. + * + * + * atomic_flag + * =========== + * + * atomic_flag is a typedef for a type with two states, set and clear, that + * provides atomic test-and-set functionality. + * + * ATOMIC_FLAG_INIT is an initializer for atomic_flag. The initial state is + * "clear". + * + * The following functions are available. + * + * bool atomic_flag_test_and_set(atomic_flag *object) + * bool atomic_flag_test_and_set_explicit(atomic_flag *object, + * memory_order); + * + * Atomically sets '*object', respsecting the given memory order (or + * memory_order_seq_cst for atomic_flag_test_and_set()). Returns the + * previous value of the flag (false for clear, true for set). + * + * void atomic_flag_clear(atomic_flag *object); + * void atomic_flag_clear_explicit(atomic_flag *object, memory_order); + * + * Atomically clears '*object', respecting the given memory order (or + * memory_order_seq_cst for atomic_flag_clear()). + */ + +#include +#include +#include +#include +#include +#include "compiler.h" +#include "util.h" + +#define IN_OVS_ATOMIC_H + #if __CHECKER__ + /* sparse doesn't understand some GCC extensions we use. */ + #include "ovs-atomic-pthreads.h" + #elif HAVE_STDATOMIC_H + #include "ovs-atomic-c11.h" + #elif __GNUC__ >= 4 && __GNUC_MINOR__ >= 7 + #include "ovs-atomic-gcc4.7+.h" + #elif __GNUC__ >= 4 + #include "ovs-atomic-gcc4+.h" + #else + #include "ovs-atomic-pthreads.h" + #endif +#undef IN_OVS_ATOMIC_H + +#endif /* ovs-atomic.h */ diff --git a/m4/openvswitch.m4 b/m4/openvswitch.m4 index 57c71e029..ca805060a 100644 --- a/m4/openvswitch.m4 +++ b/m4/openvswitch.m4 @@ -425,3 +425,24 @@ static thread_local int var;], [return var;])], GCC __thread extenions.]) fi fi]) + +dnl OVS_CHECK_ATOMIC_ALWAYS_LOCK_FREE(SIZE) +dnl +dnl Checks __atomic_always_lock_free(SIZE, 0) +AC_DEFUN([OVS_CHECK_ATOMIC_ALWAYS_LOCK_FREE], + [AC_CACHE_CHECK( + [value of __atomic_always_lock_free($1)], + [ovs_cv_atomic_always_lock_free_$1], + [AC_COMPUTE_INT( + [ovs_cv_atomic_always_lock_free_$1], + [__atomic_always_lock_free($1, 0)], + [], + [ovs_cv_atomic_always_lock_free_$1=unsupported])]) + if test ovs_cv_atomic_always_lock_free_$1 != unsupported; then + AC_DEFINE_UNQUOTED( + [ATOMIC_ALWAYS_LOCK_FREE_$1B], + [$ovs_cv_atomic_always_lock_free_$1], + [If the C compiler is GCC 4.7 or later, define to the return value of + __atomic_always_lock_free($1, 0). If the C compiler is not GCC or is + an older version of GCC, the value does not matter.]) + fi]) diff --git a/tests/automake.mk b/tests/automake.mk index 6e07a3d08..755d88ef9 100644 --- a/tests/automake.mk +++ b/tests/automake.mk @@ -98,6 +98,7 @@ valgrind_wrappers = \ tests/valgrind/ovsdb-server \ tests/valgrind/ovsdb-tool \ tests/valgrind/test-aes128 \ + tests/valgrind/test-atomic \ tests/valgrind/test-bundle \ tests/valgrind/test-byte-order \ tests/valgrind/test-classifier \ @@ -176,6 +177,10 @@ noinst_PROGRAMS += tests/test-aes128 tests_test_aes128_SOURCES = tests/test-aes128.c tests_test_aes128_LDADD = lib/libopenvswitch.a $(SSL_LIBS) +noinst_PROGRAMS += tests/test-atomic +tests_test_atomic_SOURCES = tests/test-atomic.c +tests_test_atomic_LDADD = lib/libopenvswitch.a $(SSL_LIBS) + noinst_PROGRAMS += tests/test-bundle tests_test_bundle_SOURCES = tests/test-bundle.c tests_test_bundle_LDADD = lib/libopenvswitch.a $(SSL_LIBS) diff --git a/tests/library.at b/tests/library.at index 532af3bd5..f84a55b31 100644 --- a/tests/library.at +++ b/tests/library.at @@ -25,6 +25,10 @@ AT_CHECK([test-hindex], [0], [.................. ]) AT_CLEANUP +AT_SETUP([test atomic operations]) +AT_CHECK([test-atomic]) +AT_CLEANUP + AT_SETUP([test linked lists]) AT_CHECK([test-list], [0], [.. ]) diff --git a/tests/test-atomic.c b/tests/test-atomic.c new file mode 100644 index 000000000..27bf5520e --- /dev/null +++ b/tests/test-atomic.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 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. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "ovs-atomic.h" +#include "util.h" + +#define TEST_ATOMIC_TYPE(ATOMIC_TYPE, BASE_TYPE) \ + { \ + ATOMIC_TYPE x = ATOMIC_VAR_INIT(1); \ + BASE_TYPE value, orig; \ + \ + atomic_read(&x, &value); \ + ovs_assert(value == 1); \ + \ + atomic_store(&x, 2); \ + atomic_read(&x, &value); \ + ovs_assert(value == 2); \ + \ + atomic_init(&x, 3); \ + atomic_read(&x, &value); \ + ovs_assert(value == 3); \ + \ + atomic_add(&x, 1, &orig); \ + ovs_assert(orig == 3); \ + atomic_read(&x, &value); \ + ovs_assert(value == 4); \ + \ + atomic_sub(&x, 2, &orig); \ + ovs_assert(orig == 4); \ + atomic_read(&x, &value); \ + ovs_assert(value == 2); \ + \ + atomic_or(&x, 6, &orig); \ + ovs_assert(orig == 2); \ + atomic_read(&x, &value); \ + ovs_assert(value == 6); \ + \ + atomic_and(&x, 10, &orig); \ + ovs_assert(orig == 6); \ + atomic_read(&x, &value); \ + ovs_assert(value == 2); \ + \ + atomic_xor(&x, 10, &orig); \ + ovs_assert(orig == 2); \ + atomic_read(&x, &value); \ + ovs_assert(value == 8); \ + } + +int +main(void) +{ + TEST_ATOMIC_TYPE(atomic_char, char); + TEST_ATOMIC_TYPE(atomic_uchar, unsigned char); + TEST_ATOMIC_TYPE(atomic_schar, signed char); + TEST_ATOMIC_TYPE(atomic_short, short); + TEST_ATOMIC_TYPE(atomic_ushort, unsigned short); + TEST_ATOMIC_TYPE(atomic_int, int); + TEST_ATOMIC_TYPE(atomic_uint, unsigned int); + TEST_ATOMIC_TYPE(atomic_long, long int); + TEST_ATOMIC_TYPE(atomic_ulong, unsigned long int); + TEST_ATOMIC_TYPE(atomic_llong, long long int); + TEST_ATOMIC_TYPE(atomic_ullong, unsigned long long int); + TEST_ATOMIC_TYPE(atomic_size_t, size_t); + TEST_ATOMIC_TYPE(atomic_ptrdiff_t, ptrdiff_t); + TEST_ATOMIC_TYPE(atomic_intmax_t, intmax_t); + TEST_ATOMIC_TYPE(atomic_uintmax_t, uintmax_t); + TEST_ATOMIC_TYPE(atomic_intptr_t, intptr_t); + TEST_ATOMIC_TYPE(atomic_uintptr_t, uintptr_t); + TEST_ATOMIC_TYPE(atomic_uint8_t, uint8_t); + TEST_ATOMIC_TYPE(atomic_int8_t, int8_t); + TEST_ATOMIC_TYPE(atomic_uint16_t, uint16_t); + TEST_ATOMIC_TYPE(atomic_int16_t, int16_t); + TEST_ATOMIC_TYPE(atomic_uint32_t, uint32_t); + TEST_ATOMIC_TYPE(atomic_int32_t, int32_t); + TEST_ATOMIC_TYPE(atomic_uint64_t, uint64_t); + TEST_ATOMIC_TYPE(atomic_int64_t, int64_t); + + return 0; +}