base: Add atomic CAS macros/functions
This commit is contained in:
		| @@ -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)) | ||||
|  | ||||
| /* | ||||
|  * <stdatomic.h>'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 <sys/atomic.h> | ||||
| @@ -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 <sys/atomic_op.h> | ||||
| @@ -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 | ||||
|   | ||||
| @@ -64,6 +64,7 @@ | ||||
| #include <fcntl.h> | ||||
|  | ||||
| #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; | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Nicolas Williams
					Nicolas Williams