From c7cffa0722807c4d7af91a03c6d864a10af08492 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20M=C3=A4kel=C3=A4?= Date: Sun, 7 May 2017 11:00:48 +0300 Subject: [PATCH] Add atomic compare-and-swap The atomic compare-and-swap can be used to implement lock-free structures. The planned use for this is to remove some of the locking done in the services when listeners are being manipulated. --- include/maxscale/atomic.h | 15 +++++++++++++++ server/core/atomic.cc | 10 ++++++++++ server/core/test/testatomic.cc | 29 ++++++++++++++++++++++++++--- 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/include/maxscale/atomic.h b/include/maxscale/atomic.h index 639faa216..e571f2d11 100644 --- a/include/maxscale/atomic.h +++ b/include/maxscale/atomic.h @@ -102,4 +102,19 @@ static inline void atomic_synchronize() #endif } +/** + * @brief Atomic compare-and-swap of pointers + * + * @param variable Pointer to the variable + * @param old_value Pointer to the expected value of @variable + * @param new_value Stored value if @c variable is equal to @c old_value + * + * @return True if @c variable and @c old_value were equal + * + * @note If GCC __atomic builtins are available, the contents of @c variable are + * written to @c old_value if the two are not equal. Do not rely on this behavior + * and always do a separate read before attempting a compare-and-swap. + */ +bool atomic_cas_ptr(void **variable, void** old_value, void *new_value); + MXS_END_DECLS diff --git a/server/core/atomic.cc b/server/core/atomic.cc index afff9c25d..5ffa1de90 100644 --- a/server/core/atomic.cc +++ b/server/core/atomic.cc @@ -124,3 +124,13 @@ void atomic_store_ptr(void **variable, void *value) (void)__sync_lock_test_and_set(variable, value); #endif } + +bool atomic_cas_ptr(void **variable, void** old_value, void *new_value) +{ +#ifdef MXS_USE_ATOMIC_BUILTINS + return __atomic_compare_exchange_n(variable, old_value, new_value, + false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); +#else + return __sync_val_compare_and_swap(variable, *old_value, new_value); +#endif +} diff --git a/server/core/test/testatomic.cc b/server/core/test/testatomic.cc index da405567a..f50dbb45b 100644 --- a/server/core/test/testatomic.cc +++ b/server/core/test/testatomic.cc @@ -37,7 +37,6 @@ void test_add(void* data) } } - void test_load_store(void* data) { int id = (size_t)data; @@ -51,6 +50,29 @@ void test_load_store(void* data) } } +static void* cas_dest = (void*)1; + +void test_cas(void* data) +{ + int id = (size_t)data; + static int loops = 0; + + while (atomic_load_int32(&running)) + { + intptr_t my_value = (id + 1) % NTHR; + intptr_t my_expected = id; + + while (!atomic_cas_ptr(&cas_dest, (void**)&my_expected, (void*)&my_value)) + { + ; + } + + loops++; + } + + ss_dassert(loops > 0); +} + int run_test(void(*func)(void*)) { THREAD threads[NTHR]; @@ -58,9 +80,9 @@ int run_test(void(*func)(void*)) atomic_store_int32(&expected, 0); atomic_store_int32(&running, 1); - for (int i = 0; i < NTHR; i++) + for (size_t i = 0; i < NTHR; i++) { - if (thread_start(&threads[i], func, NULL) == NULL) + if (thread_start(&threads[i], func, (void*)(i + 1)) == NULL) { ss_dassert(false); } @@ -83,6 +105,7 @@ int main(int argc, char** argv) run_test(test_load_store); run_test(test_add); + run_test(test_cas); return rval; }