diff options
author | David Robillard <d@drobilla.net> | 2013-01-11 03:35:17 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2013-01-11 03:35:17 +0000 |
commit | f8aa3ee7621758b35ba52a5b17972fa4144710ae (patch) | |
tree | 2bf934c558b5c97f858be03101fa1544ecf6835d | |
parent | 77d36f84343baa0864e742834be15c0296dc4389 (diff) | |
download | raul-f8aa3ee7621758b35ba52a5b17972fa4144710ae.tar.gz raul-f8aa3ee7621758b35ba52a5b17972fa4144710ae.tar.bz2 raul-f8aa3ee7621758b35ba52a5b17972fa4144710ae.zip |
Use C++11 atomics.
git-svn-id: http://svn.drobilla.net/lad/trunk/raul@4916 a436a847-0d15-0410-975c-d299462d15a1
-rw-r--r-- | raul/AtomicInt.hpp | 96 | ||||
-rw-r--r-- | raul/AtomicPtr.hpp | 64 | ||||
-rw-r--r-- | raul/DoubleBuffer.hpp | 9 | ||||
-rw-r--r-- | raul/Maid.hpp | 17 | ||||
-rw-r--r-- | raul/SRMWQueue.hpp | 40 | ||||
-rw-r--r-- | raul/SRSWQueue.hpp | 18 | ||||
-rw-r--r-- | test/atomic_test.cpp | 76 | ||||
-rw-r--r-- | test/queue_test.cpp | 8 | ||||
-rw-r--r-- | test/thread_test.cpp | 4 | ||||
-rw-r--r-- | wscript | 2 |
10 files changed, 48 insertions, 286 deletions
diff --git a/raul/AtomicInt.hpp b/raul/AtomicInt.hpp deleted file mode 100644 index 172af6b..0000000 --- a/raul/AtomicInt.hpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - This file is part of Raul. - Copyright 2007-2012 David Robillard <http://drobilla.net> - - Raul 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 any later version. - - Raul 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 Raul. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef RAUL_ATOMIC_INT_HPP -#define RAUL_ATOMIC_INT_HPP - -#include "raul/barrier.hpp" - -namespace Raul { - -/** Atomic integer. - * \ingroup raul - */ -class AtomicInt { -public: - inline AtomicInt(int val) : _val(val) {} - - inline AtomicInt(const AtomicInt& copy) : _val(copy.get()) {} - - inline int get() const { - Raul::barrier(); - return _val; - } - - inline void operator=(int val) { - _val = val; - Raul::barrier(); - } - - inline void operator=(const AtomicInt& val) { - _val = val.get(); - Raul::barrier(); - } - - inline AtomicInt& operator++() { return operator+=(1); } - inline AtomicInt& operator--() { return operator-=(1); } - inline bool operator==(int val) const { return get() == val; } - inline int operator+(int val) const { return get() + val; } - inline int operator-(int val) const { return get() - val; } - - inline bool operator==(const AtomicInt& other) const { - Raul::barrier(); - return _val == other._val; - } - - inline AtomicInt& operator+=(int val) { - __sync_fetch_and_add(&_val, val); - return *this; - } - - inline AtomicInt& operator-=(int val) { - __sync_fetch_and_sub(&_val, val); - return *this; - } - - /** Set value to @a val iff current value is @a old. - * @return true iff set succeeded. - */ - inline bool compare_and_exchange(int old, int val) { - return __sync_bool_compare_and_swap(&_val, old, val); - } - - /** Add val to value. - * @return value immediately before addition took place. - */ - inline int exchange_and_add(int val) { - return __sync_fetch_and_add(&_val, val); - } - - /** Decrement value. - * @return true if value is now 0, otherwise false. - */ - inline bool decrement_and_test() { - return __sync_sub_and_fetch(&_val, 1) == 0; - } - -private: - int _val; -}; - -} // namespace Raul - -#endif // RAUL_ATOMIC_INT_HPP diff --git a/raul/AtomicPtr.hpp b/raul/AtomicPtr.hpp deleted file mode 100644 index 0dc369b..0000000 --- a/raul/AtomicPtr.hpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - This file is part of Raul. - Copyright 2007-2012 David Robillard <http://drobilla.net> - - Raul 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 any later version. - - Raul 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 Raul. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef RAUL_ATOMIC_PTR_HPP -#define RAUL_ATOMIC_PTR_HPP - -#include <cstddef> - -#include "raul/barrier.hpp" - -namespace Raul { - -/** Atomic pointer. - * \ingroup raul - */ -template<typename T> -class AtomicPtr { -public: - inline AtomicPtr() : _val(NULL) {} - - inline AtomicPtr(const AtomicPtr& copy) : _val(copy.get()) {} - - inline T* get() const { - Raul::barrier(); - return static_cast<T*>(_val); - } - - inline bool operator==(const AtomicPtr& other) const { - Raul::barrier(); - return _val == other._val; - } - - inline void operator=(T* val) { - _val = val; - Raul::barrier(); - } - - /** Set value to @a val iff current value is @a old. - * @return true iff set succeeded. - */ - inline bool compare_and_exchange(void* old, void* val) { - return __sync_bool_compare_and_swap(&_val, old, val); - } - -private: - void* _val; -}; - -} // namespace Raul - -#endif // RAUL_ATOMIC_PTR_HPP diff --git a/raul/DoubleBuffer.hpp b/raul/DoubleBuffer.hpp index 28080d6..092d529 100644 --- a/raul/DoubleBuffer.hpp +++ b/raul/DoubleBuffer.hpp @@ -17,8 +17,7 @@ #ifndef RAUL_DOUBLE_BUFFER_HPP #define RAUL_DOUBLE_BUFFER_HPP -#include "raul/AtomicInt.hpp" -#include "raul/AtomicPtr.hpp" +#include <atomic> namespace Raul { @@ -88,9 +87,9 @@ private: RAUL_DB_LOCK_READ }; - AtomicInt _state; - AtomicPtr<T> _read_val; - T _vals[2]; + std::atomic<States> _state; + std::atomic<T*> _read_val; + T _vals[2]; }; } // namespace Raul diff --git a/raul/Maid.hpp b/raul/Maid.hpp index f7870ec..337d7f9 100644 --- a/raul/Maid.hpp +++ b/raul/Maid.hpp @@ -17,7 +17,8 @@ #ifndef RAUL_MAID_HPP #define RAUL_MAID_HPP -#include "raul/AtomicPtr.hpp" +#include <atomic> + #include "raul/Disposable.hpp" #include "raul/Manageable.hpp" #include "raul/Noncopyable.hpp" @@ -50,8 +51,8 @@ public: inline void dispose(Disposable* obj) { if (obj) { while (true) { - obj->_maid_next = _disposed.get(); - if (_disposed.compare_and_exchange(obj->_maid_next, obj)) { + obj->_maid_next = _disposed.load(); + if (_disposed.compare_exchange_strong(obj->_maid_next, obj)) { return; } } @@ -83,14 +84,14 @@ public: // Atomically get the head of the disposed list Disposable* disposed; while (true) { - disposed = _disposed.get(); - if (_disposed.compare_and_exchange(disposed, NULL)) { + disposed = _disposed.load(); + if (_disposed.compare_exchange_strong(disposed, NULL)) { break; } } // Free the disposed list - for (Disposable* obj = _disposed.get(); obj;) { + for (Disposable* obj = _disposed.load(); obj;) { Disposable* const next = obj->_maid_next; delete obj; obj = next; @@ -107,8 +108,8 @@ public: } private: - AtomicPtr<Disposable> _disposed; - SharedPtr<Manageable> _managed; + std::atomic<Disposable*> _disposed; + SharedPtr<Manageable> _managed; }; } // namespace Raul diff --git a/raul/SRMWQueue.hpp b/raul/SRMWQueue.hpp index ac790ea..12b4b19 100644 --- a/raul/SRMWQueue.hpp +++ b/raul/SRMWQueue.hpp @@ -21,7 +21,6 @@ #include <cstdlib> #include <cmath> -#include "raul/AtomicInt.hpp" #include "raul/Noncopyable.hpp" namespace Raul { @@ -71,16 +70,15 @@ public: private: - // Note that _front doesn't need to be an AtomicInt since it's only accessed - // by the (single) reader thread + // Note that _front needn't be atomic since it's only used by reader - unsigned _front; ///< Circular index of element at front of queue (READER ONLY) - AtomicInt _back; ///< Circular index 1 past element at back of queue (WRITERS ONLY) - AtomicInt _write_space; ///< Remaining free space for new elements (all threads) - const unsigned _size; ///< Size of @ref _objects (you can store _size-1 objects) + unsigned _front; ///< Circular index of element at front of queue (READER ONLY) + std::atomic<int> _back; ///< Circular index 1 past element at back of queue (WRITERS ONLY) + std::atomic<int> _write_space; ///< Remaining free space for new elements (all threads) + const unsigned _size; ///< Size of @ref _objects (you can store _size-1 objects) - T* const _objects; ///< Fixed array containing queued elements - AtomicInt* const _valid; ///< Parallel array to _objects, whether loc is written or not + T* const _objects; ///< Fixed array containing queued elements + std::atomic<bool>* const _valid; ///< Parallel array to _objects, whether loc is written or not }; template<typename T> @@ -90,20 +88,21 @@ SRMWQueue<T>::SRMWQueue(size_t size) , _write_space(size) , _size(size+1) , _objects((T*)calloc(_size, sizeof(T))) - , _valid((AtomicInt*)calloc(_size, sizeof(AtomicInt))) + , _valid(new std::atomic<bool>[_size]) { assert(log2(size) - (int)log2(size) == 0); assert(size > 1); - assert(_size-1 == (unsigned)_write_space.get()); + assert(_size-1 == (unsigned)_write_space.load()); for (unsigned i = 0; i < _size; ++i) { - assert(_valid[i].get() == 0); + assert(!_valid[i]); } } template <typename T> SRMWQueue<T>::~SRMWQueue() { + delete _valid; free(_objects); } @@ -115,7 +114,7 @@ template <typename T> inline bool SRMWQueue<T>::full() const { - return (_write_space.get() <= 0); + return (_write_space.load() <= 0); } /** Push an item onto the back of the SRMWQueue - realtime-safe, not thread-safe. @@ -129,7 +128,7 @@ template <typename T> inline bool SRMWQueue<T>::push(const T& elem) { - const int old_write_space = _write_space.exchange_and_add(-1); + const int old_write_space = _write_space--; const bool already_full = (old_write_space <= 0); /* Technically right here pop could be called in the reader thread and @@ -146,11 +145,11 @@ SRMWQueue<T>::push(const T& elem) } else { // Note: _size must be a power of 2 for this to not explode when _back overflows - const unsigned write_index = (unsigned)_back.exchange_and_add(1) % _size; + const unsigned write_index = _back++ % _size; - assert(_valid[write_index] == 0); + assert(!_valid[write_index]); _objects[write_index] = elem; - ++(_valid[write_index]); + _valid[write_index] = true; return true; } @@ -164,7 +163,7 @@ template <typename T> inline bool SRMWQueue<T>::empty() const { - return (_valid[_front].get() == 0); + return (!_valid[_front].load()); } /** Return the element at the front of the queue without removing it. @@ -190,12 +189,11 @@ template <typename T> inline void SRMWQueue<T>::pop() { - assert(_valid[_front] == 1); - --(_valid[_front]); + _valid[_front] = false; _front = (_front + 1) % (_size); - if (_write_space.get() < 0) + if (_write_space.load() < 0) _write_space = 1; else ++_write_space; diff --git a/raul/SRSWQueue.hpp b/raul/SRSWQueue.hpp index 409497b..b9554a6 100644 --- a/raul/SRSWQueue.hpp +++ b/raul/SRSWQueue.hpp @@ -17,9 +17,9 @@ #ifndef RAUL_SRSW_QUEUE_HPP #define RAUL_SRSW_QUEUE_HPP +#include <atomic> #include <cassert> -#include "raul/AtomicInt.hpp" #include "raul/Noncopyable.hpp" namespace Raul { @@ -60,9 +60,9 @@ public: inline void pop(); private: - AtomicInt _front; ///< Index to front of queue (circular) - AtomicInt _back; ///< Index to back of queue (one past last element) (circular) - const size_t _size; ///< Size of @ref _objects (you can store _size-1 objects) + std::atomic<size_t> _front; ///< Index to front of queue (circular) + std::atomic<size_t> _back; ///< Index to back of queue (one past last element) (circular) + const size_t _size; ///< Size of @ref _objects (you can store _size-1 objects) T* const _objects; ///< Fixed array containing queued elements }; @@ -88,7 +88,7 @@ template <typename T> inline bool SRSWQueue<T>::empty() const { - return (_back.get() == _front.get()); + return (_back.load() == _front.load()); } /** Return whether or not the queue is full. @@ -97,7 +97,7 @@ template <typename T> inline bool SRSWQueue<T>::full() const { - return (((_front.get() - _back.get() + _size) % _size) == 1); + return (((_front.load() - _back.load() + _size) % _size) == 1); } /** Return the element at the front of the queue without removing it @@ -106,7 +106,7 @@ template <typename T> inline T& SRSWQueue<T>::front() const { - return _objects[_front.get()]; + return _objects[_front.load()]; } /** Push an item onto the back of the SRSWQueue - realtime-safe, not thread-safe. @@ -121,7 +121,7 @@ SRSWQueue<T>::push(const T& elem) if (full()) { return false; } else { - unsigned back = _back.get(); + unsigned back = _back.load(); _objects[back] = elem; _back = (back + 1) % _size; return true; @@ -141,7 +141,7 @@ SRSWQueue<T>::pop() assert(!empty()); assert(_size > 0); - _front = (_front.get() + 1) % (_size); + _front = (_front.load() + 1) % (_size); } } // namespace Raul diff --git a/test/atomic_test.cpp b/test/atomic_test.cpp deleted file mode 100644 index 5b6260c..0000000 --- a/test/atomic_test.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - This file is part of Raul. - Copyright 2007-2012 David Robillard <http://drobilla.net> - - Raul 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 any later version. - - Raul 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 Raul. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "raul/AtomicInt.hpp" -#include "raul/AtomicPtr.hpp" - -using namespace std; -using namespace Raul; - -#define TEST(cond) { if (!(cond)) return 1; } - -int -main() -{ - /* TODO: Actually test functionality... */ - AtomicInt i(0); - TEST(i == 0); - - AtomicInt j(i); - TEST(i == j); - - ++i; - TEST(i == 1); - - --i; - TEST(i == 0); - - TEST(i + 1 == 1); - TEST(i - 1 == -1); - - i += 2; - TEST(i == 2); - - i -= 4; - TEST(i == -2); - - TEST(i.compare_and_exchange(-2, 0)); - TEST(i == 0); - - TEST(i.exchange_and_add(5) == 0); - TEST(i == 5); - - i = 1; - TEST(i == 1); - TEST(i.decrement_and_test()); - - int five = 5; - int seven = 7; - AtomicPtr<int> p; - TEST(p.get() == NULL); - - AtomicPtr<int> q(p); - TEST(p == q); - - p = &five; - TEST(p.get() == &five); - TEST(*p.get() == 5); - TEST(p.compare_and_exchange(&five, &seven)); - TEST(p.get() == &seven); - TEST(*p.get() = 7); - - return 0; -} diff --git a/test/queue_test.cpp b/test/queue_test.cpp index bcdf039..b13c13f 100644 --- a/test/queue_test.cpp +++ b/test/queue_test.cpp @@ -19,11 +19,11 @@ #include <stdlib.h> #include <algorithm> +#include <atomic> #include <iostream> #include <string> #include <vector> -#include "raul/AtomicInt.hpp" #include "raul/SRMWQueue.hpp" #include "raul/SRSWQueue.hpp" #include "raul/Thread.hpp" @@ -41,8 +41,8 @@ static const unsigned PUSHES_PER_ITERATION = 3; struct Record { Record() : read_count(0), write_count(0) {} - AtomicInt read_count; - AtomicInt write_count; + std::atomic<int> read_count; + std::atomic<int> write_count; }; Record data[NUM_DATA]; @@ -90,7 +90,7 @@ data_is_sane() { unsigned ret = 0; for (unsigned i = 0; i < NUM_DATA; ++i) { - unsigned diff = abs(data[i].read_count.get() - data[i].write_count.get()); + unsigned diff = abs(data[i].read_count.load() - data[i].write_count.load()); ret += diff; } diff --git a/test/thread_test.cpp b/test/thread_test.cpp index f823936..f59894f 100644 --- a/test/thread_test.cpp +++ b/test/thread_test.cpp @@ -14,9 +14,9 @@ along with Raul. If not, see <http://www.gnu.org/licenses/>. */ +#include <atomic> #include <iostream> -#include "raul/AtomicInt.hpp" #include "raul/Semaphore.hpp" #include "raul/Thread.hpp" #include "raul/ThreadVar.hpp" @@ -25,7 +25,7 @@ using namespace std; using namespace Raul; Raul::ThreadVar<int> var(0); -Raul::AtomicInt n_errors(0); +std::atomic<int> n_errors(0); class Waiter : public Raul::Thread { public: @@ -43,6 +43,7 @@ def options(opt): def configure(conf): conf.load('compiler_cxx') autowaf.configure(conf) + conf.env.append_unique('CXXFLAGS', ['-std=c++11']) conf.line_just = 40 autowaf.display_header('Raul Configuration') @@ -76,7 +77,6 @@ def configure(conf): tests = ''' test/atom_test - test/atomic_test test/double_buffer_test test/path_test test/ptr_test |