00001
00033 #ifndef GALOIS_RUNTIME_LL_PTRLOCK_H
00034 #define GALOIS_RUNTIME_LL_PTRLOCK_H
00035
00036 #include "Galois/config.h"
00037 #include "Galois/Runtime/ll/CompilerSpecific.h"
00038
00039 #include <stdint.h>
00040 #include <cassert>
00041 #include GALOIS_CXX11_STD_HEADER(atomic)
00042
00043 namespace Galois {
00044 namespace Runtime {
00045 namespace LL {
00046
00051 template<typename T, bool isALock>
00052 class PtrLock;
00053
00054 template<typename T>
00055 class PtrLock<T, true> {
00056 std::atomic<uintptr_t> _lock;
00057
00058 GALOIS_ATTRIBUTE_NOINLINE
00059 void slow_lock() {
00060 uintptr_t oldval;
00061 do {
00062 while ((_lock.load(std::memory_order_acquire) & 1) != 0) {
00063 asmPause();
00064 }
00065 oldval = _lock.fetch_or(1, std::memory_order_acq_rel);
00066 } while (oldval & 1);
00067 assert(_lock);
00068 }
00069
00070 public:
00071 PtrLock() : _lock(0) {}
00072
00073 PtrLock(const PtrLock& p) : _lock(p._lock.load(std::memory_order_relaxed)) {}
00074
00075 PtrLock& operator=(const PtrLock& p) {
00076 if (&p == this) return *this;
00077
00078 _lock.store(p._lock.load(std::memory_order_relaxed), std::memory_order_relaxed);
00079 return *this;
00080 }
00081
00082 inline void lock() {
00083 uintptr_t oldval = _lock.load(std::memory_order_relaxed);
00084 if (oldval & 1)
00085 goto slow_path;
00086 if (!_lock.compare_exchange_weak(oldval, oldval | 1, std::memory_order_acq_rel, std::memory_order_relaxed))
00087 goto slow_path;
00088 assert(is_locked());
00089 return;
00090
00091 slow_path:
00092 slow_lock();
00093 }
00094
00095 inline void unlock() {
00096 assert(is_locked());
00097 _lock.store(_lock.load(std::memory_order_relaxed) & ~(uintptr_t)1, std::memory_order_release);
00098 }
00099
00100 inline void unlock_and_clear() {
00101 assert(is_locked());
00102 _lock.store(0, std::memory_order_release);
00103 }
00104
00105 inline void unlock_and_set(T* val) {
00106 assert(is_locked());
00107 assert(!((uintptr_t)val & 1));
00108 _lock.store((uintptr_t) val, std::memory_order_release);
00109 }
00110
00111 inline T* getValue() const {
00112 return (T*)(_lock.load(std::memory_order_relaxed) & ~(uintptr_t)1);
00113 }
00114
00115 inline void setValue(T* val) {
00116 uintptr_t nval = (uintptr_t)val;
00117 nval |= (_lock & 1);
00118
00119 _lock.store(nval, std::memory_order_relaxed);
00120 }
00121
00122 inline bool try_lock() {
00123 uintptr_t oldval = _lock.load(std::memory_order_relaxed);
00124 if ((oldval & 1) != 0)
00125 return false;
00126 oldval = _lock.fetch_or(1, std::memory_order_acq_rel);
00127 return !(oldval & 1);
00128 }
00129
00130 inline bool is_locked() const {
00131 return _lock.load(std::memory_order_acquire) & 1;
00132 }
00133
00136 inline bool CAS(T* oldval, T* newval) {
00137 assert(!((uintptr_t)oldval & 1) && !((uintptr_t)newval & 1));
00138 uintptr_t old = (uintptr_t)oldval;
00139 return _lock.compare_exchange_strong(old, (uintptr_t)newval);
00140 }
00141
00144 inline bool stealing_CAS(T* oldval, T* newval) {
00145 uintptr_t old = 1 | (uintptr_t)oldval;
00146 return _lock.compare_exchange_strong(old, 1 | (uintptr_t)newval);
00147 }
00148 };
00149
00150 template<typename T>
00151 class PtrLock<T, false> {
00152 T* _lock;
00153 public:
00154 PtrLock() : _lock() {}
00155
00156 inline void lock() {}
00157 inline void unlock() {}
00158 inline void unlock_and_clear() { _lock = 0; }
00159 inline void unlock_and_set(T* val) { _lock = val; }
00160 inline T* getValue() const { return _lock; }
00161 inline void setValue(T* val) { _lock = val; }
00162 inline bool try_lock() const { return true; }
00163 inline bool is_locked() const { return false; }
00164 inline bool CAS(T* oldval, T* newval) {
00165 if (_lock == oldval) {
00166 _lock = newval;
00167 return true;
00168 }
00169 return false;
00170 }
00171 inline bool stealing_CAS(T* oldval, T* newval) {
00172 return CAS(oldval, newval);
00173 }
00174 };
00175
00176 }
00177 }
00178 }
00179
00180 #endif