Scythe-1.0.3
|
00001 00002 /* 00003 * Scythe Statistical Library Copyright (C) 2000-2002 Andrew D. Martin 00004 * and Kevin M. Quinn; 2002-present Andrew D. Martin, Kevin M. Quinn, 00005 * and Daniel Pemstein. All Rights Reserved. 00006 * 00007 * This program is free software; you can redistribute it and/or 00008 * modify under the terms of the GNU General Public License as 00009 * published by Free Software Foundation; either version 2 of the 00010 * License, or (at your option) any later version. See the text files 00011 * COPYING and LICENSE, distributed with this source code, for further 00012 * information. 00013 * -------------------------------------------------------------------- 00014 * scythestat/datablock.h 00015 */ 00016 00040 #ifndef SCYTHE_DATABLOCK_H 00041 #define SCYTHE_DATABLOCK_H 00042 00043 #ifdef SCYTHE_COMPILE_DIRECT 00044 #include "error.h" 00045 #else 00046 #include "scythestat/error.h" 00047 #endif 00048 00049 #ifdef SCYTHE_PTHREAD 00050 #include <pthread.h> 00051 #endif 00052 00053 namespace scythe { 00054 /* Convenience typedefs */ 00055 namespace { // local to this file 00056 typedef unsigned int uint; 00057 } 00058 00065 template <typename T_type> 00066 class DataBlock { 00067 public: 00068 /**** CONSTRUCTORS ****/ 00069 00070 /* 00071 * Create an empty data block. 00072 */ 00073 00074 DataBlock () 00075 : data_ (0), 00076 size_ (0), 00077 refs_ (0) 00078 {} 00079 00080 /* 00081 * Create a block of a given size. 00082 */ 00083 explicit 00084 DataBlock (uint size) 00085 : data_ (0), 00086 size_ (0), 00087 refs_ (0) 00088 { 00089 resize(size); 00090 SCYTHE_DEBUG_MSG("Constructed new " << size << "(" << size_ 00091 << ") DataBlock at address " << data_); 00092 } 00093 00094 /* 00095 * Create an exact copy of another data block. 00096 */ 00097 DataBlock (const DataBlock<T_type>& b) 00098 : data_ (b.data_), 00099 size_ (b.size_), 00100 refs_ (b.refs_) 00101 {} 00102 00103 /**** DESTRUCTOR ****/ 00104 00105 ~DataBlock () 00106 { 00107 SCYTHE_DEBUG_MSG("Destructing block at " << data_); 00108 deallocate(); 00109 } 00110 00111 /**** REFERENCE COUNTING ****/ 00112 00113 inline uint addReference () 00114 { 00115 SCYTHE_DEBUG_MSG("Added reference to DataBlock at address " 00116 << data_); 00117 return ++refs_; 00118 } 00119 00120 inline uint removeReference () 00121 { 00122 SCYTHE_DEBUG_MSG("Removed reference to DataBlock at address " 00123 << data_); 00124 return --refs_ ; 00125 } 00126 00127 inline uint references () 00128 { 00129 return refs_; 00130 } 00131 00132 /**** ACCESSORS ****/ 00133 00134 inline T_type* data() 00135 { 00136 return data_; 00137 } 00138 00139 inline const T_type* data() const 00140 { 00141 return data_; 00142 } 00143 00144 inline uint size () const 00145 { 00146 return size_; 00147 } 00148 00149 protected: 00150 /**** (DE)ALLOCATION AND RESIZING ****/ 00151 00152 /* Allocate data given the current block size. */ 00153 inline void allocate (uint size) 00154 { 00155 /* TODO Think about cache boundary allocations for big blocks 00156 * see blitz++ */ 00157 00158 if (data_ != 0) // Get rid of previous allocation if it exists 00159 deallocate(); 00160 00161 data_ = new (std::nothrow) T_type[size]; 00162 00163 SCYTHE_CHECK_10(data_ == 0, scythe_alloc_error, 00164 "Failure allocating DataBlock of size " << size); 00165 } 00166 00167 /* Deallocate a block's data */ 00168 inline void deallocate () 00169 { 00170 SCYTHE_DEBUG_MSG(" Deallocating DataBlock of size " << size_ 00171 << " at address " << data_); 00172 delete[] data_; 00173 data_ = 0; 00174 } 00175 00176 public: 00177 /* TODO At the moment, references call this method directly. Not 00178 * sure if this is the best interface choice. */ 00179 /* Resize a block. */ 00180 void resize (uint newsize) 00181 { 00182 if (newsize > size_) 00183 grow(newsize); 00184 else if (newsize < size_ / 4) 00185 shrink(); 00186 } 00187 00188 protected: 00189 /* Make a block larger. Expects to be called by resize and does 00190 * not reset the size_ variable. */ 00191 inline void grow (uint newsize) 00192 { 00193 size_ = size_ ? size_ : 1; // make sure not zero 00194 00195 /* TODO Can we speed this up? In 20 iters we're at 00196 * 1048576 elems doing the math might be more costly... 00197 */ 00198 while (size_ < newsize) 00199 size_ <<= 1; 00200 00201 allocate(size_); 00202 } 00203 00204 /* Make a block smaller. Expects to be called by resize */ 00205 inline void shrink () 00206 { 00207 size_ >>= 1; 00208 allocate(size_); 00209 } 00210 00211 private: 00212 /**** INSTANCE VARIABLES ****/ 00213 T_type *data_; // The data array 00214 uint size_; // The number of elements in the block 00215 uint refs_; // The number of views looking at this block 00216 }; // end class DataBlock 00217 00222 template <class T_type> 00223 class NullDataBlock : public DataBlock<T_type> 00224 { 00225 typedef DataBlock<T_type> T_base; 00226 public: 00227 00228 NullDataBlock () 00229 : DataBlock<T_type> () 00230 { 00231 // never want to deallocate (or resize) this one 00232 T_base::addReference(); 00233 SCYTHE_DEBUG_MSG("Constructed NULL datablock"); 00234 } 00235 00236 ~NullDataBlock () 00237 {} 00238 00239 }; // end class NullDataBlock 00240 00241 00249 template <class T_type> 00250 class DataBlockReference { 00251 public: 00252 /**** CONSTRUCTORS ****/ 00253 00254 /* Default constructor: points the object at a static null block 00255 */ 00256 DataBlockReference () 00257 : data_ (0), 00258 block_ (&nullBlock_) 00259 { 00260 #ifdef SCYTHE_PTHREAD 00261 pthread_mutex_lock (&ndbMutex_); 00262 #endif 00263 block_->addReference(); 00264 #ifdef SCYTHE_PTHREAD 00265 pthread_mutex_unlock (&ndbMutex_); 00266 #endif 00267 } 00268 00269 /* New block constructor: creates a new underlying block of a 00270 * given size and points at it. */ 00271 explicit 00272 DataBlockReference (uint size) 00273 : data_ (0), 00274 block_ (0) 00275 { 00276 block_ = new (std::nothrow) DataBlock<T_type> (size); 00277 SCYTHE_CHECK_10 (block_ == 0, scythe_alloc_error, 00278 "Could not allocate DataBlock object"); 00279 00280 data_ = block_->data(); 00281 block_->addReference(); 00282 } 00283 00284 /* Refrence to an existing block constructor: points to an 00285 * offset within an existing block. */ 00286 DataBlockReference (const DataBlockReference<T_type>& reference, 00287 uint offset = 0) 00288 : data_ (reference.data_ + offset), 00289 block_ (reference.block_) 00290 { 00291 #ifdef SCYTHE_PTHREAD 00292 bool lock = false; 00293 if (block_ == &nullBlock_) { 00294 pthread_mutex_lock (&ndbMutex_); 00295 lock = true; 00296 } 00297 #endif 00298 block_->addReference(); 00299 #ifdef SCYTHE_PTHREAD 00300 if (lock) 00301 pthread_mutex_unlock (&ndbMutex_); 00302 #endif 00303 } 00304 00305 /**** DESTRUCTOR ****/ 00306 /* Automates removal of underlying block objects when refcount 00307 * hits nil. 00308 */ 00309 virtual ~DataBlockReference () 00310 { 00311 #ifdef SCYTHE_PTHREAD 00312 bool lock = false; 00313 if (block_ == &nullBlock_) { 00314 pthread_mutex_lock (&ndbMutex_); 00315 lock = true; 00316 } 00317 #endif 00318 withdrawReference(); 00319 #ifdef SCYTHE_PTHREAD 00320 if (lock) 00321 pthread_mutex_unlock (&ndbMutex_); 00322 #endif 00323 } 00324 00325 protected: 00326 00327 /**** MEMBERS CALLED BY DERIVED CLASS ****/ 00328 void referenceOther (const DataBlockReference<T_type>& ref, 00329 uint offset = 0) 00330 { 00331 #ifdef SCYTHE_PTHREAD 00332 bool lock = false; 00333 if (block_ == &nullBlock_ || ref.block_ == &nullBlock_) { 00334 pthread_mutex_lock (&ndbMutex_); 00335 lock = true; 00336 } 00337 #endif 00338 withdrawReference (); 00339 block_ = ref.block_; 00340 block_->addReference(); 00341 data_ = ref.data_ + offset; 00342 #ifdef SCYTHE_PTHREAD 00343 if (lock) 00344 pthread_mutex_lock (&ndbMutex_); 00345 #endif 00346 } 00347 00348 void referenceNew (uint size) 00349 { 00350 #ifdef SCYTHE_PTHREAD 00351 bool lock = false; 00352 if (block_ == &nullBlock_) { 00353 pthread_mutex_lock (&ndbMutex_); 00354 lock = true; 00355 } 00356 #endif 00357 /* If we are the only referent to this data block, resize it. 00358 * Otherwise, shift the reference to point to a newly 00359 * constructed block. 00360 */ 00361 if (block_->references() == 1) { 00362 block_->resize(size); 00363 data_ = block_->data(); // This is a pretty good indication 00364 // that the interface and implementation are too tightly 00365 // coupled for resizing. 00366 } else { 00367 withdrawReference(); 00368 block_ = 0; 00369 block_ = new (std::nothrow) DataBlock<T_type> (size); 00370 SCYTHE_CHECK_10(block_ == 0, scythe_alloc_error, 00371 "Could not allocate new data block"); 00372 data_ = block_->data(); 00373 block_->addReference(); 00374 } 00375 #ifdef SCYTHE_PTHREAD 00376 if (lock) 00377 pthread_mutex_unlock (&ndbMutex_); 00378 #endif 00379 } 00380 00381 private: 00382 /**** INTERNAL MEMBERS ****/ 00383 void withdrawReference () 00384 { 00385 // All calls to withdrawReference are mutex protected and protecting 00386 // this too can create a race condition. 00387 00388 if (block_->removeReference() == 0 00389 && block_ != &nullBlock_) 00390 delete block_; 00391 } 00392 00393 void referenceNull () 00394 { 00395 #ifdef SCYTHE_PTHREAD 00396 pthread_mutex_lock (&ndbMutex_); 00397 #endif 00398 withdrawReference(); 00399 block_ = &nullBlock_; 00400 block_->addReference(); 00401 data_ = 0; 00402 00403 #ifdef SCYTHE_PTHREAD 00404 pthread_mutex_unlock (&ndbMutex_); 00405 #endif 00406 } 00407 00408 00409 /**** INSTANCE VARIABLES ****/ 00410 protected: 00411 T_type* data_; // Pointer to the underlying data (offset) 00412 00413 private: 00414 DataBlock<T_type>* block_; 00415 static NullDataBlock<T_type> nullBlock_; 00416 #ifdef SCYTHE_PTHREAD 00417 static pthread_mutex_t ndbMutex_; 00418 #endif 00419 00420 }; // end class DataBlockReference 00421 00422 /* Instantiation of the static null memory block */ 00423 template <typename T> 00424 NullDataBlock<T> DataBlockReference<T>::nullBlock_; 00425 00426 #ifdef SCYTHE_PTHREAD 00427 // mutex initialization 00428 template <typename T> 00429 pthread_mutex_t 00430 DataBlockReference<T>::ndbMutex_ = PTHREAD_MUTEX_INITIALIZER; 00431 #endif 00432 00433 } // end namespace scythe 00434 00435 #endif /* SCYTHE_DATABLOCK_H */