git-svn-id: svn://svn.code.sf.net/p/loki-lib/code/trunk@772 7ec92016-0320-0410-acc4-a06ded1c099a
622 lines
16 KiB
C++
622 lines
16 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
|
// The Loki Library
|
|
// Copyright (c) 2006 Rich Sposato
|
|
// Permission to use, copy, modify, distribute and sell this software for any
|
|
// purpose is hereby granted without fee, provided that the above copyright
|
|
// notice appear in all copies and that both that copyright notice and this
|
|
// permission notice appear in supporting documentation.
|
|
// The author makes no representations about the
|
|
// suitability of this software for any purpose. It is provided "as is"
|
|
// without express or implied warranty.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// $Id$
|
|
|
|
|
|
#include <loki/StrongPtr.h>
|
|
|
|
#include <memory.h>
|
|
#ifdef DO_EXTRA_LOKI_TESTS
|
|
#include <cassert>
|
|
#endif
|
|
|
|
#include <loki/SmallObj.h>
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
namespace Loki
|
|
{
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
TwoRefCounts::TwoRefCounts( bool strong )
|
|
: m_counts( NULL )
|
|
{
|
|
void * temp = SmallObject<>::operator new(
|
|
sizeof(Loki::Private::TwoRefCountInfo) );
|
|
#ifdef DO_EXTRA_LOKI_TESTS
|
|
assert( temp != 0 );
|
|
#endif
|
|
m_counts = new ( temp ) Loki::Private::TwoRefCountInfo( strong );
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
TwoRefCounts::TwoRefCounts( const void * p, bool strong )
|
|
: m_counts( NULL )
|
|
{
|
|
void * temp = SmallObject<>::operator new(
|
|
sizeof(Loki::Private::TwoRefCountInfo) );
|
|
#ifdef DO_EXTRA_LOKI_TESTS
|
|
assert( temp != 0 );
|
|
#endif
|
|
void * p2 = const_cast< void * >( p );
|
|
m_counts = new ( temp ) Loki::Private::TwoRefCountInfo( p2, strong );
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void TwoRefCounts::Increment( bool strong )
|
|
{
|
|
if ( strong )
|
|
{
|
|
m_counts->IncStrongCount();
|
|
}
|
|
else
|
|
{
|
|
m_counts->IncWeakCount();
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
bool TwoRefCounts::Decrement( bool strong )
|
|
{
|
|
if ( strong )
|
|
{
|
|
m_counts->DecStrongCount();
|
|
}
|
|
else
|
|
{
|
|
m_counts->DecWeakCount();
|
|
}
|
|
return !m_counts->HasStrongPointer();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void TwoRefCounts::Swap( TwoRefCounts & rhs )
|
|
{
|
|
std::swap( m_counts, rhs.m_counts );
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void TwoRefCounts::ZapPointer( void )
|
|
{
|
|
#ifdef DO_EXTRA_LOKI_TESTS
|
|
assert( !m_counts->HasStrongPointer() );
|
|
#endif
|
|
if ( m_counts->HasWeakPointer() )
|
|
{
|
|
m_counts->ZapPointer();
|
|
}
|
|
else
|
|
{
|
|
SmallObject<>::operator delete ( m_counts,
|
|
sizeof(Loki::Private::TwoRefCountInfo) );
|
|
m_counts = NULL;
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#if defined (LOKI_OBJECT_LEVEL_THREADING) || defined (LOKI_CLASS_LEVEL_THREADING)
|
|
|
|
typedef SmallValueObject< ::Loki::ClassLevelLockable > ThreadSafePointerAllocator;
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
LockableTwoRefCounts::LockableTwoRefCounts( bool strong )
|
|
: m_counts( NULL )
|
|
{
|
|
void * temp = ThreadSafePointerAllocator::operator new(
|
|
sizeof(Loki::Private::LockableTwoRefCountInfo) );
|
|
#ifdef DO_EXTRA_LOKI_TESTS
|
|
assert( temp != 0 );
|
|
#endif
|
|
m_counts = new ( temp ) Loki::Private::LockableTwoRefCountInfo( strong );
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
LockableTwoRefCounts::LockableTwoRefCounts( const void * p, bool strong )
|
|
: m_counts( NULL )
|
|
{
|
|
void * temp = ThreadSafePointerAllocator::operator new(
|
|
sizeof(Loki::Private::LockableTwoRefCountInfo) );
|
|
#ifdef DO_EXTRA_LOKI_TESTS
|
|
assert( temp != 0 );
|
|
#endif
|
|
void * p2 = const_cast< void * >( p );
|
|
m_counts = new ( temp )
|
|
Loki::Private::LockableTwoRefCountInfo( p2, strong );
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void LockableTwoRefCounts::Increment( bool strong )
|
|
{
|
|
if ( strong )
|
|
{
|
|
m_counts->IncStrongCount();
|
|
}
|
|
else
|
|
{
|
|
m_counts->IncWeakCount();
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
bool LockableTwoRefCounts::Decrement( bool strong )
|
|
{
|
|
if ( strong )
|
|
{
|
|
m_counts->DecStrongCount();
|
|
}
|
|
else
|
|
{
|
|
m_counts->DecWeakCount();
|
|
}
|
|
return !m_counts->HasStrongPointer();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void LockableTwoRefCounts::Swap( LockableTwoRefCounts & rhs )
|
|
{
|
|
std::swap( m_counts, rhs.m_counts );
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void LockableTwoRefCounts::ZapPointer( void )
|
|
{
|
|
#ifdef DO_EXTRA_LOKI_TESTS
|
|
assert( !m_counts->HasStrongPointer() );
|
|
#endif
|
|
if ( m_counts->HasWeakPointer() )
|
|
{
|
|
m_counts->ZapPointer();
|
|
}
|
|
else
|
|
{
|
|
ThreadSafePointerAllocator::operator delete ( m_counts,
|
|
sizeof(Loki::Private::LockableTwoRefCountInfo) );
|
|
m_counts = NULL;
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#endif // if object-level-locking or class-level-locking
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
TwoRefLinks::TwoRefLinks( const void * p, bool strong )
|
|
: m_pointer( const_cast< void * >( p ) )
|
|
, m_strong( strong )
|
|
{
|
|
m_prev = m_next = this;
|
|
#ifdef DO_EXTRA_LOKI_TESTS
|
|
assert( CountPrevCycle( this ) == CountNextCycle( this ) );
|
|
#endif
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
TwoRefLinks::TwoRefLinks( const TwoRefLinks & rhs, bool strong )
|
|
: m_pointer( rhs.m_pointer )
|
|
, m_prev( const_cast< TwoRefLinks * >( &rhs ) )
|
|
, m_next( rhs.m_next )
|
|
, m_strong( strong )
|
|
{
|
|
m_prev->m_next = this;
|
|
m_next->m_prev = this;
|
|
|
|
#ifdef DO_EXTRA_LOKI_TESTS
|
|
assert( m_prev->HasPrevNode( this ) );
|
|
assert( m_next->HasNextNode( this ) );
|
|
assert( rhs.m_next->HasNextNode( this ) );
|
|
assert( rhs.m_prev->HasPrevNode( this ) );
|
|
assert( CountPrevCycle( this ) == CountNextCycle( this ) );
|
|
assert( CountPrevCycle( this ) == CountNextCycle( &rhs ) );
|
|
assert( AllNodesHaveSamePointer() );
|
|
#endif
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void TwoRefLinks::SetPointer( void * p )
|
|
{
|
|
TwoRefLinks * node = m_prev;
|
|
if ( ( this == node ) || ( 0 == node ) )
|
|
return;
|
|
|
|
#ifdef DO_EXTRA_LOKI_TESTS
|
|
assert( m_prev->HasPrevNode( this ) );
|
|
assert( m_next->HasNextNode( this ) );
|
|
assert( CountPrevCycle( this ) == CountNextCycle( this ) );
|
|
assert( AllNodesHaveSamePointer() );
|
|
#endif
|
|
|
|
while ( node != this )
|
|
{
|
|
node->m_pointer = p;
|
|
node = node->m_next;
|
|
}
|
|
m_pointer = node;
|
|
|
|
#ifdef DO_EXTRA_LOKI_TESTS
|
|
assert( m_prev->HasPrevNode( this ) );
|
|
assert( m_next->HasNextNode( this ) );
|
|
assert( CountPrevCycle( this ) == CountNextCycle( this ) );
|
|
assert( AllNodesHaveSamePointer() );
|
|
#endif
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
bool TwoRefLinks::Release( bool strong )
|
|
{
|
|
|
|
(void)strong;
|
|
#ifdef DO_EXTRA_LOKI_TESTS
|
|
assert( strong == m_strong );
|
|
assert( m_prev->HasPrevNode( this ) );
|
|
assert( m_next->HasNextNode( this ) );
|
|
assert( CountPrevCycle( this ) == CountNextCycle( this ) );
|
|
assert( AllNodesHaveSamePointer() );
|
|
#endif
|
|
|
|
if ( NULL == m_next )
|
|
{
|
|
#ifdef DO_EXTRA_LOKI_TESTS
|
|
assert( NULL == m_prev );
|
|
#endif
|
|
// Return false so it does not try to destroy shared object
|
|
// more than once.
|
|
return false;
|
|
}
|
|
else if (m_next == this)
|
|
{
|
|
#ifdef DO_EXTRA_LOKI_TESTS
|
|
assert(m_prev == this);
|
|
#endif
|
|
// Set these to NULL to prevent re-entrancy.
|
|
m_prev = NULL;
|
|
m_next = NULL;
|
|
// Last one in the cycle has to release the pointer.
|
|
return true;
|
|
}
|
|
|
|
#ifdef DO_EXTRA_LOKI_TESTS
|
|
assert( this != m_prev );
|
|
assert( NULL != m_prev );
|
|
assert( m_prev->HasPrevNode( this ) );
|
|
assert( m_next->HasNextNode( this ) );
|
|
#endif
|
|
|
|
// If a single node is strong, then return false so it won't release.
|
|
if ( HasStrongPointer() )
|
|
{
|
|
// A cyclic chain of pointers is only as strong as the strongest link.
|
|
m_prev->m_next = m_next;
|
|
m_next->m_prev = m_prev;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void TwoRefLinks::ZapAllNodes( void )
|
|
{
|
|
TwoRefLinks * p = m_prev;
|
|
if ( ( this == p ) || ( 0 == p ) )
|
|
return;
|
|
#ifdef DO_EXTRA_LOKI_TESTS
|
|
assert( AllNodesHaveSamePointer() );
|
|
#endif
|
|
|
|
while ( p != this )
|
|
{
|
|
TwoRefLinks * p1 = p->m_prev;
|
|
p->m_pointer = 0;
|
|
p->m_next = p;
|
|
p->m_prev = p;
|
|
p = p1;
|
|
}
|
|
m_pointer = 0;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void TwoRefLinks::Swap( TwoRefLinks & rhs )
|
|
{
|
|
|
|
#ifdef DO_EXTRA_LOKI_TESTS
|
|
assert( m_prev->HasPrevNode( this ) );
|
|
assert( m_next->HasNextNode( this ) );
|
|
assert( CountPrevCycle( this ) == CountNextCycle( this ) );
|
|
assert( AllNodesHaveSamePointer() );
|
|
assert( rhs.AllNodesHaveSamePointer() );
|
|
#endif
|
|
|
|
std::swap( rhs.m_pointer, m_pointer );
|
|
if (m_next == this)
|
|
{
|
|
#ifdef DO_EXTRA_LOKI_TESTS
|
|
// This is in a cycle by itself.
|
|
assert(m_prev == this);
|
|
#endif
|
|
if (rhs.m_next == &rhs)
|
|
{
|
|
#ifdef DO_EXTRA_LOKI_TESTS
|
|
assert(rhs.m_prev == &rhs);
|
|
#endif
|
|
// both are in 1-node cycles - thus there is nothing to do.
|
|
return;
|
|
}
|
|
m_prev = rhs.m_prev;
|
|
m_next = rhs.m_next;
|
|
m_prev->m_next = m_next->m_prev = this;
|
|
rhs.m_next = rhs.m_prev = &rhs;
|
|
return;
|
|
}
|
|
if (rhs.m_next == &rhs)
|
|
{
|
|
#ifdef DO_EXTRA_LOKI_TESTS
|
|
// rhs is in a cycle by itself.
|
|
assert( rhs.m_prev == &rhs );
|
|
#endif
|
|
rhs.m_prev = m_prev;
|
|
rhs.m_next = m_next;
|
|
m_prev->m_next = m_next->m_prev = &rhs;
|
|
m_next = m_prev = this;
|
|
return;
|
|
}
|
|
if (m_next == &rhs ) // rhs is next neighbour
|
|
{
|
|
if ( m_prev == &rhs )
|
|
return; // cycle of 2 pointers - no need to swap.
|
|
std::swap(m_prev, m_next);
|
|
std::swap(rhs.m_prev, rhs.m_next);
|
|
std::swap(rhs.m_prev, m_next);
|
|
std::swap(rhs.m_prev->m_next,m_next->m_prev);
|
|
}
|
|
else if ( m_prev == &rhs ) // rhs is prev neighbor
|
|
{
|
|
if ( m_next == &rhs )
|
|
return; // cycle of 2 pointers - no need to swap.
|
|
std::swap( m_prev, m_next );
|
|
std::swap( rhs.m_next, rhs.m_prev );
|
|
std::swap( rhs.m_next, m_prev );
|
|
std::swap( rhs.m_next->m_prev, m_prev->m_next );
|
|
}
|
|
else // not neighhbors
|
|
{
|
|
std::swap(m_prev, rhs.m_prev);
|
|
std::swap(m_next, rhs.m_next);
|
|
std::swap(m_prev->m_next, rhs.m_prev->m_next);
|
|
std::swap(m_next->m_prev, rhs.m_next->m_prev);
|
|
}
|
|
|
|
#ifdef DO_EXTRA_LOKI_TESTS
|
|
assert( m_next == this ? m_prev == this : m_prev != this);
|
|
assert( m_prev == this ? m_next == this : m_next != this);
|
|
assert( m_prev->HasPrevNode( this ) );
|
|
assert( m_next->HasNextNode( this ) );
|
|
assert( CountPrevCycle( this ) == CountNextCycle( this ) );
|
|
assert( rhs.m_prev->HasPrevNode( &rhs ) );
|
|
assert( rhs.m_next->HasNextNode( &rhs ) );
|
|
assert( CountPrevCycle( &rhs ) == CountNextCycle( &rhs ) );
|
|
assert( AllNodesHaveSamePointer() );
|
|
assert( rhs.AllNodesHaveSamePointer() );
|
|
#endif
|
|
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
bool TwoRefLinks::AllNodesHaveSamePointer( void ) const
|
|
{
|
|
const TwoRefLinks * next = m_next;
|
|
if ( NULL == next )
|
|
return true;
|
|
do
|
|
{
|
|
if ( next->m_pointer != m_pointer )
|
|
return false;
|
|
next = next->m_next;
|
|
} while ( next != this );
|
|
return true;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
unsigned int TwoRefLinks::CountPrevCycle( const TwoRefLinks * pThis )
|
|
{
|
|
if ( NULL == pThis )
|
|
return 0;
|
|
const TwoRefLinks * p = pThis->m_prev;
|
|
if ( NULL == p )
|
|
return 0;
|
|
if ( pThis == p )
|
|
return 1;
|
|
|
|
unsigned int count = 1;
|
|
do
|
|
{
|
|
p = p->m_prev;
|
|
++count;
|
|
} while ( p != pThis );
|
|
|
|
return count;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
unsigned int TwoRefLinks::CountNextCycle( const TwoRefLinks * pThis )
|
|
{
|
|
if ( NULL == pThis )
|
|
return 0;
|
|
const TwoRefLinks * p = pThis->m_next;
|
|
if ( NULL == p )
|
|
return 0;
|
|
if ( pThis == p )
|
|
return 1;
|
|
|
|
unsigned int count = 1;
|
|
while ( p != pThis )
|
|
{
|
|
p = p->m_next;
|
|
++count;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
bool TwoRefLinks::HasPrevNode( const TwoRefLinks * p ) const
|
|
{
|
|
if ( this == p )
|
|
return true;
|
|
const TwoRefLinks * prev = m_prev;
|
|
if ( NULL == prev )
|
|
return false;
|
|
while ( prev != this )
|
|
{
|
|
if ( p == prev )
|
|
return true;
|
|
prev = prev->m_prev;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
bool TwoRefLinks::HasNextNode( const TwoRefLinks * p ) const
|
|
{
|
|
if ( this == p )
|
|
return true;
|
|
const TwoRefLinks * next = m_next;
|
|
if ( NULL == next )
|
|
return false;
|
|
while ( next != this )
|
|
{
|
|
if ( p == next )
|
|
return true;
|
|
next = next->m_next;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
bool TwoRefLinks::HasStrongPointer( void ) const
|
|
{
|
|
const TwoRefLinks * next = m_next;
|
|
if ( NULL == next )
|
|
return false;
|
|
while ( next != this )
|
|
{
|
|
if ( next->m_strong )
|
|
return true;
|
|
next = next->m_next;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
bool TwoRefLinks::Merge( TwoRefLinks & rhs )
|
|
{
|
|
|
|
if ( NULL == m_next )
|
|
{
|
|
#ifdef DO_EXTRA_LOKI_TESTS
|
|
assert( NULL == m_prev );
|
|
#endif
|
|
return false;
|
|
}
|
|
TwoRefLinks * prhs = &rhs;
|
|
if ( prhs == this )
|
|
return true;
|
|
if ( NULL == prhs->m_next )
|
|
{
|
|
#ifdef DO_EXTRA_LOKI_TESTS
|
|
assert( NULL == prhs->m_prev );
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
#ifdef DO_EXTRA_LOKI_TESTS
|
|
assert( CountPrevCycle( this ) == CountNextCycle( this ) );
|
|
assert( CountPrevCycle( prhs ) == CountNextCycle( prhs ) );
|
|
#endif
|
|
// If rhs node is already in this cycle, then no need to merge.
|
|
if ( HasPrevNode( &rhs ) )
|
|
{
|
|
#ifdef DO_EXTRA_LOKI_TESTS
|
|
assert( HasNextNode( &rhs ) );
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
if ( prhs == prhs->m_next )
|
|
{
|
|
/// rhs is in a cycle with 1 node.
|
|
#ifdef DO_EXTRA_LOKI_TESTS
|
|
assert( prhs->m_prev == prhs );
|
|
#endif
|
|
prhs->m_prev = m_prev;
|
|
prhs->m_next = this;
|
|
m_prev->m_next = prhs;
|
|
m_prev = prhs;
|
|
}
|
|
else if ( this == m_next )
|
|
{
|
|
/// this is in a cycle with 1 node.
|
|
#ifdef DO_EXTRA_LOKI_TESTS
|
|
assert( m_prev == this );
|
|
#endif
|
|
m_prev = prhs->m_prev;
|
|
m_next = prhs;
|
|
prhs->m_prev->m_next = this;
|
|
prhs->m_prev = this;
|
|
}
|
|
else
|
|
{
|
|
m_next->m_prev = prhs->m_prev;
|
|
prhs->m_prev->m_next = m_prev;
|
|
m_next = prhs;
|
|
prhs->m_prev = this;
|
|
}
|
|
|
|
|
|
#ifdef DO_EXTRA_LOKI_TESTS
|
|
assert( CountPrevCycle( this ) == CountNextCycle( this ) );
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
} // end namespace Loki
|
|
|