blob: db41b407d0e3720827b5fb544a5b13cf7d80412c [file] [log] [blame]
#ifndef __sys_atomic_h__
#define __sys_atomic_h__ 1
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**@file
* Portable atomic operations on uint32_t.
*/
#include <stdint.h>
/******************************************************************************
* C11 atomics *
******************************************************************************/
#if defined(__STDC__) && (__STDC_VERSION__ >= 201112L) && !defined(__STDC_NO_ATOMICS__)
#include <stdatomic.h>
typedef atomic_uint sys_atomic_t;
static inline void sys_atomic_init(sys_atomic_t *ref, uint32_t value)
{
atomic_store(ref, value);
}
static inline uint32_t sys_atomic_add(sys_atomic_t *ref, uint32_t value)
{
return atomic_fetch_add(ref, value);
}
static inline uint32_t sys_atomic_sub(sys_atomic_t *ref, uint32_t value)
{
return atomic_fetch_sub(ref, value);
}
static inline uint32_t sys_atomic_get(sys_atomic_t *ref)
{
return atomic_load(ref);
}
static inline int32_t sys_atomic_set(sys_atomic_t *ref, uint32_t value)
{
return atomic_exchange(ref, value);
}
static inline void sys_atomic_destroy(sys_atomic_t *ref) {}
#elif defined(__GNUC__) || defined(__clang__)
/******************************************************************************
* GCC specific atomics *
******************************************************************************/
typedef volatile uint32_t sys_atomic_t;
static inline void sys_atomic_init(sys_atomic_t *ref, uint32_t value)
{
*ref = value;
}
static inline uint32_t sys_atomic_add(sys_atomic_t *ref, uint32_t value)
{
return __sync_fetch_and_add(ref, value);
}
static inline uint32_t sys_atomic_sub(sys_atomic_t *ref, uint32_t value)
{
return __sync_fetch_and_sub(ref, value);
}
static inline uint32_t sys_atomic_get(sys_atomic_t *ref)
{
return *ref;
}
static inline uint32_t sys_atomic_set(sys_atomic_t *ref, uint32_t value)
{
uint32_t old = *ref;
while (!__sync_bool_compare_and_swap(ref, old, value)) {
old = *ref;
}
return old;
}
static inline void sys_atomic_destroy(sys_atomic_t *ref) {}
#elif defined(__sun)
/******************************************************************************
* Solaris specific atomics *
******************************************************************************/
#include <atomic.h>
typedef volatile uint32_t sys_atomic_t;
static inline void sys_atomic_init(sys_atomic_t *ref, uint32_t value)
{
*ref = value;
}
static inline uint32_t sys_atomic_add(sys_atomic_t *ref, uint32_t value)
{
uint32_t oldValue = *ref;
atomic_add_32(ref, value);
return oldValue;
}
static inline uint32_t sys_atomic_sub(sys_atomic_t *ref, uint32_t value)
{
uint32_t oldValue = *ref;
atomic_add_32(ref, -value);
return oldValue;
}
static inline uint32_t sys_atomic_get(sys_atomic_t *ref)
{
return *ref;
}
static inline void sys_atomic_set(sys_atomic_t *ref, uint32_t value)
{
return atomic_swap_32(ref, value);
}
static inline void sys_atomic_destroy(sys_atomic_t *ref) {}
#else
/******************************************************************************
* Mutex fallback atomics *
******************************************************************************/
#include <qpid/dispatch/threading.h>
struct sys_atomic_t {
sys_mutex_t *lock;
uint32_t value;
};
typedef struct sys_atomic_t sys_atomic_t;
static inline void sys_atomic_init(sys_atomic_t *ref, uint32_t value)
{
ref->lock = sys_mutex();
ref->value = value;
}
static inline uint32_t sys_atomic_add(sys_atomic_t *ref, uint32_t value)
{
sys_mutex_lock(ref->lock);
uint32_t prev = ref->value;
ref->value += value;
sys_mutex_unlock(ref->lock);
return prev;
}
static inline uint32_t sys_atomic_sub(sys_atomic_t *ref, uint32_t value)
{
sys_mutex_lock(ref->lock);
uint32_t prev = ref->value;
ref->value -= value;
sys_mutex_unlock(ref->lock);
return prev;
}
static inline uint32_t sys_atomic_get(sys_atomic_t *ref)
{
sys_mutex_lock(ref->lock);
uint32_t value = ref->value;
sys_mutex_unlock(ref->lock);
return value;
}
static inline uint32_t sys_atomic_set(sys_atomic_t *ref, uint32_t value)
{
uint32_t old;
sys_mutex_lock(ref->lock);
old = ref->value;
ref->value = value;
sys_mutex_unlock(ref->lock);
return old;
}
static inline void sys_atomic_destroy(sys_atomic_t *ref)
{
sys_mutex_lock(ref->lock);
sys_mutex_free(ref->lock);
}
#endif
/** Atomic increase: NOTE returns value *before* increase, like i++ */
static inline uint32_t sys_atomic_inc(sys_atomic_t *ref) { return sys_atomic_add((ref), 1); }
/** Atomic decrease: NOTE returns value *before* decrease, like i-- */
static inline uint32_t sys_atomic_dec(sys_atomic_t *ref) { return sys_atomic_sub((ref), 1); }
#endif