Commit d8ffea29 authored by Christian Schulte zu Berge's avatar Christian Schulte zu Berge
Browse files

[WIP] Started drafting writable data semantics.

parent 113239ac
Loading
Loading
Loading
Loading
+5 −3
Original line number Diff line number Diff line
@@ -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:
        /**
@@ -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;
    };

}
+3 −14
Original line number Diff line number Diff line
@@ -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.");
@@ -52,7 +52,6 @@ namespace campvis {

        DataHandle dh(data);
        addDataHandle(name, dh);
        return dh;
    }

    void DataContainer::addDataHandle(const std::string& name, DataHandle dh) {
@@ -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);
    }
@@ -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));
        }

+76 −4
Original line number Diff line number Diff line
@@ -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"
@@ -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,
@@ -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.
@@ -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.
+3 −5
Original line number Diff line number Diff line
@@ -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;
    }

+12 −4
Original line number Diff line number Diff line
@@ -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.
@@ -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:
@@ -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.
@@ -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