#ifndef SPTR_REFCOUNT_HXX_
#define SPTR_REFCOUNT_HXX_
/* ====================================================================
* The Vovida Software License, Version 1.0
*
* Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The names "VOCAL", "Vovida Open Communication Application Library",
* and "Vovida Open Communication Application Library (VOCAL)" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact vocal@vovida.org.
*
* 4. Products derived from this software may not be called "VOCAL", nor
* may "VOCAL" appear in their name, without prior written
* permission of Vovida Networks, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
* NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
* NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
* IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* ====================================================================
*
* This software consists of voluntary contributions made by Vovida
* Networks, Inc. and many individuals on behalf of Vovida Networks,
* Inc. For more information on Vovida Networks, Inc., please see
* <http://www.vovida.org/>.
*
*/
static const char* const Sptr_RefCount_hxx_Version =
"$Id: SptrRefCount.hxx,v 1.11.4.1 2003/01/30 22:39:09 bko Exp $";
#include <assert.h>
#include "VMutex.h"
#include "debug.h"
#include "CountSemaphore.hxx"
#include "FastMutex.hxx"
#define SPTR_DEBUG 1
#define SPTR_THREADSAFE 1
#define SPTR_TRACEBACK 1
#ifdef CODE_OPTIMIZE
// turn off debugging and tracebacks in optimized code
#undef SPTR_DEBUG
#undef SPTR_TRACEBACK
#define SPTR_DEBUG 0
#define SPTR_TRACEBACK 0
#endif
#ifdef WIN32
#define WORKAROUND_VISUAL_C
#endif
#if SPTR_DEBUG
#include <map>
/** assists in debugging Sptrs (both use and implementation), by
* constructing a map of currently active Sptrs. A singleton. */
class SptrDebugger
{
private:
/// default constructor
SptrDebugger();
/// used to get an instance. If no instance exists, create one
static SptrDebugger* getInstance();
typedef map < void*, bool > PtrMap;
static SptrDebugger* impl_;
PtrMap ptrsInUse;
VMutex mutex;
public:
/// add a new pointer when it is new.
static void newPtr(void* ptr);
/// remove an pointer after it has been deleted.
static void deletePtr(void* ptr);
};
// these functions are used as debug breakpoints if SPTR_TRACEBACK
// is set to 1
void sptrDebugMarkTraceback(void* sptr, void* ptr);
void sptrDebugClearTraceback(void* sptr, void* ptr);
void sptrDebugDumpTraceback(char* filename);
#endif
#if SPTR_THREADSAFE
#define SPTR_LOCK(x) (x).lock()
#define SPTR_UNLOCK(x) (x).unlock()
#else
#define SPTR_LOCK(x);
#define SPTR_UNLOCK(x);
#endif
/** Template simulates a "smart" pointer which deletes the item it is
pointing to when no more references to the item exist. Warning:
circular references will produce memory leaks.
<P>Note that only one Sptr should be constructed from the original
ptr -- Sptr will free twice (and cause havoc) if it is misused like so:
<P>
<B> WRONG: </B>
<PRE>
T* obj = new T();
Sptr<T> p;
Sptr<T> q;
p = obj;
q = obj;
</PRE>
<p>
<I>now both p and q think they are the only ones who will free the
memory, so you will get an error.</i>
</p>
<p><B>CORRECT:</b></p>
<PRE>
T* obj = new T();
Sptr<T> p;
Sptr<T> q;
p = obj;
q = p; // this keeps things OK.
</PRE>
<p>
When all Sptr<T> objects pointing to the object go out of scope, or are
assigned to point to some other object, the object is deleted.
</p>
<p>
<b>Examples</b>
</p>
<p>
<PRE>
Sptr <Data> object = new Data;
*object = "string1";
cout << *object << endl; // prints string1
{
Sptr <Data> o2 = new Data("string2");
object = o2;
// at this point, the string1 object has no references
// because object points at string2 rather than string1, so
// string1 is deleted.
// the string2 object has 2 references to it: object and o2.
Sptr <Data> o3 = new Data("string3");
// at this point, o3 points to string3.
}
// at this point, the second string "string2" has only 1
// pointer to it since o2 was destroyed (it was local to the
// above section) since the reference count is 1 (object points
// to it) it is left alone.
// however, since o3 was destoyed, "string3"'s reference count
// goes to 0 and it is destroyed.
LocalScopeAllocator lo;
printf("%s\n", object->getData(lo)); // print the object
</PRE>
</p>
*/
template < class T >
class Sptr
{
// we need to work around Visual C++'s limitations. this
// is needed so that we can grab these bits directly
#if defined(WORKAROUND_VISUAL_C)
public:
#else
private:
#endif
mutable T* ptr;
mutable CountSemaphore* count;
#if defined (VOCAL_FASTMUTEX_AVAILABLE)
mutable FastMutex mutex;
#else
mutable VMutex mutex;
#endif
/// increment the reference count.
void increment()
{
if (ptr)
{
if (!count)
{
count = new CountSemaphore();
}
count->increment();
}
#if SPTR_TRACEBACK
sptrDebugMarkTraceback((void*) this, (void*) ptr);
#endif
}
/// decrement the reference count
void decrement()
{
if (ptr && count)
{
#if SPTR_TRACEBACK
sptrDebugClearTraceback((void*) this, (void*) ptr);
#endif
if(count->decrement())
{
#if SPTR_DEBUG
#if 0
cerr << "deleting " << ptr << endl;
breakpoint();
#endif
SptrDebugger::deletePtr((void*) ptr);
#endif
delete ptr;
delete count;
}
}
ptr = 0;
count = 0;
}
public:
// we will skip these since they don't work under Visual C++
#if !defined(WORKAROUND_VISUAL_C)
#if defined (__SUNPRO_CC) || defined (__INTEL_COMPILER)
/** conversion operator converts pointers of this class to
* class Sptr< T2 >., where T2 is a different base class. This
* is most often used when attempting to call a method of the
* base class through a derived class pointer.
*/
template < class T2 >
operator Sptr<T2 > ()
{
return Sptr < T2 > (ptr, count);
}
#endif
/** conversion operator converts pointers of this const class
* to class const Sptr< T2 >., where T2 is a different base
* class. This is most often used when attempting to call a
* const method of the base class through a derived class
* pointer. This is a workaround for SUNPRO .
*/
template < class T2 >
operator const Sptr<T2 > () const
{
return Sptr < T2 > (ptr, count);
}
#endif
/// default constructor. points to NULL.
Sptr() : ptr(0), count(0)
{}
;
/** constructor used most often as the constructor from a
* plain pointer. Do not use this to convert a single pointer
* to a smart pointer multiple times -- this will result in an
* error (see class introduction for details).
*/
Sptr(T* original, CountSemaphore* myCount = 0)
: ptr(original), count(myCount)
{
if (ptr)
{
#if SPTR_DEBUG
if (!myCount)
{
SptrDebugger::newPtr((void*) ptr); // void* takes away warning
}
#endif
increment();
}
};
#if defined(WORKAROUND_VISUAL_C)
/** conversion constructor -- this converts Sptrs of one type to
* another, but is ONLY used in working around the missing
* template conversion operators in Visual C++.
*/
template <class T2>
Sptr(const Sptr<T2>& x)
: ptr(x.ptr), count(x.count)
{
increment();
};
#endif
/** copy constructor
*/
Sptr(const Sptr& x)
: ptr(x.ptr), count(x.count)
{
increment();
};
/// destructor
~Sptr()
{
{
SPTR_LOCK(*this);
decrement();
SPTR_UNLOCK(*this);
}
}
/// dereference operator
T& operator*() const
{
assert(ptr);
return *ptr;
}
/// ! operator . Returns true if ptr == 0, false otherwise.
int operator!() const
{
if (ptr)
{
return (ptr == 0);
}
else
return true;
}
/// pointer operator.
T* operator->() const
{
return ptr;
}
/** dynamicCast works similarly to the actual dynamic_cast()
* operator.
<P>
<B>EXAMPLE</B>
<PRE>
class A {
...
};
class B : public A {
...
};
class C {
...
};
...
int main()
{
Sptr< A > a;
Sptr< B > b;
Sptr< C > c;
a = new B;
b.dynamicCast(a);
// now, b points to the same thing as a
c.dynamicCast(a);
// now, c is the NULL pointer.
}
</PRE>
*/
template < class T2 > Sptr& dynamicCast(const Sptr < T2 > & x)
{
if (ptr == x.getPtr()) return *this;
SPTR_LOCK(x);
SPTR_LOCK(*this);
decrement();
if(T* p = dynamic_cast < T* > (x.getPtr()))
{
count = x.getCount();
ptr = p;
increment();
}
SPTR_UNLOCK(*this);
SPTR_UNLOCK(x);
return *this;
}
/** assignment operator -- this is most often used to assign
* from a smart pointer to a derived type to a smart pointer
* of the base type.
*/
template < class T2 >
Sptr& operator=(const Sptr < T2 > & x)
{
if (ptr == x.getPtr()) return * this;
SPTR_LOCK(x);
SPTR_LOCK(*this);
decrement();
ptr = x.getPtr();
count = x.getCount();
increment();
SPTR_UNLOCK(x);
SPTR_UNLOCK(*this);
return *this;
}
/** assignment operator from plain pointer. Do not use this
* to convert a single pointer to a smart pointer multiple
* times -- this will result in an error (see class
* introduction for details).
*/
Sptr& operator=(T* original)
{
if (ptr == original) return * this;
SPTR_LOCK(*this);
decrement();
#if SPTR_DEBUG
SptrDebugger::newPtr((void*) original);
#endif
ptr = original;
increment();
SPTR_UNLOCK(*this);
return *this;
};
/// assignment operator
Sptr& operator=(const Sptr& x)
{
if (ptr == x.ptr) return * this;
SPTR_LOCK(x);
SPTR_LOCK(*this);
decrement();
ptr = x.ptr;
count = x.count;
increment();
SPTR_UNLOCK(*this);
SPTR_UNLOCK(x);
return *this;
}
/// compare whether a pointer and a smart pointer point to different things
friend bool operator!=(const void* y, const Sptr& x)
{
if (x.ptr != y)
return true;
else
return false;
}
/// compare whether a smart pointer and a pointer point to different things
friend bool operator!=(const Sptr& x, const void* y)
{
return (y != x);
}
/// compare whether a pointer and a smart pointer point to the same thing
friend bool operator==(const void* y, const Sptr& x)
{
if (x.ptr == y)
return true;
else
return false;
}
/// compare whether a smart pointer and a pointer point to the same thing
friend bool operator==(const Sptr& x, const void* y)
{
return (y == x);
}
/// compare whether two smart pointers point to the same thing
bool operator==(const Sptr& x) const
{
if (x.ptr == ptr)
return true;
else
return false;
}
/// compare whether two smart pointers point to the same thing
bool operator!=(const Sptr& x) const
{
return !(operator==(x));
}
/**
this interface is here because it may sometimes be
necessary. DO NOT USE unless you must use it.
get the actual mutex of the smart pointer.
*/
VMutex* getMutex() const
{
return 0;
}
/**
this interface is here because it may sometimes be
necessary. DO NOT USE unless you must use it.
get the value of the reference count of the smart pointer.
*/
CountSemaphore* getCount() const
{
return count;
}
/**
this interface is here because it may sometimes be
necessary. DO NOT USE unless you must use it.
get the pointer to which the smart pointer points.
*/
T* getPtr() const
{
return ptr;
}
#if SPTR_THREADSAFE
void lock() const
{
mutex.lock();
}
void unlock() const
{
mutex.unlock();
}
#endif
};
#endif
syntax highlighted by Code2HTML, v. 0.9.1