/* This file is part of Jellyfish.
Jellyfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Jellyfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Jellyfish. If not, see .
*/
#ifndef __JELLYFISH_ATOMIC_FIELD_HPP__
#define __JELLYFISH_ATOMIC_FIELD_HPP__
#include
namespace jflib {
/* Define a_get, a_set and a_update
*/
template
T a_load(T *x) { return *(volatile T*)x; }
template
T a_store(T* lhs, const U& rhs) {
return (*(volatile T*)lhs = rhs);
}
template
T* a_load_ptr(T* x) { return a_load((T**)&x); }
template
T* a_store_ptr(T* x, const U& rhs) { return a_store((T**)&x, rhs); }
/** Set value to f(value).
* @return f(value)
*
* The function f may be called more than once. Be careful about
* side effects (probably better if f has no side effects).
*/
template
T a_update(T* x, T (*f)(T)) {
T ov(a_load(x));
T nv(f(ov));
while(!cas(x, ov, nv, &ov)) { nv = f(ov); }
return nv;
}
template
T a_load(T &x) { return a_load(&x); }
template
T a_store(T &lhs, const U& rhs) { return a_store(&lhs, rhs); }
/* POD with atomic operators.
*/
template
struct atomic_pod {
typedef T type;
T x;
};
#define AF_COMPOUND_ASSIGN(op) \
template \
T operator op ## = (atomic_pod &x, const U &rhs) { \
T ov(a_load(&x.x)); \
T nv(ov op rhs); \
while(!cas(&x.x, ov, nv, &ov)) { nv = ov op rhs; } \
return nv; \
}
AF_COMPOUND_ASSIGN(+);
AF_COMPOUND_ASSIGN(-);
AF_COMPOUND_ASSIGN(*);
AF_COMPOUND_ASSIGN(/);
AF_COMPOUND_ASSIGN(%);
AF_COMPOUND_ASSIGN(>>);
AF_COMPOUND_ASSIGN(<<);
AF_COMPOUND_ASSIGN(&);
AF_COMPOUND_ASSIGN(|);
AF_COMPOUND_ASSIGN(^);
/** Set value to f(value).
* @return f(value)
*
* The function f may be called more than once. Be careful about
* side effects (probably better if f has no side effects).
*/
template
T a_load(atomic_pod &x) { return a_load(&x.x); }
template
T a_store(atomic_pod &lhs, const U &rhs) {
return a_store(&lhs.x, rhs);
}
template
T a_update(atomic_pod &x, T (*f)(T)) {
return a_update(&x.x, f);
}
/* Similar to an atomic_pod, but not a POD, because of its
constructor and other member functions. Easier to use.
*/
template
class atomic_field : public atomic_pod {
public:
typedef typename atomic_pod::type type;
explicit atomic_field() { }
explicit atomic_field(const T& v) { a_store(&this->x, v); }
atomic_field& operator=(const atomic_pod& rhs) { a_store(&this->x, rhs.x); return *this; }
atomic_field& operator=(const T& v) { a_store(&this->x, v); return *this; }
operator T() const { return a_load(&this->x); }
T update(T (*f)(T)) { return a_update(&this->x, f); }
};
template
T a_load(atomic_field &x) { return a_load((atomic_pod&)x); }
template
atomic_field& a_store(atomic_field& lhs, const U& rhs) { a_store((atomic_pod&)lhs, rhs); return lhs; }
template
T a_update(atomic_field& x, T (*f)(T)) { return a_update((atomic_pod&)x, f); }
/* Allows atomic operation on any (already allocated) data.
*/
template
class atomic_ref {
T *ptr;
public:
typedef T type;
explicit atomic_ref(T& x) : ptr(&x) { }
explicit atomic_ref(T* x) : ptr(x) { }
atomic_ref& operator=(const T& v) { a_store(ptr, v); return *this; }
operator T() const { assert(ptr != 0); return a_load(ptr); }
T* operator&() const { return ptr; }
};
#define AR_COMPOUND_ASSIGN(op) \
template \
T operator op ## = (atomic_ref &x, const U &rhs) { \
T ov(x); \
T nv(ov op rhs); \
while(!cas(&x, ov, nv, &ov)) { nv = ov op rhs; } \
return nv; \
}
AR_COMPOUND_ASSIGN(+);
AR_COMPOUND_ASSIGN(-);
AR_COMPOUND_ASSIGN(*);
AR_COMPOUND_ASSIGN(/);
AR_COMPOUND_ASSIGN(%);
AR_COMPOUND_ASSIGN(>>);
AR_COMPOUND_ASSIGN(<<);
AR_COMPOUND_ASSIGN(&);
AR_COMPOUND_ASSIGN(|);
AR_COMPOUND_ASSIGN(^);
}
#endif /* __JELLYFISH_ATOMIC_FIELD_HPP__ */