Scythe-1.0.3
datablock.h
Go to the documentation of this file.
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 */