/* * Copyright (C) 2014, 2015 Filip Pizlo. All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY FILIP PIZLO ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL FILIP PIZLO OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef FP_TSF_ATOMICS_H #define FP_TSF_ATOMICS_H #include "tsf_config.h" #include "tsf_types.h" #include #ifdef HAVE_SCHED_H #include #endif #ifdef TSF_HAVE_PTHREAD #include #endif #ifdef __cplusplus extern "C" { #endif #if 0 /* hack to deconfuse emacs */ } #endif struct tsf_mutex; struct tsf_atomic_count; struct tsf_once; typedef struct tsf_mutex tsf_mutex_t; typedef struct tsf_atomic_count tsf_atomic_count_t; typedef struct tsf_once tsf_once_t; #ifdef TSF_HAVE_BUILTIN_CAS static TSF_inline tsf_bool_t tsf_cas32( volatile void *pointer, int32_t expected, int32_t new_value) { return __sync_bool_compare_and_swap( (volatile int32_t*)pointer, expected, new_value); } static TSF_inline tsf_bool_t tsf_cas_ptr( volatile void *pointer, intptr_t expected, intptr_t new_value) { return __sync_bool_compare_and_swap( (volatile intptr_t*)pointer, expected, new_value); } #define TSF_HAVE_CAS 1 #elif !defined(TSF_HAVE_PTHREAD) static TSF_inline tsf_bool_t tsf_cas32( volatile void *rawPointer, int32_t expected, int32_t new_value) { volatile int32_t *pointer = (volatile int32_t*)rawPointer; if (*pointer == expected) { *pointer = new_value; return tsf_true; } else { return tsf_false; } } static TSF_inline tsf_bool_t tsf_cas_ptr( volatile void *rawPointer, intptr_t expected, intptr_t new_value) { volatile intptr_t *pointer = (volatile intptr_t*)rawPointer; if (*pointer == expected) { *pointer = new_value; return tsf_true; } else { return tsf_false; } } #define TSF_HAVE_CAS 1 #endif #ifdef TSF_HAVE_BUILTIN_XADD static TSF_inline int32_t tsf_xadd32(volatile void *pointer, int32_t value) { return __sync_fetch_and_add((volatile int32_t*)pointer, value); } #define TSF_HAVE_XADD 1 #elif defined(TSF_HAVE_CAS) static TSF_inline int32_t tsf_xadd32(volatile void *rawPointer, int32_t value) { int32_t result; volatile int32_t *pointer = (volatile int32_t*)rawPointer; do { result = *pointer; } while (!tsf_cas32(pointer, result, result + value)); return result; } #define TSF_HAVE_XADD 1 #endif #ifdef TSF_HAVE_X86_INLINE_ASM_FENCES /* This is a fence that would have no hardware effect on TSO architectures. Placing * it between two operations gives you guarantees like those that TSO would have * given you. */ static TSF_inline void tsf_basic_fence(void) { asm volatile ("" : : : "memory"); } /* This is a fence that you would need even on TSO and guarantees memory SC. */ static TSF_inline void tsf_strong_fence(void) { asm volatile ("mfence" : : : "memory"); } #define TSF_HAVE_FENCES 1 #elif !defined(TSF_HAVE_PTHREAD) static TSF_inline void tsf_basic_fence(void) { } static TSF_inline void tsf_strong_fence(void) { } #define TSF_HAVE_FENCES 1 #endif struct tsf_mutex { #ifdef TSF_HAVE_PTHREAD pthread_mutex_t mutex; #else char dummy; #endif }; struct tsf_atomic_count { #if !defined(TSF_HAVE_XADD) tsf_mutex_t mutex; #endif volatile uint32_t count; }; static inline void tsf_yield(void) { #ifdef HAVE_SCHED_YIELD sched_yield(); #endif } static inline void* tsf_thread_self(void) { #if TSF_HAVE_PTHREAD return (void*)pthread_self(); #else return NULL; #endif } /* Note that we assume that locking operations always succeed. There is little point in worrying about what happens if they fail. */ static inline void tsf_mutex_init(tsf_mutex_t *mutex) { #ifdef TSF_HAVE_PTHREAD pthread_mutex_init(&mutex->mutex, NULL); #endif } static inline void tsf_mutex_lock(tsf_mutex_t *mutex) { #ifdef TSF_HAVE_PTHREAD pthread_mutex_lock(&mutex->mutex); #endif } static inline void tsf_mutex_unlock(tsf_mutex_t *mutex) { #ifdef TSF_HAVE_PTHREAD pthread_mutex_unlock(&mutex->mutex); #endif } static inline void tsf_mutex_destroy(tsf_mutex_t *mutex) { #ifdef TSF_HAVE_PTHREAD pthread_mutex_destroy(&mutex->mutex); #endif } static inline void tsf_atomic_count_init(tsf_atomic_count_t *count, uint32_t initial_value) { count->count = initial_value; #if !defined(TSF_HAVE_XADD) tsf_mutex_init(&count->mutex); #endif } static inline void tsf_atomic_count_destroy(tsf_atomic_count_t *count) { #if !defined(TSF_HAVE_XADD) tsf_mutex_destroy(&count->mutex); #endif } static inline uint32_t tsf_atomic_count_value(tsf_atomic_count_t *count) { return count->count; } static inline void tsf_atomic_count_set_value(tsf_atomic_count_t *count, uint32_t value) { count->count = value; } static inline uint32_t tsf_atomic_count_xadd(tsf_atomic_count_t *count, uint32_t delta) { #ifdef TSF_HAVE_XADD return tsf_xadd32(&count->count, delta); #else uint32_t result; tsf_mutex_lock(&count->mutex); result = count->count; count->count += delta; tsf_mutex_unlock(&count->mutex); return result; #endif } static inline uint32_t tsf_atomic_count_xsub(tsf_atomic_count_t *count, uint32_t delta) { return tsf_atomic_count_xadd(count, (uint32_t)-(int32_t)delta); } struct tsf_once { #ifdef TSF_HAVE_PTHREAD #if defined(TSF_HAVE_BUILTIN_CAS) && defined(TSF_HAVE_FENCES) volatile int32_t state; #else pthread_once_t once_control; #endif #else tsf_bool_t is_initialized; #endif }; #ifdef TSF_HAVE_PTHREAD #if defined(TSF_HAVE_BUILTIN_CAS) && defined(TSF_HAVE_FENCES) #define TSF_ONCE_INIT { 0 } #else #define TSF_ONCE_INIT { PTHREAD_ONCE_INIT } #endif #else #define TSF_ONCE_INIT { tsf_false } #endif static inline void tsf_once(tsf_once_t *control, void (*action)(void)) { #ifdef TSF_HAVE_PTHREAD #if defined(TSF_HAVE_BUILTIN_CAS) && defined(TSF_HAVE_FENCES) /* Define the possible values of control->state. */ static const int32_t notInitialized = 0; static const int32_t initializing = 1; static const int32_t initialized = 2; for (;;) { int32_t current_state = control->state; if (current_state == initialized) { tsf_basic_fence(); break; } else if (current_state == initializing) { tsf_yield(); } else if (current_state == notInitialized) { if (tsf_cas32(&control->state, notInitialized, initializing)) { action(); tsf_basic_fence(); control->state = initialized; break; } else { tsf_yield(); } } else { /* should never get here. */ abort(); } } #else pthread_once(&control->once_control, action); #endif #else if (!control->is_initialized) { action(); control->is_initialized = tsf_true; } #endif } #if defined(TSF_HAVE_FENCES) #define TSF_HAVE_INIT_POINTER 1 static inline void *tsf_init_pointer(void **pointer, void *(*initializer)(void)) { void *result = *pointer; if (result) { tsf_basic_fence(); } else { result = initializer(); tsf_basic_fence(); *pointer = result; } return result; } #else #undef TSF_HAVE_INIT_POINTER #endif #ifdef __cplusplus } #endif #endif // FP_TSF_ATOMICS_H