base_dlist.hh

Go to the documentation of this file.
00001 /*
00011  * LEGAL:   COPYRIGHT (C) 2004 JIM E. BROOKS
00012  *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
00013  *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
00014  *****************************************************************************/
00015 
00016 #ifndef BASE_DLIST_HH
00017 #define BASE_DLIST_HH 1
00018 
00019 namespace base {
00020 
00021 template<typename T,typename LOCK> class Dlist;
00022 template<typename T,typename LOCK> class DlistIterator;
00023 
00032 template<typename T,typename LOCK=FastLock>
00033 class Dlink
00034 {
00035 
00036 friend class Dlist<T,LOCK>;
00037 private:
00038     // ctor/dtor are private: only Dlist can create/delete Dlinks.
00039     Dlink( void ) { SET_TYPESIG( this, TYPESIG_DLINK ); }
00040     ~Dlink()      { INVALIDATE_TYPESIG( this, TYPESIG_DLINK ); }
00041     Dlink*  mPrev;  // next link
00042     Dlink*  mNext;  // previous link
00043     T*      mObj;   // pointer to actual object
00044 public:
00045     // Client can get object pointer but can't reassign it.
00046     T*          Obj( void ) const { CHECK_TYPESIG(this,TYPESIG_DLINK); return mObj; }
00047     
00048 };
00049 
00138 template<typename T,typename LOCK=FastLock>  // or LOCK can be NoLock
00139 class Dlist : public Shared, public SemiThreadable
00140 {
00141 
00142 
00143     // Sentinel never has an object, but use a different address than NULL
00144     // to distinguish from other dlinks that may have NULL pointers.
00145 #define DLIST_SENTINEL_NULL_OBJ  (reinterpret_cast<T*>(ILLEGAL_PTR))
00146 
00147     // mHead should always point to sentinel.  Sentinel should never have an object.
00148 #define CHECK_DLIST_HEAD_TAIL() \
00149     CHECK_TYPESIG( mHead, TYPESIG_DLINK ); \
00150     CHECK_TYPESIG( mTail, TYPESIG_DLINK ); \
00151     ASSERT( mHead->mObj == DLIST_SENTINEL_NULL_OBJ );
00152 
00153 public:
00154     enum eDestroy { DESTROY, KEEP };
00155 
00156 public:
00160     Dlist( eDestroy destroy = DESTROY )
00161     {
00162         // Allows Dlist to be on stack while lock is on heap.
00163         THREAD_CODE( mInstanceLock = new LOCK; )
00164 
00165         SET_TYPESIG( this, TYPESIG_DLIST );
00166 
00167         mDestroy = destroy;
00168 
00169         // Make the sentinel which points to itself.
00170         mHead = mTail = new Dlink<T>;
00171         mHead->mObj = DLIST_SENTINEL_NULL_OBJ;  // sentinel never has an object
00172         mHead->mPrev = mHead->mNext = mTail->mPrev = mTail->mNext = mHead;
00173     }
00174 
00175 public:
00180     ~Dlist()
00181     {
00182         Clear();       // delete linked objects
00183         delete mHead;  // delete hidden sentinel (Clear() kept it)
00184         THREAD_CODE( delete mInstanceLock; )
00185         INVALIDATE_TYPESIG( this, TYPESIG_DLIST );
00186     }
00187 
00188 public:
00193     void
00194     Clear( void )
00195     {
00196     THREAD_CODE( typename LOCK::Auto autolock( mInstanceLock ); )
00197 
00198         // Delete links (and optionally the linked objects) but keep the sentinel.
00199         // Sentinel is needed if after clearing, new links are added.
00200         Dlink<T>* dlink = mHead->mNext;
00201         while ( dlink != dlink->mNext )
00202         {
00203             //ASSERT( dlink->mObj != NULL );  // allow client to assign NULL object
00204             CHECK_TYPESIG(dlink,TYPESIG_DLINK);
00205             CHECK_TYPESIG(dlink->mNext,TYPESIG_DLINK);
00206 
00207             Dlink<T>* next = dlink->mNext;  // before dlink is unlinked (don't saw off branch)
00208             if ( mDestroy == DESTROY )
00209                 delete dlink->mObj;
00210             Unlink( dlink );  // delete the link struct itself
00211             dlink = next;
00212         }
00213         ASSERT( mHead == mTail );  // head==tail==sentinel
00214         CHECK_DLIST_HEAD_TAIL();
00215     }
00216 
00217 public:
00224     Dlink<T>*  // Dlink<> is a safe ptr as it destructor is private
00225     Prepend( T* obj )
00226     {
00227     THREAD_CODE( typename LOCK::Auto autolock( mInstanceLock ); )
00228 
00229     CHECK_DLIST_HEAD_TAIL();
00230 
00231         // As a sentinel is used, head/tail always point to something,
00232         // and there is always a previous and a next link.
00233 
00234         // New link struct for this object.
00235         Dlink<T>* dlink = new Dlink<T>;
00236 
00237         // Insert new link between head/sentinel link and second link.
00238         if ( UX(mHead == mTail) ) mTail = dlink; // if dlist is empty, new link is the tail
00239         dlink->mNext = mHead->mNext;
00240         dlink->mPrev = mHead;
00241         dlink->mObj  = obj;
00242         mHead->mNext = dlink->mNext->mPrev = dlink;
00243         CHECK_TYPESIG(dlink->mNext,TYPESIG_DLINK);
00244         CHECK_TYPESIG(dlink->mPrev,TYPESIG_DLINK);
00245         CHECK_DLIST_HEAD_TAIL();
00246         return dlink;
00247     }
00248 
00249 public:
00255     Dlink<T>*
00256     Append( T* obj )
00257     {
00258     THREAD_CODE( typename LOCK::Auto autolock( mInstanceLock ); )
00259 
00260     CHECK_DLIST_HEAD_TAIL();
00261 
00262         // As a sentinel is used, head/tail always point to something,
00263         // and there is always a previous and a next link.
00264         // New link struct for this object.
00265         Dlink<T>* dlink = new Dlink<T>;
00266 
00267         // Link to tail and supplant it.
00268         dlink->mPrev = mTail;
00269         dlink->mNext = mTail->mNext;  // mTail->mNext = mHead = sentinel
00270         dlink->mObj  = obj;
00271         mTail->mNext = dlink;
00272         mTail        = dlink;
00273         mHead->mPrev = dlink;  // head back to tail
00274         CHECK_TYPESIG(dlink->mNext,TYPESIG_DLINK);
00275         CHECK_TYPESIG(dlink->mPrev,TYPESIG_DLINK);
00276         CHECK_DLIST_HEAD_TAIL();
00277 
00278         return dlink;
00279     }
00280 
00281 public:
00288     void
00289     Unlink( Dlink<T>* dlink )
00290     {
00291     THREAD_CODE( typename LOCK::Auto autolock( mInstanceLock ); )
00292 
00293     CHECK_TYPESIG( dlink, TYPESIG_DLINK );
00294     CHECK_DLIST_HEAD_TAIL();
00295 
00296         // As a sentinel is used, head/tail always point to something,
00297         // and there is always a previous and a next link.
00298         if ( UX(mTail == dlink) ) mTail = dlink->mPrev;  // update tail if unlinking it
00299         CHECK_TYPESIG(dlink->mNext,TYPESIG_DLINK);
00300         CHECK_TYPESIG(dlink->mPrev,TYPESIG_DLINK);
00301         dlink->mPrev->mNext = dlink->mNext;  // preceding --> following
00302         dlink->mNext->mPrev = dlink->mPrev;  // following --> preceding
00303         delete dlink;
00304         CHECK_DLIST_HEAD_TAIL();
00305     }
00306 
00307 public:
00311     bool
00312     IfEmpty( void ) const
00313     {
00314     THREAD_CODE( typename LOCK::Auto autolock( mInstanceLock ); )
00315 
00316     CHECK_DLIST_HEAD_TAIL();
00317 
00318         return mHead == mTail;  // empty if sentinel is the only link
00319     }
00320 
00321 public:
00325     bool
00326     IfMoreThanOne( void ) const
00327     {
00328     THREAD_CODE( typename LOCK::Auto autolock( mInstanceLock ); )
00329 
00330     CHECK_DLIST_HEAD_TAIL();
00331 
00332         // If first and second links exist.
00333         return mHead->mNext != mHead
00334             && mHead->mNext->mNext != mHead;
00335     }
00336 
00337 public:
00341     Dlink<T>*
00342     First( void ) const
00343     {
00344     THREAD_CODE( typename LOCK::Auto autolock( mInstanceLock ); )
00345 
00346     CHECK_DLIST_HEAD_TAIL();
00347     CHECK_TYPESIG(mHead->mNext,TYPESIG_DLINK);  // works either way
00348 
00349         return mHead == mTail ? NULL : mHead->mNext;
00350     }
00351 
00352 public:
00356     Dlink<T>*
00357     Last( void ) const
00358     {
00359     THREAD_CODE( typename LOCK::Auto autolock( mInstanceLock ); )
00360 
00361     CHECK_DLIST_HEAD_TAIL();
00362 
00363         return mHead == mTail ? NULL : mTail;
00364     }
00365 
00366 public:
00370     Dlink<T>*
00371     Prev( const Dlink<T>* dlink ) const
00372     {
00373     THREAD_CODE( typename LOCK::Auto autolock( mInstanceLock ); )
00374 
00375     CHECK_DLIST_HEAD_TAIL();
00376     CHECK_TYPESIG(dlink,TYPESIG_DLINK);
00377     CHECK_TYPESIG(dlink->mPrev,TYPESIG_DLINK);
00378     ASSERT( dlink != mHead );  // catch passing head
00379     ASSERT( ! (dlink->mPrev == mHead && mHead->mPrev == mHead) );  // catch returning head
00380 
00381         return dlink->mPrev != mHead ? dlink->mPrev : mHead->mPrev;
00382     }
00383 
00384 public:
00388     Dlink<T>*
00389     Next( const Dlink<T>* dlink ) const
00390     {
00391     THREAD_CODE( typename LOCK::Auto autolock( mInstanceLock ); )
00392 
00393     CHECK_DLIST_HEAD_TAIL();
00394     CHECK_TYPESIG(dlink,TYPESIG_DLINK);
00395     CHECK_TYPESIG(dlink->mNext,TYPESIG_DLINK);
00396     ASSERT( dlink != mHead );  // catch passing head
00397     ASSERT( ! (dlink->mNext == mHead && mHead->mNext == mHead) );  // catch returning head
00398 
00399         return dlink->mNext != mHead ? dlink->mNext : mHead->mNext;
00400     }
00401 
00402 public:
00406     bool
00407     IfLinked( const T* obj )
00408     {
00409         Dlink<T>* first = First();
00410         if ( first != NULL )  // if not empty
00411         {
00412             // Until Next() wraps back to first link.
00413             // Start with first link.
00414             Dlink<T>* lnk = first;
00415             do
00416             {
00417                 if ( lnk->Obj() == obj )
00418                     return true;
00419                 lnk = Next( lnk );
00420             }
00421             while ( lnk != first );  // fall thru to return false
00422         }
00423         return false;
00424     }
00425 
00426 public:
00430     void Lock( void ) const  // logically const
00431     {
00432         THREAD_CODE( mInstanceLock->Lock(); )
00433     }
00434 
00435     void Unlock( void ) const
00436     {
00437         THREAD_CODE( mInstanceLock->Unlock(); )
00438     }
00439 
00440 public:
00441     typedef LOCK LockType;  // traits
00442 
00443 private:
00444     eDestroy            mDestroy;   
00445     Dlink<T>*           mHead;      
00446     Dlink<T>*           mTail;      
00447 
00448     // Iterator necessitates a recursive lock 
00449     THREAD_CODE( mutable LOCK* mInstanceLock; )
00450 
00451 public:
00452     
00453     friend class DlistIterator<T,LOCK>;
00454 };
00455 
00467 template<typename T,typename LOCK=FastLock>
00468 class DlistIterator : public AbstractIterator< SafePtr<T> >
00469 {
00470 public:
00471     DlistIterator( const Dlist<T,LOCK>& dlist )
00472     :   mDlist(dlist),
00473         mNext(NULL),  // to catch mistake of calling Next() before operator bool()
00474         mNextNext(dlist.First()),
00475         mLocked(false)
00476     {
00477     CHECK_TYPESIG(&mDlist,TYPESIG_DLIST);
00478 
00479         // Lock the whole list unless it was empty.
00480         if ( mNextNext != NULL )
00481         {
00482             mDlist.Lock();
00483             mLocked = true;
00484         }
00485         // If list was empty, Dlist::First() returned NULL into mNextNext
00486         // which causes operator bool() to return false.
00487     }
00488 
00489     ~DlistIterator()
00490     {
00491         // Don't even think about eliminating mLocked with testing list emptiness,
00492         // because the client may have added links.
00493         if ( mLocked ) mDlist.Unlock();
00494     }
00495 
00497     operator bool()
00498     {
00499         // mNextNext also serves as a flag.
00500         // When NULL, iteration should stop (empty or end of list).
00501         if ( mNextNext != NULL )
00502         {
00503         CHECK_TYPESIG(mNextNext,TYPESIG_DLINK);
00504 
00505             // Prepare for Next() being called.
00506             mNext = mNextNext;
00507 
00508             // Prepare for operator bool() being called again.
00509             // Advancing to the next link before Next() returns it is a critical step
00510             // to survive an iteration that unlinks what Next() returns.
00511             mNextNext = mDlist.Next( mNextNext );
00512             if ( UX( mNextNext == mDlist.First() ) )
00513                 mNextNext = NULL;  // wrapped back to first link, end-of-list condition
00514             return true;
00515         }
00516         else
00517         {
00518             // No more items.  Unlock the list in case the client
00519             // will use the list after the iteration.
00520             if ( mLocked )
00521             {
00522                 mDlist.Unlock(); mLocked = false;
00523             }
00524             return false;
00525         }
00526     }
00527 
00529     SafePtr<T>
00530     Next( void )
00531     {
00532         return mNext->Obj();
00533     }
00534 
00535 private:
00536     const Dlist<T,LOCK>&    mDlist;     // iterator doesn't logically affect list elements
00537     Dlink<T>*               mNext;      // next link that Next() will return (current state)
00538     Dlink<T>*               mNextNext;  // link after mNext, if NULL then stops iteration (next state)
00539     bool                    mLocked;    // if this iterator locked the list
00540 };
00541 
00542 } // namespace base
00543 
00544 #endif // BASE_DLIST_HH
Palomino 3D Engine documents generated by doxygen 1.5.3 on Fri Nov 23 11:26:06 2007