00001 // -*- C++ -*- 00002 /*************************************************************************** 00003 * blitz/memblock.h MemoryBlock<T> and MemoryBlockReference<T> 00004 * 00005 * $Id: memblock.h,v 1.20 2008/02/21 03:21:53 julianc Exp $ 00006 * 00007 * Copyright (C) 1997-1999 Todd Veldhuizen <tveldhui@oonumerics.org> 00008 * 00009 * This program is free software; you can redistribute it and/or 00010 * modify it under the terms of the GNU General Public License 00011 * as published by the Free Software Foundation; either version 2 00012 * of the License, or (at your option) any later version. 00013 * 00014 * This program is distributed in the hope that it will be useful, 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00017 * GNU General Public License for more details. 00018 * 00019 * Suggestions: blitz-dev@oonumerics.org 00020 * Bugs: blitz-bugs@oonumerics.org 00021 * 00022 * For more information, please see the Blitz++ Home Page: 00023 * http://oonumerics.org/blitz/ 00024 * 00025 ***************************************************************************/ 00026 00027 #ifndef BZ_MEMBLOCK_H 00028 #define BZ_MEMBLOCK_H 00029 00030 #include <blitz/blitz.h> 00031 00032 #include <stddef.h> // ptrdiff_t 00033 00034 BZ_NAMESPACE(blitz) 00035 00036 enum preexistingMemoryPolicy { 00037 duplicateData, 00038 deleteDataWhenDone, 00039 neverDeleteData 00040 }; 00041 00042 // Forward declaration of MemoryBlockReference 00043 template<typename T_type> class MemoryBlockReference; 00044 00045 // Class MemoryBlock provides a reference-counted block of memory. This block 00046 // may be referred to by multiple vector, matrix and array objects. The memory 00047 // is automatically deallocated when the last referring object is destructed. 00048 // MemoryBlock may be subclassed to provide special allocators. 00049 template<typename P_type> 00050 class MemoryBlock { 00051 00052 friend class MemoryBlockReference<P_type>; 00053 00054 public: 00055 typedef P_type T_type; 00056 00057 protected: 00058 MemoryBlock() 00059 { 00060 length_ = 0; 00061 data_ = 0; 00062 dataBlockAddress_ = 0; 00063 references_ = 0; 00064 00065 BZ_MUTEX_INIT(mutex) 00066 mutexLocking_ = true; 00067 } 00068 00069 explicit MemoryBlock(size_t items) 00070 { 00071 length_ = items; 00072 allocate(length_); 00073 00074 #ifdef BZ_DEBUG_LOG_ALLOCATIONS 00075 cout << "MemoryBlock: allocated " << setw(8) << length_ 00076 << " at " << ((void *)dataBlockAddress_) << endl; 00077 #endif 00078 00079 BZASSERT(dataBlockAddress_ != 0); 00080 00081 references_ = 0; 00082 00083 BZ_MUTEX_INIT(mutex) 00084 mutexLocking_ = true; 00085 } 00086 00087 MemoryBlock(size_t length, T_type* data) 00088 { 00089 length_ = length; 00090 data_ = data; 00091 dataBlockAddress_ = data; 00092 references_ = 0; 00093 BZ_MUTEX_INIT(mutex) 00094 mutexLocking_ = true; 00095 } 00096 00097 virtual ~MemoryBlock() 00098 { 00099 if (dataBlockAddress_) 00100 { 00101 00102 #ifdef BZ_DEBUG_LOG_ALLOCATIONS 00103 cout << "MemoryBlock: freed " << setw(8) << length_ 00104 << " at " << ((void *)dataBlockAddress_) << endl; 00105 #endif 00106 00107 deallocate(); 00108 } 00109 00110 BZ_MUTEX_DESTROY(mutex) 00111 } 00112 00113 // set mutex locking policy and return true if successful 00114 bool doLock(bool lockingPolicy) 00115 { 00116 if (mutexLocking_ == lockingPolicy) { // already set 00117 return true; 00118 } 00119 else if (references_ <= 1) { // no multiple references, safe to change 00120 mutexLocking_ = lockingPolicy; 00121 return true; 00122 } 00123 return false; // unsafe to change 00124 } 00125 00126 void addReference() 00127 { 00128 if (mutexLocking_) { 00129 BZ_MUTEX_LOCK(mutex) 00130 } 00131 ++references_; 00132 00133 #ifdef BZ_DEBUG_LOG_REFERENCES 00134 cout << "MemoryBlock: reffed " << setw(8) << length_ 00135 << " at " << ((void *)dataBlockAddress_) << " (r=" 00136 << (int)references_ << ")" << endl; 00137 #endif 00138 if (mutexLocking_) { 00139 BZ_MUTEX_UNLOCK(mutex) 00140 } 00141 } 00142 00143 T_type* restrict data() 00144 { 00145 return data_; 00146 } 00147 00148 const T_type* restrict data() const 00149 { 00150 return data_; 00151 } 00152 00153 T_type*& dataBlockAddress() 00154 { 00155 return dataBlockAddress_; 00156 } 00157 00158 size_t length() const 00159 { 00160 return length_; 00161 } 00162 00163 int removeReference() 00164 { 00165 00166 if (mutexLocking_) { 00167 BZ_MUTEX_LOCK(mutex) 00168 } 00169 int refcount = --references_; 00170 00171 #ifdef BZ_DEBUG_LOG_REFERENCES 00172 cout << "MemoryBlock: dereffed " << setw(8) << length_ 00173 << " at " << ((void *)dataBlockAddress_) << " (r=" << (int)references_ 00174 << ")" << endl; 00175 #endif 00176 if (mutexLocking_) { 00177 BZ_MUTEX_UNLOCK(mutex) 00178 } 00179 return refcount; 00180 } 00181 00182 int references() const 00183 { 00184 if (mutexLocking_) { 00185 BZ_MUTEX_LOCK(mutex) 00186 } 00187 int refcount = references_; 00188 if (mutexLocking_) { 00189 BZ_MUTEX_UNLOCK(mutex) 00190 } 00191 00192 return refcount; 00193 } 00194 00195 protected: 00196 inline void allocate(size_t length); 00197 void deallocate(); 00198 00199 private: // Disabled member functions 00200 MemoryBlock(const MemoryBlock<T_type>&) 00201 { } 00202 00203 void operator=(const MemoryBlock<T_type>&) 00204 { } 00205 00206 private: // Data members 00207 T_type * restrict data_; 00208 T_type * dataBlockAddress_; 00209 00210 #ifdef BZ_DEBUG_REFERENCE_ROLLOVER 00211 volatile unsigned char references_; 00212 #else 00213 volatile int references_; 00214 #endif 00215 00216 BZ_MUTEX_DECLARE(mutex) 00217 bool mutexLocking_; 00218 size_t length_; 00219 }; 00220 00221 template<typename P_type> 00222 class MemoryBlockReference { 00223 00224 public: 00225 typedef P_type T_type; 00226 00227 protected: 00228 T_type * restrict data_; 00229 00230 private: 00231 MemoryBlock<T_type>* block_; 00232 00233 public: 00234 00235 MemoryBlockReference() 00236 { 00237 block_ = 0; 00238 addReference(); 00239 data_ = 0; 00240 } 00241 00242 MemoryBlockReference(MemoryBlockReference<T_type>& ref, size_t offset=0) 00243 { 00244 block_ = ref.block_; 00245 addReference(); 00246 data_ = ref.data_ + offset; 00247 } 00248 00249 MemoryBlockReference(size_t length, T_type* data, 00250 preexistingMemoryPolicy deletionPolicy) 00251 { 00252 // Create a memory block using already allocated memory. 00253 00254 // Note: if the deletionPolicy is duplicateData, this must 00255 // be handled by the leaf class. In MemoryBlockReference, 00256 // this is treated as neverDeleteData; the leaf class (e.g. Array) 00257 // must duplicate the data. 00258 00259 if ((deletionPolicy == neverDeleteData) 00260 || (deletionPolicy == duplicateData)) { 00261 // in this case, we do not need a MemoryBlock to ref-count the data 00262 block_ = 0; 00263 } 00264 else if (deletionPolicy == deleteDataWhenDone) { 00265 block_ = new MemoryBlock<T_type>(length, data); 00266 00267 #ifdef BZ_DEBUG_LOG_ALLOCATIONS 00268 cout << "MemoryBlockReference: created MemoryBlock at " 00269 << ((void*)block_) << endl; 00270 #endif 00271 } 00272 addReference(); 00273 00274 data_ = data; 00275 } 00276 00277 explicit MemoryBlockReference(size_t items) 00278 { 00279 block_ = new MemoryBlock<T_type>(items); 00280 addReference(); 00281 data_ = block_->data(); 00282 00283 #ifdef BZ_DEBUG_LOG_ALLOCATIONS 00284 cout << "MemoryBlockReference: created MemoryBlock at " 00285 << ((void*)block_) << endl; 00286 #endif 00287 00288 } 00289 00290 ~MemoryBlockReference() 00291 { 00292 blockRemoveReference(); 00293 } 00294 00295 protected: 00296 00297 int numReferences() const 00298 { 00299 if (block_) 00300 return block_->references(); 00301 #ifdef BZ_DEBUG_LOG_REFERENCES 00302 cout << "Invalid reference count for data at "<< data_ << endl; 00303 #endif 00304 return -1; 00305 } 00306 00307 bool lockReferenceCount(bool lockingPolicy) const 00308 { 00309 if (block_) 00310 return block_->doLock(lockingPolicy); 00311 // if we have no block, consider request successful 00312 #ifdef BZ_DEBUG_LOG_REFERENCES 00313 cout << "No reference count locking for data at "<< data_ << endl; 00314 #endif 00315 return true; 00316 } 00317 00318 void changeToNullBlock() 00319 { 00320 blockRemoveReference(); 00321 block_ = 0; 00322 addReference(); 00323 data_ = 0; 00324 } 00325 00326 void changeBlock(MemoryBlockReference<T_type>& ref, size_t offset=0) 00327 { 00328 blockRemoveReference(); 00329 block_ = ref.block_; 00330 addReference(); 00331 data_ = ref.data_ + offset; 00332 } 00333 00334 void newBlock(size_t items) 00335 { 00336 blockRemoveReference(); 00337 block_ = new MemoryBlock<T_type>(items); 00338 addReference(); 00339 data_ = block_->data(); 00340 00341 #ifdef BZ_DEBUG_LOG_ALLOCATIONS 00342 cout << "MemoryBlockReference: created MemoryBlock at " 00343 << ((void*)block_) << endl; 00344 #endif 00345 } 00346 00347 private: 00348 void blockRemoveReference() 00349 { 00350 int refcount = removeReference(); 00351 if (refcount == 0) 00352 { 00353 #ifdef BZ_DEBUG_LOG_ALLOCATIONS 00354 cout << "MemoryBlock: no more refs, delete MemoryBlock object at " 00355 << ((void*)block_) << endl; 00356 #endif 00357 00358 delete block_; 00359 } 00360 } 00361 00362 void addReference() const 00363 { 00364 if (block_) { 00365 block_->addReference(); 00366 } 00367 else { 00368 #ifdef BZ_DEBUG_LOG_REFERENCES 00369 cout << "Skipping reference count for data at "<< data_ << endl; 00370 #endif 00371 } 00372 }; 00373 00374 int removeReference() const 00375 { 00376 if (block_) 00377 return block_->removeReference(); 00378 #ifdef BZ_DEBUG_LOG_REFERENCES 00379 cout << "Skipping reference count for data at "<< data_ << endl; 00380 #endif 00381 return -1; 00382 }; 00383 00384 void operator=(const MemoryBlockReference<T_type>&) 00385 { } 00386 }; 00387 00388 00389 BZ_NAMESPACE_END 00390 00391 #include <blitz/memblock.cc> 00392 00393 #endif // BZ_MEMBLOCK_H