diff --git a/lib/base/heimbase-atomics.h b/lib/base/heimbase-atomics.h index 4aee4d241..fdf87382f 100644 --- a/lib/base/heimbase-atomics.h +++ b/lib/base/heimbase-atomics.h @@ -61,6 +61,38 @@ #define heim_base_exchange_32(t,v) atomic_exchange((t), (v)) #define heim_base_exchange_64(t,v) atomic_exchange((t), (v)) +/* + * 's and AIX's CAS functions take a pointer to an expected value + * and return a boolean, setting the pointed-to variable to the old value of + * the target. + * + * Other CAS functions, like GCC's, Solaris'/Illumos', and Windows', return the + * old value and don't take a pointer to an expected value. + * + * We implement the latter semantics. + */ +static inline void * +heim_base_cas_pointer_(heim_base_atomic(void *)*t, void *e, void *d) +{ + return atomic_compare_exchange_strong(t, &e, d), e; +} + +static inline uint32_t +heim_base_cas_32_(heim_base_atomic(uint32_t)*t, uint32_t e, uint32_t d) +{ + return atomic_compare_exchange_strong(t, &e, d), e; +} + +static inline uint64_t +heim_base_cas_64_(heim_base_atomic(uint64_t)*t, uint64_t e, uint64_t d) +{ + return atomic_compare_exchange_strong(t, &e, d), e; +} + +#define heim_base_cas_pointer(t,e,d) heim_base_cas_pointer_((t), (e), (d)) +#define heim_base_cas_32(t,e,d) heim_base_cas_32_((t), (e), (d)) +#define heim_base_cas_64(t,e,d) heim_base_cas_64_((t), (e), (d)) + #elif defined(__GNUC__) && defined(HAVE___SYNC_ADD_AND_FETCH) #define heim_base_atomic_barrier() __sync_synchronize() @@ -84,6 +116,10 @@ #define heim_base_exchange_32(t,v) heim_base_exchange_pointer((t), (v)) #define heim_base_exchange_64(t,v) heim_base_exchange_pointer((t), (v)) +#define heim_base_cas_pointer(t,e,d) __sync_val_compare_and_swap((t), (e), (d)) +#define heim_base_cas_32(t,e,d) __sync_val_compare_and_swap((t), (e), (d)) +#define heim_base_cas_64(t,e,d) __sync_val_compare_and_swap((t), (e), (d)) + #elif defined(__sun) #include @@ -107,6 +143,10 @@ static inline void __heim_base_atomic_barrier(void) #define heim_base_exchange_32(t,v) atomic_swap_32((t), (v)) #define heim_base_exchange_64(t,v) atomic_swap_64((t), (v)) +#define heim_base_cas_pointer(t,e,d) atomic_cas_ptr((t), (e), (d)) +#define heim_base_cas_32(t,e,d) atomic_cas_32((t), (e), (d)) +#define heim_base_cas_64(t,e,d) atomic_cas_64((t), (e), (d)) + #elif defined(_AIX) #include @@ -151,6 +191,28 @@ heim_base_exchange_64(uint64_t *p, uint64_t newval) return val; } +static inline void * +heim_base_cas_pointer_(heim_base_atomic(void *)*t, void *e, void *d) +{ + return compare_and_swaplp((atomic_l)t, &e, d), e; +} + +static inline uint32_t +heim_base_cas_32_(heim_base_atomic(uint32_t)*t, uint32_t e, uint32_t d) +{ + return compare_and_swap((atomic_p)t, &e, d), e; +} + +static inline uint64_t +heim_base_cas_64_(heim_base_atomic(uint64_t)*t, uint64_t e, uint64_t d) +{ + return compare_and_swaplp((atomic_l)t, &e, d), e; +} + +#define heim_base_cas_pointer(t,e,d) heim_base_cas_pointer_((t), (e), (d)) +#define heim_base_cas_32(t,e,d) heim_base_cas_32_((t), (e), (d)) +#define heim_base_cas_64(t,e,d) heim_base_cas_64_((t), (e), (d)) + #elif defined(_WIN32) #define heim_base_atomic_barrier() MemoryBarrier() @@ -162,7 +224,11 @@ heim_base_exchange_64(uint64_t *p, uint64_t newval) #define heim_base_exchange_pointer(t,v) InterlockedExchangePointer((PVOID volatile *)(t), (PVOID)(v)) #define heim_base_exchange_32(t,v) ((ULONG)InterlockedExchange((LONG volatile *)(t), (LONG)(v))) -#define heim_base_exchange_64(t,v) ((ULONG64)InterlockedExchange64((LONG64 violatile *)(t), (LONG64)(v))) +#define heim_base_exchange_64(t,v) ((ULONG64)InterlockedExchange64((ULONG64 volatile *)(t), (LONG64)(v))) + +#define heim_base_cas_pointer(t,e,d) InterlockedCompareExchangePointer((PVOID volatile *)(t), (d), (e)) +#define heim_base_cas_32(t,e,d) InterlockedCompareExchange ((LONG volatile *)(t), (d), (e)) +#define heim_base_cas_64(t,e,d) InterlockedCompareExchange64((ULONG64 volatile *)(t), (d), (e)) #else @@ -244,6 +310,41 @@ heim_base_exchange_64(uint64_t *p, uint64_t newval) return old; } +static inline void * +heim_base_cas_pointer(void *target, void *expected, void *desired) +{ + void *old; + HEIMDAL_MUTEX_lock(&_heim_base_mutex); + if ((old = *(void **)target) == expected) + *(void **)target = desired; + HEIMDAL_MUTEX_unlock(&_heim_base_mutex); + return old; +} + +static inline uint32_t +heim_base_cas_32(uint32_t *p, uint32_t expected, uint32_t desired) +{ + uint32_t old; + HEIMDAL_MUTEX_lock(&_heim_base_mutex); + if ((old = *(uint32_t *)p) == expected) + old = *p; + *p = desired; + HEIMDAL_MUTEX_unlock(&_heim_base_mutex); + return old; +} + +static inline uint64_t +heim_base_cas_64(uint64_t *p, uint64_t expected,uint64_t desired) +{ + uint64_t old; + HEIMDAL_MUTEX_lock(&_heim_base_mutex); + if ((old = *(uint64_t *)p) == expected) + old = *p; + *p = desired; + HEIMDAL_MUTEX_unlock(&_heim_base_mutex); + return old; +} + #endif /* defined(__GNUC__) && defined(HAVE___SYNC_ADD_AND_FETCH) */ #ifndef heim_base_atomic diff --git a/lib/base/test_base.c b/lib/base/test_base.c index 6d0668663..cc875d7f8 100644 --- a/lib/base/test_base.c +++ b/lib/base/test_base.c @@ -64,6 +64,7 @@ #include #include "baselocl.h" +#include "heimbase-atomics.h" static void HEIM_CALLCONV memory_free(heim_object_t obj) @@ -1273,6 +1274,84 @@ test_array() return 0; } +/* This function tests only that heimbase-atomics.h compiles */ +static int +test_atomics(void) +{ + heim_base_atomic(void *) tptr; + heim_base_atomic(uint32_t) tu32; + heim_base_atomic(uint64_t) tu64; + + heim_base_atomic_init(&tptr, NULL); + heim_base_atomic_init(&tu32, 0); + heim_base_atomic_init(&tu64, 0); + + if (heim_base_atomic_load(&tptr)) + return 1; + if (heim_base_atomic_load(&tu32)) + return 1; + if (heim_base_atomic_load(&tu64)) + return 1; + + heim_base_atomic_store(&tptr, &tptr); + heim_base_atomic_store(&tu32, 1); + heim_base_atomic_store(&tu64, 1); + + if (heim_base_atomic_load(&tptr) != &tptr) + return 1; + if (heim_base_atomic_load(&tu32) != 1) + return 1; + if (heim_base_atomic_load(&tu64) != 1) + return 1; + + if (heim_base_atomic_inc_32(&tu32) != 2 || + heim_base_atomic_load(&tu32) != 2) + return 1; + if (heim_base_atomic_inc_64(&tu64) != 2 || + heim_base_atomic_load(&tu64) != 2) + return 1; + + if (heim_base_atomic_dec_32(&tu32) != 1 || + heim_base_atomic_load(&tu32) != 1) + return 1; + if (heim_base_atomic_dec_64(&tu64) != 1 || + heim_base_atomic_load(&tu64) != 1) + return 1; + + heim_base_exchange_pointer(&tptr, (void *)&tu32); + if (heim_base_atomic_load(&tptr) != &tu32) + return 1; + heim_base_exchange_32(&tu32, 32); + if (heim_base_atomic_load(&tu32) != 32) + return 1; + heim_base_exchange_64(&tu64, 64); + if (heim_base_atomic_load(&tu64) != 64) + return 1; + + if (heim_base_cas_pointer(&tptr, (void *)&tu32, (void *)&tu64) != &tu32) + return 1; + if (heim_base_cas_pointer(&tptr, (void *)&tu32, (void *)&tptr) != &tu64) + return 1; + if (heim_base_atomic_load(&tptr) != (void *)&tu64) + return 1; + + if (heim_base_cas_32(&tu32, 32, 4) != 32) + return 1; + if (heim_base_cas_32(&tu32, 32, 4) != 4) + return 1; + if (heim_base_atomic_load(&tu32) != 4) + return 1; + + if (heim_base_cas_64(&tu64, 64, 4) != 64) + return 1; + if (heim_base_cas_64(&tu64, 64, 4) != 4) + return 1; + if (heim_base_atomic_load(&tu64) != 4) + return 1; + + return 0; +} + int main(int argc, char **argv) { @@ -1295,6 +1374,7 @@ main(int argc, char **argv) res |= test_db(NULL, NULL); res |= test_db("json", argc > 1 ? argv[1] : "test_db.json"); res |= test_array(); + res |= test_atomics(); return res ? 1 : 0; }