Shadowrun: Awakened 29 September 2011 - Build 871
SingleProducerConsumer.h
Go to the documentation of this file.
00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 #ifndef __SINGLE_PRODUCER_CONSUMER_H
00010 #define __SINGLE_PRODUCER_CONSUMER_H
00011 
00012 #include "RakAssert.h"
00013 
00014 static const int MINIMUM_LIST_SIZE=8;
00015 
00016 #include "RakMemoryOverride.h"
00017 #include "Export.h"
00018 
00021 namespace DataStructures
00022 {
00024     template <class SingleProducerConsumerType>
00025     class RAK_DLL_EXPORT SingleProducerConsumer
00026     {
00027     public:
00028         // Constructor
00029         SingleProducerConsumer();
00030 
00031         // Destructor
00032         ~SingleProducerConsumer();
00033 
00036         SingleProducerConsumerType* WriteLock(void);
00037 
00041         void CancelWriteLock(SingleProducerConsumerType* cancelToLocation);
00042 
00044         void WriteUnlock(void);
00045 
00049         SingleProducerConsumerType* ReadLock(void);
00050 
00051         // Cancelling locks cancels all locks back up to the data passed.  So if you lock twice and cancel using the first lock, the second lock is ignored
00053         void CancelReadLock(SingleProducerConsumerType* cancelToLocation);
00054 
00057         void ReadUnlock(void);
00058 
00060         void Clear(void);
00061 
00064         int Size(void) const;
00065 
00068         bool CheckReadUnlockOrder(const SingleProducerConsumerType* data) const;
00069 
00072         bool ReadIsLocked(void) const;
00073 
00074     private:
00075         struct DataPlusPtr
00076         {
00077             DataPlusPtr () {readyToRead=false;}
00078             SingleProducerConsumerType object;
00079 
00080             // Ready to read is so we can use an equality boolean comparison, in case the writePointer var is trashed while context switching.
00081             volatile bool readyToRead;
00082             volatile DataPlusPtr *next;
00083         };
00084         volatile DataPlusPtr *readAheadPointer;
00085         volatile DataPlusPtr *writeAheadPointer;
00086         volatile DataPlusPtr *readPointer;
00087         volatile DataPlusPtr *writePointer;
00088         unsigned readCount, writeCount;
00089     };
00090 
00091     template <class SingleProducerConsumerType>
00092         SingleProducerConsumer<SingleProducerConsumerType>::SingleProducerConsumer()
00093     {
00094         // Preallocate
00095         readPointer = RakNet::OP_NEW<DataPlusPtr>( _FILE_AND_LINE_ );
00096         writePointer=readPointer;
00097         readPointer->next = RakNet::OP_NEW<DataPlusPtr>( _FILE_AND_LINE_ );
00098         int listSize;
00099 #ifdef _DEBUG
00100         RakAssert(MINIMUM_LIST_SIZE>=3);
00101 #endif
00102         for (listSize=2; listSize < MINIMUM_LIST_SIZE; listSize++)
00103         {
00104             readPointer=readPointer->next;
00105             readPointer->next = RakNet::OP_NEW<DataPlusPtr>( _FILE_AND_LINE_ );
00106         }
00107         readPointer->next->next=writePointer; // last to next = start
00108         readPointer=writePointer;
00109         readAheadPointer=readPointer;
00110         writeAheadPointer=writePointer;
00111         readCount=writeCount=0;
00112     }
00113 
00114     template <class SingleProducerConsumerType>
00115         SingleProducerConsumer<SingleProducerConsumerType>::~SingleProducerConsumer()
00116     {
00117         volatile DataPlusPtr *next;
00118         readPointer=writeAheadPointer->next;
00119         while (readPointer!=writeAheadPointer)
00120         {
00121             next=readPointer->next;
00122             RakNet::OP_DELETE((char*) readPointer, _FILE_AND_LINE_);
00123             readPointer=next;
00124         }
00125         RakNet::OP_DELETE((char*) readPointer, _FILE_AND_LINE_);
00126     }
00127 
00128     template <class SingleProducerConsumerType>
00129         SingleProducerConsumerType* SingleProducerConsumer<SingleProducerConsumerType>::WriteLock( void )
00130     {
00131         if (writeAheadPointer->next==readPointer ||
00132             writeAheadPointer->next->readyToRead==true)
00133         {
00134             volatile DataPlusPtr *originalNext=writeAheadPointer->next;
00135             writeAheadPointer->next=RakNet::OP_NEW<DataPlusPtr>(_FILE_AND_LINE_);
00136             RakAssert(writeAheadPointer->next);
00137             writeAheadPointer->next->next=originalNext;
00138         }
00139 
00140         volatile DataPlusPtr *last;
00141         last=writeAheadPointer;
00142         writeAheadPointer=writeAheadPointer->next;
00143 
00144         return (SingleProducerConsumerType*) last;
00145     }
00146 
00147     template <class SingleProducerConsumerType>
00148         void SingleProducerConsumer<SingleProducerConsumerType>::CancelWriteLock( SingleProducerConsumerType* cancelToLocation )
00149     {
00150         writeAheadPointer=(DataPlusPtr *)cancelToLocation;
00151     }
00152 
00153     template <class SingleProducerConsumerType>
00154         void SingleProducerConsumer<SingleProducerConsumerType>::WriteUnlock( void )
00155     {
00156         //  DataPlusPtr *dataContainer = (DataPlusPtr *)structure;
00157 
00158 #ifdef _DEBUG
00159         RakAssert(writePointer->next!=readPointer);
00160         RakAssert(writePointer!=writeAheadPointer);
00161 #endif
00162 
00163         writeCount++;
00164         // User is done with the data, allow send by updating the write pointer
00165         writePointer->readyToRead=true;
00166         writePointer=writePointer->next;
00167     }
00168 
00169     template <class SingleProducerConsumerType>
00170         SingleProducerConsumerType* SingleProducerConsumer<SingleProducerConsumerType>::ReadLock( void )
00171     {
00172             if (readAheadPointer==writePointer ||
00173                 readAheadPointer->readyToRead==false)
00174             {
00175                 return 0;
00176             }
00177 
00178             volatile DataPlusPtr *last;
00179             last=readAheadPointer;
00180             readAheadPointer=readAheadPointer->next;
00181             return (SingleProducerConsumerType*)last;
00182     }
00183 
00184     template <class SingleProducerConsumerType>
00185         void SingleProducerConsumer<SingleProducerConsumerType>::CancelReadLock( SingleProducerConsumerType* cancelToLocation )
00186     {
00187 #ifdef _DEBUG
00188         RakAssert(readPointer!=writePointer);
00189 #endif
00190         readAheadPointer=(DataPlusPtr *)cancelToLocation;
00191     }
00192 
00193     template <class SingleProducerConsumerType>
00194         void SingleProducerConsumer<SingleProducerConsumerType>::ReadUnlock( void )
00195     {
00196 #ifdef _DEBUG
00197         RakAssert(readAheadPointer!=readPointer); // If hits, then called ReadUnlock before ReadLock
00198         RakAssert(readPointer!=writePointer); // If hits, then called ReadUnlock when Read returns 0
00199 #endif
00200         readCount++;
00201 
00202         // Allow writes to this memory block
00203         readPointer->readyToRead=false;
00204         readPointer=readPointer->next;
00205     }
00206 
00207     template <class SingleProducerConsumerType>
00208         void SingleProducerConsumer<SingleProducerConsumerType>::Clear( void )
00209     {
00210         // Shrink the list down to MINIMUM_LIST_SIZE elements
00211         volatile DataPlusPtr *next;
00212         writePointer=readPointer->next;
00213 
00214         int listSize=1;
00215         next=readPointer->next;
00216         while (next!=readPointer)
00217         {
00218             listSize++;
00219             next=next->next;
00220         }
00221 
00222         while (listSize-- > MINIMUM_LIST_SIZE)
00223         {
00224             next=writePointer->next;
00225 #ifdef _DEBUG
00226             RakAssert(writePointer!=readPointer);
00227 #endif
00228             RakNet::OP_DELETE((char*) writePointer, _FILE_AND_LINE_);
00229             writePointer=next;
00230         }
00231 
00232         readPointer->next=writePointer;
00233         writePointer=readPointer;
00234         readAheadPointer=readPointer;
00235         writeAheadPointer=writePointer;
00236         readCount=writeCount=0;
00237     }
00238 
00239     template <class SingleProducerConsumerType>
00240         int SingleProducerConsumer<SingleProducerConsumerType>::Size( void ) const
00241     {
00242         return writeCount-readCount;
00243     }
00244 
00245     template <class SingleProducerConsumerType>
00246         bool SingleProducerConsumer<SingleProducerConsumerType>::CheckReadUnlockOrder(const SingleProducerConsumerType* data) const
00247     {
00248         return const_cast<const SingleProducerConsumerType *>(&readPointer->object) == data;
00249     }
00250 
00251 
00252     template <class SingleProducerConsumerType>
00253         bool SingleProducerConsumer<SingleProducerConsumerType>::ReadIsLocked(void) const
00254     {
00255         return readAheadPointer!=readPointer;
00256     }   
00257 }
00258 
00259 #endif

Copyright © 2007-2010 by The Shadowrun: Awakened Team. This work is licensed under the GNU Lesser General Public License 3.

GNU Lesser General Public License 3 Sourceforge.net