![]() |
Shadowrun: Awakened 29 September 2011 - Build 871
|
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.