//////////////////////////////////////////////////////////////////////////////// // 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 #include #ifdef DO_EXTRA_LOKI_TESTS #include #endif #include // ---------------------------------------------------------------------------- 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