Commit c1785d9d authored by schultezub's avatar schultezub
Browse files

Started implementing real reference counting for DataHandles

 * introduced ReferenceCounted as base class
 * adapted AbstractData, DataHandle and DataContainer

Hence, there is still some work to do to - this version is still not more powerfull than the previous one.

git-svn-id: https://camplinux.in.tum.de/svn/campvis/trunk@201 bb408c1c-ae56-11e1-83d9-df6b3e0c105e
parent ee5d4d97
......@@ -2,7 +2,9 @@
namespace TUMVis {
AbstractData::AbstractData() {
AbstractData::AbstractData()
: ReferenceCounted()
{
}
AbstractData::~AbstractData() {
......
#ifndef ABSTRACTDATA_H__
#define ABSTRACTDATA_H__
#include "core/tools/referencecounted.h"
namespace TUMVis {
class DataHandle;
......@@ -10,7 +12,7 @@ namespace TUMVis {
*
* \todo
*/
class AbstractData {
class AbstractData : public ReferenceCounted {
public:
AbstractData();
......
......@@ -10,7 +10,7 @@ namespace TUMVis {
DataContainer::~DataContainer() {
// remove ownership op all owned DataHandles
for (std::map<std::string, const DataHandle*>::iterator it = _handles.begin(); it != _handles.end(); ++it) {
DataHandle::removeOwner(it->second, this);
delete it->second;
}
_handles.clear();
}
......@@ -26,14 +26,13 @@ namespace TUMVis {
tbb::spin_mutex::scoped_lock lock(_localMutex);
std::map<std::string, const DataHandle*>::iterator it = _handles.lower_bound(name);
if (it != _handles.end() && it->first == name) {
DataHandle::removeOwner(it->second, this);
delete it->second;
it->second = dh;
}
else {
_handles.insert(it, std::make_pair(name, dh));
}
}
DataHandle::addOwner(dh, this);
s_dataAdded(name, dh);
}
......
......@@ -15,13 +15,13 @@ namespace TUMVis {
* 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,
* as soon as an AbstractData instance is added to a DataContainer via DataContainer::addData(), its
* lifetime is managed by the wrapping DataHandle instance.
* lifetime is managed by the wrapping DataHandle instance and its reference counting mechanism.
* Because the DataHandles are stored as const handles, the underlying data cannot be changed anymore. This
* also ensures (hopefully) that nobody can do messy things, such as changing the data while some other
* thread is reading it or adding the same AbstractData instance twice to a DataContainer (which would
* really mess up the lifetime management!).
* thread is reading it. Theoretically this should be possible, but a correct implementation would require
* some brain fuck.
*
* \todo We definately want thread-safety here!
* \todo Check thread-safety
*
* \todo If the original data changes, the other objects accessing that very DataHandle might want to
* get notified of the change. Hence, some observer pattern might be useful.
......
#include "datahandle.h"
#include "tgt/assert.h"
#include "tgt/logmanager.h"
#include "core/datastructures/abstractdata.h"
namespace TUMVis {
const std::string DataHandle::loggerCat_ = "TUMVis.core.datastructures.DataHandle";
DataHandle::DataHandle(AbstractData* data, const DataContainer* owner /*= 0*/)
DataHandle::DataHandle(AbstractData* data)
: _data(data)
{
if (owner != 0)
addOwner(this, owner);
tgtAssert(_data != 0, "Provided data must not be 0!");
init();
}
DataHandle::~DataHandle() {
delete _data;
DataHandle::DataHandle(const DataHandle& rhs)
: _data(rhs._data)
{
init();
}
void DataHandle::addOwner(const DataHandle* handle, const DataContainer* owner) {
tgtAssert(handle != 0, "Handle must not be 0!");
tgtAssert(owner != 0, "Owning DataContainer must not be 0!");
tbb::spin_mutex::scoped_lock lock(handle->_localMutex);
handle->_owners.insert(owner);
DataHandle& DataHandle::operator=(const DataHandle& rhs) {
if (_data != rhs._data) {
AbstractData* oldData = _data;
_data = rhs._data;
init();
if (oldData) {
oldData->removeReference();
}
}
return *this;
}
void DataHandle::removeOwner(const DataHandle* handle, const DataContainer* owner) {
tgtAssert(handle != 0, "Handle must not be 0!");
tgtAssert(owner != 0, "Owning DataContainer must not be 0!");
{
tbb::spin_mutex::scoped_lock lock(handle->_localMutex);
handle->_owners.erase(owner);
}
if (handle->_owners.empty()) {
delete handle;
}
DataHandle::~DataHandle() {
_data->removeReference();
}
const AbstractData* DataHandle::getData() const {
return _data;
}
AbstractData* DataHandle::getData() {
if (_data->isShared())
_data = _data->clone();
_data->markUnsharable();
return _data;
}
void DataHandle::init() {
if (! _data->isShareable())
_data = _data->clone();
_data->addReference();
}
}
\ No newline at end of file
#ifndef datahandle_h__
#define datahandle_h__
#include "tbb/include/tbb/spin_mutex.h"
#include "tgt/logmanager.h"
#include "core/datastructures/abstractdata.h"
#include <string>
#include <set>
namespace TUMVis {
class DataContainer;
class AbstractData;
/**
* A DataHandle is responsible to manage the lifetime of an AbstractData instance.
* Therefore, it holds a pointer to the managed data object as well as a set of pointers to the
* DataContainer instances containing this very DataHandle. Each DataContainer is responsible for
* registering itsself as owner of its DataHandles (via DataHandle::addOwner()). When removing a
* DataHandle from a container, make sure to deregister via DataHandle::removeOwner().
* Therefore, it implements a reference counting technique in cooperation with AbstractData.
*
* \note For clarity: A DataHandle can have multiple owners, as soon as the owner count drops
* to 0 it 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).
* \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.
* 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
*
* \todo implement locks/mutexes as soon as we have a convenient library for that.
* \todo Check for thread-safety
*/
class DataHandle {
// DataContainer is the only class allowed to access the private constructor and ownership modification methods.
friend class DataContainer;
public:
/**
* Destructor, will delete the managed AbstractData.
* Creates a new DataHandle for the given data.
* \note By passing the data to DataHandle you will transfer its ownership to the reference
* counting mechanism. Make sure not to interfere with it or delete \a data yourself!
* \param data Data for the DataHandle
*/
virtual ~DataHandle();
DataHandle(AbstractData* data);
/**
* Grants const access to the managed AbstractData instance.
* \return _data;
* Copy-constructor
* \note If \a rhs is not shareable, this implies a copy of the data!
* \param rhs Source DataHandle
*/
const AbstractData* getData() const;
DataHandle(const DataHandle& rhs);
/**
* Grants access to the managed AbstractData instance.
* \return _data;
* Assignment operator
* \note If \a rhs is not shareable, this implies a copy of the data!
* \param rhs source DataHandle
* \return *this
*/
AbstractData* getData();
DataHandle& operator=(const DataHandle& rhs);
private:
/**
* Creates a new DataHandle for \a data and takes its ownership in terms of lifetime management.
* If \a owner != 0, it will automatically be added to the set of owners of this DataHandle, so
* in this case it is NOT needed to call DataHandle::addOwner().
*
* \param data AbstractData instance to manage
* \param owner Initial owner of this DataHandle, may be 0.
* Destructor, will delete the managed AbstractData.
*/
DataHandle(AbstractData* data, const DataContainer* owner = 0);
virtual ~DataHandle();
/**
* DO NOT USE - it is private on purpose!
*
* IMHO a DataHandle does not need a copy-constructor - in particular it could be a bad idea to use
* one, because it does not exactly what you expect. If you really need a copy-constructor, please
* make sure to implement it correctly.
* Grants const access to the managed AbstractData instance.
* \return _data;
*/
DataHandle(const DataHandle& rhs);
const AbstractData* getData() const;
/**
* DO NOT USE - it is private on purpose!
*
* IMHO a DataHandle does not need an assignment-operator - in particular it could be a bad idea to use
* one, because it does not exactly what you expect. If you really need an assignment-operator, please
* make sure to implement it correctly.
* Grants access to the managed AbstractData instance.
* \note If the data is referenced by more than one object, this implies a copy of the data!
* \return A modifyable version of the held data.
*/
DataHandle& operator=(const DataHandle& rhs);
AbstractData* getData();
/**
* Registers \a owner as owner of the DataHandle \a handle.
* \param handle DataHandle that gets another ownership.
* \param owner Owner that shall be added to the owner list of \a handle.
*/
static void addOwner(const DataHandle* handle, const DataContainer* owner);
private:
/**
* Removes \a owner from the owner list of \a handle. If afterwards the number of owners is 0, \a handle will be deleted.
* \param handle DataHandle of which to remove the ownership.
* \param owner Owner that shall be removed from the owner list of \a handle.
* Initializes the reference counting for the data.
*/
static void removeOwner(const DataHandle* handle, const DataContainer* owner);
void init();
AbstractData* const _data; ///< managed data
mutable std::set<const DataContainer*> _owners; ///< set of owners of this DataHandle
mutable tbb::spin_mutex _localMutex; ///< Mutex used when altering local members
AbstractData* _data; ///< managed data
static const std::string loggerCat_;
};
......
#include "referencecounted.h"
namespace TUMVis {
ReferenceCounted::ReferenceCounted()
: _shareable(true)
{
_refCount = 0;
}
ReferenceCounted::ReferenceCounted(const ReferenceCounted& rhs)
: _shareable(true)
{
_refCount = 0;
}
ReferenceCounted& ReferenceCounted::operator=(const ReferenceCounted& rhs) {
return *this;
}
ReferenceCounted::~ReferenceCounted() {
}
void ReferenceCounted::addReference() {
++_refCount;
}
void ReferenceCounted::removeReference() {
// TODO: I'm afraid this is not 100% thread-safe - refCount might change between atomic decrement, check and deletion...
if (--_refCount == 0)
delete this;
}
void ReferenceCounted::markUnsharable() {
_shareable = false;
}
bool ReferenceCounted::isShareable() const {
return _shareable;
}
bool ReferenceCounted::isShared() const {
return _refCount > 1;
}
}
#ifndef REFERENCECOUNTED_H__
#define REFERENCECOUNTED_H__
#include "tbb/include/tbb/atomic.h"
namespace TUMVis {
/**
* Base class for reference counted objects.
*
* \note Implementation inspired from Scott Meyers: More Effective C++, Item 29
* \todo Check thread-safety
*/
class ReferenceCounted {
public:
/**
* Create a new reference counted object.
*/
ReferenceCounted();
/**
* Copy-constructor for reference counted objects.
* \param rhs source object
*/
ReferenceCounted(const ReferenceCounted& rhs);
/**
* Assignment operator for reference counted objects
* \param rhs source object
* \return *this
*/
ReferenceCounted& operator= (const ReferenceCounted& rhs);
/**
* Pure virtual destructor.
*/
virtual ~ReferenceCounted() = 0;
/**
* Increment the reference count.
*/
void addReference();
/**
* Decrement the reference count and delete the object if necessary.
*/
void removeReference();
/**
* Mark this object as not shareable, i.e. there exist non-const pointers to this object.
*/
void markUnsharable();
/**
* Returns, whether this object is shareable, i.e. no non-const pointers to this object exist.
* \return _shareable
*/
bool isShareable() const;
/**
* Returns, whether this object has more than one references.
* \return _refCount > 1
*/
bool isShared() const;
private:
tbb::atomic<size_t> _refCount; ///< number of references to this object
bool _shareable; ///< flag whether this object is shareable, i.e. no non-const pointers to this object exist
};
}
#endif // REFERENCECOUNTED_H__
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment