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.
This commit is contained in:
@ -102,4 +102,19 @@ static inline void atomic_synchronize()
|
|||||||
#endif
|
#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
|
MXS_END_DECLS
|
||||||
|
@ -124,3 +124,13 @@ void atomic_store_ptr(void **variable, void *value)
|
|||||||
(void)__sync_lock_test_and_set(variable, value);
|
(void)__sync_lock_test_and_set(variable, value);
|
||||||
#endif
|
#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
|
||||||
|
}
|
||||||
|
@ -37,7 +37,6 @@ void test_add(void* data)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void test_load_store(void* data)
|
void test_load_store(void* data)
|
||||||
{
|
{
|
||||||
int id = (size_t)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*))
|
int run_test(void(*func)(void*))
|
||||||
{
|
{
|
||||||
THREAD threads[NTHR];
|
THREAD threads[NTHR];
|
||||||
@ -58,9 +80,9 @@ int run_test(void(*func)(void*))
|
|||||||
atomic_store_int32(&expected, 0);
|
atomic_store_int32(&expected, 0);
|
||||||
atomic_store_int32(&running, 1);
|
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);
|
ss_dassert(false);
|
||||||
}
|
}
|
||||||
@ -83,6 +105,7 @@ int main(int argc, char** argv)
|
|||||||
|
|
||||||
run_test(test_load_store);
|
run_test(test_load_store);
|
||||||
run_test(test_add);
|
run_test(test_add);
|
||||||
|
run_test(test_cas);
|
||||||
|
|
||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user