Loading core/datastructures/abstractdata.h +5 −3 Original line number Diff line number Diff line Loading @@ -53,13 +53,12 @@ namespace campvis { // ================================================================================================ class DataHandle; /** * Abstract base class for data handled by a DataHandle and stored in a DataContainer. */ class CAMPVIS_CORE_API AbstractData { friend class DataHandle; template<bool isWriteLock> friend class AccessLockedAbstractData; public: /** Loading Loading @@ -102,6 +101,9 @@ namespace campvis { /// Should be only accessed by DataHandle (therefore the friendship) in order to avoid /// multiple owning groups for the same object. std::weak_ptr<AbstractData> _weakPtr; /// Reader-writer mutex for accessing the data. mutable tbb::queuing_rw_mutex _mutex; }; } Loading core/datastructures/datacontainer.cpp +3 −14 Original line number Diff line number Diff line Loading @@ -41,10 +41,10 @@ namespace campvis { _handles.clear(); } DataHandle DataContainer::addData(const std::string& name, AbstractData* data) { void DataContainer::addData(const std::string& name, AbstractData* data) { if (name.empty()) { LERROR("Tried to add data with empty name to DataContainer."); return DataHandle(0); return; } cgtAssert(data != 0, "The Data must not be 0."); Loading @@ -52,7 +52,6 @@ namespace campvis { DataHandle dh(data); addDataHandle(name, dh); return dh; } void DataContainer::addDataHandle(const std::string& name, DataHandle dh) { Loading @@ -75,16 +74,6 @@ namespace campvis { return _handles.find(a, name); } DataHandle DataContainer::getData(const std::string& name) const { tbb::concurrent_hash_map<std::string, DataHandle>::const_accessor a; if (_handles.find(a, name)) { return a->second; } else { return DataHandle(0); } } void DataContainer::removeData(const std::string& name) { _handles.erase(name); } Loading @@ -94,7 +83,7 @@ namespace campvis { toReturn.reserve(_handles.size()); tbb::spin_mutex::scoped_lock lock(_localMutex); for (tbb::concurrent_hash_map<std::string, DataHandle>::const_iterator it = _handles.begin(); it != _handles.end(); ++it) { for (auto it = _handles.begin(); it != _handles.end(); ++it) { toReturn.push_back(std::make_pair(it->first, it->second)); } Loading core/datastructures/datacontainer.h +76 −4 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ #include "sigslot/sigslot.h" #include <tbb/concurrent_hash_map.h> #include <tbb/queuing_rw_mutex.h> #include <tbb/spin_mutex.h> #include "core/coreapi.h" Loading @@ -40,6 +41,69 @@ namespace campvis { class AbstractData; template<bool writeAccess = false> class ScopedData : public tbb::queuing_rw_mutex::scoped_lock { public: ScopedData() : tbb::queuing_rw_mutex::scoped_lock() , _data(nullptr) {} ScopedData(std::shared_ptr<AbstractData> data) : tbb::queuing_rw_mutex::scoped_lock(data->_mutex, writeAccess) , _data(data) {} ScopedData(const ScopedData<writeAccess>& other) : tbb::queuing_rw_mutex::scoped_lock(other._data->_mutex, writeAccess) , _data(other._data) {} ScopedData<writeAccess>& operator=(ScopedData<writeAccess> rhs) { // tbb::queuing_rw_mutex::scoped_lock is not_assignable. Let's pray that this release-acquire move is working as intended... this->release(); std::swap(this->_data, rhs._data); this->acquire(_data->_mutex, writeAccess); return *this; } const AbstractData* getData() const { return _data.get(); } AbstractData* getWritableData() { static_assert(writeAccess, "Can not access non-const AbstractData from a read-only AccessLock!"); return _data.get(); } /** * Implicit conversion operator to const AbstractData*. */ operator const AbstractData*() const { return _data.get(); } /** * Implicit arrow operator to const AbstractData*. */ const AbstractData* operator->() const { return _data.get(); } DataHandle getDataHandle() const { return _data; } WritableDataHandle getWritableDataHandle() const { static_assert(writeAccess, "Can not access non-const DataHandle from a read-only AccessLock!"); return _data; } private: WritableDataHandle _data; }; /** * A DataContainer manages instances of AbstractData and offers access to them via string identifiers (names/keys). * Therefore, it stores them in DataHandles which take ownership of the AbstractData instance. Hence, Loading Loading @@ -71,9 +135,8 @@ namespace campvis { * * \param name Key for accessing the DataHandle within this DataContainer. * \param data The data to wrap in a DataHandle and add to this DataContainer, must not be 0. * \return A DataHandle containing \a data. */ DataHandle addData(const std::string& name, AbstractData* data); void addData(const std::string& name, AbstractData* data); /** * Adds the given DataHandle \a data, accessible by the key \name, to this DataContainer. Loading @@ -99,7 +162,16 @@ namespace campvis { * \param name Key of the DataHandle to search for * \return The stored DataHandle with the given name, an empty DataHandle if no such DataHandle exists. */ DataHandle getData(const std::string& name) const; template<bool writeAccess = false> ScopedData<writeAccess> getData(const std::string& name) const { tbb::concurrent_hash_map<std::string, DataHandle>::const_accessor a; if (_handles.find(a, name)) { return ScopedData<writeAccess>(a->second); } else { return ScopedData<writeAccess>(); } } /** * Removes the DataHandle with the given name from this container. Loading core/datastructures/datahandle.cpp +3 −5 Original line number Diff line number Diff line Loading @@ -62,12 +62,10 @@ namespace campvis { } const AbstractData* DataHandle::getData() const { return _ptr.get(); } clock_t DataHandle::getTimestamp() const { auto a = getData(); auto b = getData<true>(); return _timestamp; } Loading core/datastructures/datahandle.h +12 −4 Original line number Diff line number Diff line Loading @@ -33,6 +33,12 @@ namespace campvis { class AbstractData; typedef std::shared_ptr<const AbstractData> DataHandle; typedef std::shared_ptr<AbstractData> WritableDataHandle; #if 0 /** * A DataHandle is responsible to manage the lifetime of an AbstractData instance. * Therefore, it implements a reference counting technique in cooperation with AbstractData. Loading @@ -42,11 +48,10 @@ namespace campvis { * * Concurrent access to the same AbstractData instance via different DataHandles is safe. * * \note For clarity: An AbstractData instance can be referenced by multiple DataHandles. As soon * as it is afterwards reference by 0 DataHandles, the AbstractData instance will be destroyed. * as it is referenced by 0 DataHandles afterwards, the AbstractData instance will be destroyed. * Also remember that a DataHandle takes ownership of the given AbstractData instance. So do * not delete it once it has been assigned to a DataHandle (respectively DataContainer) or mess * with its reference counting! * \note Reference counting implementation inspired from Scott Meyers: More Effective C++, Item 29 */ class CAMPVIS_CORE_API DataHandle { public: Loading Loading @@ -82,7 +87,10 @@ namespace campvis { * Grants const access to the managed AbstractData instance. * \return _data; */ const AbstractData* getData() const; template<bool writeAccess = false> AccessLockedAbstractData<writeAccess> getData() const { return _ptr ? AccessLockedAbstractData<writeAccess>(_ptr) : AccessLockedAbstractData<writeAccess>(); } /** * Gets the timestamp when this data has been created. Loading @@ -95,7 +103,7 @@ namespace campvis { std::shared_ptr<AbstractData> _ptr; ///< managed data clock_t _timestamp; ///< Timestamp when this data has been created }; #endif } #endif // datahandle_h__ No newline at end of file Loading
core/datastructures/abstractdata.h +5 −3 Original line number Diff line number Diff line Loading @@ -53,13 +53,12 @@ namespace campvis { // ================================================================================================ class DataHandle; /** * Abstract base class for data handled by a DataHandle and stored in a DataContainer. */ class CAMPVIS_CORE_API AbstractData { friend class DataHandle; template<bool isWriteLock> friend class AccessLockedAbstractData; public: /** Loading Loading @@ -102,6 +101,9 @@ namespace campvis { /// Should be only accessed by DataHandle (therefore the friendship) in order to avoid /// multiple owning groups for the same object. std::weak_ptr<AbstractData> _weakPtr; /// Reader-writer mutex for accessing the data. mutable tbb::queuing_rw_mutex _mutex; }; } Loading
core/datastructures/datacontainer.cpp +3 −14 Original line number Diff line number Diff line Loading @@ -41,10 +41,10 @@ namespace campvis { _handles.clear(); } DataHandle DataContainer::addData(const std::string& name, AbstractData* data) { void DataContainer::addData(const std::string& name, AbstractData* data) { if (name.empty()) { LERROR("Tried to add data with empty name to DataContainer."); return DataHandle(0); return; } cgtAssert(data != 0, "The Data must not be 0."); Loading @@ -52,7 +52,6 @@ namespace campvis { DataHandle dh(data); addDataHandle(name, dh); return dh; } void DataContainer::addDataHandle(const std::string& name, DataHandle dh) { Loading @@ -75,16 +74,6 @@ namespace campvis { return _handles.find(a, name); } DataHandle DataContainer::getData(const std::string& name) const { tbb::concurrent_hash_map<std::string, DataHandle>::const_accessor a; if (_handles.find(a, name)) { return a->second; } else { return DataHandle(0); } } void DataContainer::removeData(const std::string& name) { _handles.erase(name); } Loading @@ -94,7 +83,7 @@ namespace campvis { toReturn.reserve(_handles.size()); tbb::spin_mutex::scoped_lock lock(_localMutex); for (tbb::concurrent_hash_map<std::string, DataHandle>::const_iterator it = _handles.begin(); it != _handles.end(); ++it) { for (auto it = _handles.begin(); it != _handles.end(); ++it) { toReturn.push_back(std::make_pair(it->first, it->second)); } Loading
core/datastructures/datacontainer.h +76 −4 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ #include "sigslot/sigslot.h" #include <tbb/concurrent_hash_map.h> #include <tbb/queuing_rw_mutex.h> #include <tbb/spin_mutex.h> #include "core/coreapi.h" Loading @@ -40,6 +41,69 @@ namespace campvis { class AbstractData; template<bool writeAccess = false> class ScopedData : public tbb::queuing_rw_mutex::scoped_lock { public: ScopedData() : tbb::queuing_rw_mutex::scoped_lock() , _data(nullptr) {} ScopedData(std::shared_ptr<AbstractData> data) : tbb::queuing_rw_mutex::scoped_lock(data->_mutex, writeAccess) , _data(data) {} ScopedData(const ScopedData<writeAccess>& other) : tbb::queuing_rw_mutex::scoped_lock(other._data->_mutex, writeAccess) , _data(other._data) {} ScopedData<writeAccess>& operator=(ScopedData<writeAccess> rhs) { // tbb::queuing_rw_mutex::scoped_lock is not_assignable. Let's pray that this release-acquire move is working as intended... this->release(); std::swap(this->_data, rhs._data); this->acquire(_data->_mutex, writeAccess); return *this; } const AbstractData* getData() const { return _data.get(); } AbstractData* getWritableData() { static_assert(writeAccess, "Can not access non-const AbstractData from a read-only AccessLock!"); return _data.get(); } /** * Implicit conversion operator to const AbstractData*. */ operator const AbstractData*() const { return _data.get(); } /** * Implicit arrow operator to const AbstractData*. */ const AbstractData* operator->() const { return _data.get(); } DataHandle getDataHandle() const { return _data; } WritableDataHandle getWritableDataHandle() const { static_assert(writeAccess, "Can not access non-const DataHandle from a read-only AccessLock!"); return _data; } private: WritableDataHandle _data; }; /** * A DataContainer manages instances of AbstractData and offers access to them via string identifiers (names/keys). * Therefore, it stores them in DataHandles which take ownership of the AbstractData instance. Hence, Loading Loading @@ -71,9 +135,8 @@ namespace campvis { * * \param name Key for accessing the DataHandle within this DataContainer. * \param data The data to wrap in a DataHandle and add to this DataContainer, must not be 0. * \return A DataHandle containing \a data. */ DataHandle addData(const std::string& name, AbstractData* data); void addData(const std::string& name, AbstractData* data); /** * Adds the given DataHandle \a data, accessible by the key \name, to this DataContainer. Loading @@ -99,7 +162,16 @@ namespace campvis { * \param name Key of the DataHandle to search for * \return The stored DataHandle with the given name, an empty DataHandle if no such DataHandle exists. */ DataHandle getData(const std::string& name) const; template<bool writeAccess = false> ScopedData<writeAccess> getData(const std::string& name) const { tbb::concurrent_hash_map<std::string, DataHandle>::const_accessor a; if (_handles.find(a, name)) { return ScopedData<writeAccess>(a->second); } else { return ScopedData<writeAccess>(); } } /** * Removes the DataHandle with the given name from this container. Loading
core/datastructures/datahandle.cpp +3 −5 Original line number Diff line number Diff line Loading @@ -62,12 +62,10 @@ namespace campvis { } const AbstractData* DataHandle::getData() const { return _ptr.get(); } clock_t DataHandle::getTimestamp() const { auto a = getData(); auto b = getData<true>(); return _timestamp; } Loading
core/datastructures/datahandle.h +12 −4 Original line number Diff line number Diff line Loading @@ -33,6 +33,12 @@ namespace campvis { class AbstractData; typedef std::shared_ptr<const AbstractData> DataHandle; typedef std::shared_ptr<AbstractData> WritableDataHandle; #if 0 /** * A DataHandle is responsible to manage the lifetime of an AbstractData instance. * Therefore, it implements a reference counting technique in cooperation with AbstractData. Loading @@ -42,11 +48,10 @@ namespace campvis { * * Concurrent access to the same AbstractData instance via different DataHandles is safe. * * \note For clarity: An AbstractData instance can be referenced by multiple DataHandles. As soon * as it is afterwards reference by 0 DataHandles, the AbstractData instance will be destroyed. * as it is referenced by 0 DataHandles afterwards, the AbstractData instance will be destroyed. * Also remember that a DataHandle takes ownership of the given AbstractData instance. So do * not delete it once it has been assigned to a DataHandle (respectively DataContainer) or mess * with its reference counting! * \note Reference counting implementation inspired from Scott Meyers: More Effective C++, Item 29 */ class CAMPVIS_CORE_API DataHandle { public: Loading Loading @@ -82,7 +87,10 @@ namespace campvis { * Grants const access to the managed AbstractData instance. * \return _data; */ const AbstractData* getData() const; template<bool writeAccess = false> AccessLockedAbstractData<writeAccess> getData() const { return _ptr ? AccessLockedAbstractData<writeAccess>(_ptr) : AccessLockedAbstractData<writeAccess>(); } /** * Gets the timestamp when this data has been created. Loading @@ -95,7 +103,7 @@ namespace campvis { std::shared_ptr<AbstractData> _ptr; ///< managed data clock_t _timestamp; ///< Timestamp when this data has been created }; #endif } #endif // datahandle_h__ No newline at end of file