Newer
Older
James Cook
committed
/**
* @file llhandle.h
* @brief "Handle" to an object (usually a floater) whose lifetime you don't
* control.
*
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
James Cook
committed
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
James Cook
committed
*
* This library 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
* Lesser General Public License for more details.
James Cook
committed
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
James Cook
committed
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
James Cook
committed
* $/LicenseInfo$
*/
#ifndef LLHANDLE_H
#define LLHANDLE_H
#include "llpointer.h"
Richard Linden
committed
#include <boost/type_traits/is_convertible.hpp>
#include <boost/utility/enable_if.hpp>
James Cook
committed
class LLTombStone : public LLRefCount
{
public:
Richard Linden
committed
LLTombStone(void* target = NULL) : mTarget(target) {}
James Cook
committed
Richard Linden
committed
void setTarget(void* target) { mTarget = target; }
void* getTarget() const { return mTarget; }
James Cook
committed
private:
Richard Linden
committed
mutable void* mTarget;
James Cook
committed
};
// LLHandles are used to refer to objects whose lifetime you do not control or influence.
// Calling get() on a handle will return a pointer to the referenced object or NULL,
// if the object no longer exists. Note that during the lifetime of the returned pointer,
// you are assuming that the object will not be deleted by any action you perform,
// or any other thread, as normal when using pointers, so avoid using that pointer outside of
// the local code block.
//
// https://wiki.lindenlab.com/mediawiki/index.php?title=LLHandle&oldid=79669
template <typename T>
class LLHandle
{
Richard Linden
committed
template <typename U> friend class LLHandle;
template <typename U> friend class LLHandleProvider;
James Cook
committed
public:
Richard Nelson
committed
LLHandle() : mTombStone(getDefaultTombStone()) {}
Richard Linden
committed
template<typename U>
LLHandle(const LLHandle<U>& other, typename boost::enable_if< typename boost::is_convertible<U*, T*> >::type* dummy = 0)
: mTombStone(other.mTombStone)
{}
James Cook
committed
bool isDead() const
{
return mTombStone->getTarget() == NULL;
}
void markDead()
{
Richard Nelson
committed
mTombStone = getDefaultTombStone();
James Cook
committed
}
T* get() const
{
Richard Linden
committed
return reinterpret_cast<T*>(mTombStone->getTarget());
James Cook
committed
}
friend bool operator== (const LLHandle<T>& lhs, const LLHandle<T>& rhs)
{
return lhs.mTombStone == rhs.mTombStone;
}
friend bool operator!= (const LLHandle<T>& lhs, const LLHandle<T>& rhs)
{
return !(lhs == rhs);
}
friend bool operator< (const LLHandle<T>& lhs, const LLHandle<T>& rhs)
{
return lhs.mTombStone < rhs.mTombStone;
}
friend bool operator> (const LLHandle<T>& lhs, const LLHandle<T>& rhs)
{
return lhs.mTombStone > rhs.mTombStone;
}
protected:
Richard Linden
committed
LLPointer<LLTombStone> mTombStone;
James Cook
committed
private:
Richard Linden
committed
typedef T* pointer_t;
static LLPointer<LLTombStone>& getDefaultTombStone()
Richard Nelson
committed
{
Richard Linden
committed
static LLPointer<LLTombStone> sDefaultTombStone = new LLTombStone;
Richard Nelson
committed
return sDefaultTombStone;
}
James Cook
committed
};
template <typename T>
class LLRootHandle : public LLHandle<T>
{
public:
Richard Linden
committed
typedef LLRootHandle<T> self_t;
typedef LLHandle<T> base_t;
James Cook
committed
LLRootHandle(T* object) { bind(object); }
LLRootHandle() {};
~LLRootHandle() { unbind(); }
Richard Linden
committed
// this is redundant, since an LLRootHandle *is* an LLHandle
//LLHandle<T> getHandle() { return LLHandle<T>(*this); }
James Cook
committed
void bind(T* object)
{
// unbind existing tombstone
if (LLHandle<T>::mTombStone.notNull())
{
Richard Linden
committed
if (LLHandle<T>::mTombStone->getTarget() == (void*)object) return;
James Cook
committed
LLHandle<T>::mTombStone->setTarget(NULL);
}
// tombstone reference counted, so no paired delete
Richard Linden
committed
LLHandle<T>::mTombStone = new LLTombStone((void*)object);
James Cook
committed
}
void unbind()
{
LLHandle<T>::mTombStone->setTarget(NULL);
}
//don't allow copying of root handles, since there should only be one
private:
LLRootHandle(const LLRootHandle& other) {};
};
// Use this as a mixin for simple classes that need handles and when you don't
// want handles at multiple points of the inheritance hierarchy
template <typename T>
class LLHandleProvider
{
Richard Linden
committed
public:
LLHandle<T> getHandle() const
{
// perform lazy binding to avoid small tombstone allocations for handle
// providers whose handles are never referenced
mHandle.bind(static_cast<T*>(const_cast<LLHandleProvider<T>* >(this)));
return mHandle;
}
James Cook
committed
protected:
typedef LLHandle<T> handle_type_t;
LLHandleProvider()
{
// provided here to enforce T deriving from LLHandleProvider<T>
}
Richard Linden
committed
template <typename U>
LLHandle<U> getDerivedHandle(typename boost::enable_if< typename boost::is_convertible<U*, T*> >::type* dummy = 0) const
Richard Linden
committed
{
LLHandle<U> downcast_handle;
Richard Linden
committed
return downcast_handle;
James Cook
committed
}
Richard Linden
committed
James Cook
committed
private:
Richard Linden
committed
mutable LLRootHandle<T> mHandle;