// -*- C++ -*- /*************************************************************************** * blitz/memblock.h MemoryBlock and MemoryBlockReference * * $Id: memblock.h,v 1.18 2005/10/11 21:54:57 julianc Exp $ * * Copyright (C) 1997-1999 Todd Veldhuizen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Suggestions: blitz-dev@oonumerics.org * Bugs: blitz-bugs@oonumerics.org * * For more information, please see the Blitz++ Home Page: * http://oonumerics.org/blitz/ * ***************************************************************************/ #ifndef BZ_MEMBLOCK_H #define BZ_MEMBLOCK_H #include #include // ptrdiff_t #ifdef BZ_THREADSAFE #include #endif BZ_NAMESPACE(blitz) enum preexistingMemoryPolicy { duplicateData, deleteDataWhenDone, neverDeleteData }; // Forward declaration of MemoryBlockReference template class MemoryBlockReference; // Class MemoryBlock provides a reference-counted block of memory. This block // may be referred to by multiple vector, matrix and array objects. The memory // is automatically deallocated when the last referring object is destructed. // MemoryBlock may be subclassed to provide special allocators. template class MemoryBlock { friend class MemoryBlockReference; public: typedef P_type T_type; protected: MemoryBlock() { length_ = 0; data_ = 0; dataBlockAddress_ = 0; references_ = 0; BZ_MUTEX_INIT(mutex) } explicit MemoryBlock(size_t items) { length_ = items; allocate(length_); #ifdef BZ_DEBUG_LOG_ALLOCATIONS cout << "MemoryBlock: allocated " << setw(8) << length_ << " at " << ((void *)dataBlockAddress_) << endl; #endif BZASSERT(dataBlockAddress_ != 0); references_ = 0; BZ_MUTEX_INIT(mutex) } MemoryBlock(size_t length, T_type* data) { length_ = length; data_ = data; dataBlockAddress_ = data; references_ = 0; BZ_MUTEX_INIT(mutex) } virtual ~MemoryBlock() { if (dataBlockAddress_) { #ifdef BZ_DEBUG_LOG_ALLOCATIONS cout << "MemoryBlock: freed " << setw(8) << length_ << " at " << ((void *)dataBlockAddress_) << endl; #endif deallocate(); } BZ_MUTEX_DESTROY(mutex) } void addReference() { BZ_MUTEX_LOCK(mutex) ++references_; #ifdef BZ_DEBUG_LOG_REFERENCES cout << "MemoryBlock: reffed " << setw(8) << length_ << " at " << ((void *)dataBlockAddress_) << " (r=" << (int)references_ << ")" << endl; #endif BZ_MUTEX_UNLOCK(mutex) } T_type* restrict data() { return data_; } const T_type* restrict data() const { return data_; } T_type*& dataBlockAddress() { return dataBlockAddress_; } size_t length() const { return length_; } int removeReference() { BZ_MUTEX_LOCK(mutex) int refcount = --references_; #ifdef BZ_DEBUG_LOG_REFERENCES cout << "MemoryBlock: dereffed " << setw(8) << length_ << " at " << ((void *)dataBlockAddress_) << " (r=" << (int)references_ << ")" << endl; #endif BZ_MUTEX_UNLOCK(mutex) return refcount; } int references() const { BZ_MUTEX_LOCK(mutex) int refcount = references_; BZ_MUTEX_UNLOCK(mutex) return refcount; } protected: inline void allocate(size_t length); void deallocate(); private: // Disabled member functions MemoryBlock(const MemoryBlock&) { } void operator=(const MemoryBlock&) { } private: // Data members T_type * restrict data_; T_type * dataBlockAddress_; #ifdef BZ_DEBUG_REFERENCE_ROLLOVER volatile unsigned char references_; #else volatile int references_; #endif BZ_MUTEX_DECLARE(mutex) size_t length_; }; template class UnownedMemoryBlock : public MemoryBlock { public: UnownedMemoryBlock(size_t length, P_type* data) : MemoryBlock(length,data) { // This ensures that MemoryBlock destructor will not // attempt to delete data MemoryBlock::dataBlockAddress() = 0; } virtual ~UnownedMemoryBlock() { } }; template class NullMemoryBlock : public MemoryBlock { public: NullMemoryBlock() { // This ensures that the delete operator will not be invoked // on an instance of NullMemoryBlock in removeReference(). MemoryBlock::addReference(); } virtual ~NullMemoryBlock() { } }; template class MemoryBlockReference { public: typedef P_type T_type; protected: T_type * restrict data_; private: MemoryBlock* block_; static NullMemoryBlock nullBlock_; public: MemoryBlockReference() { block_ = &nullBlock_; block_->addReference(); data_ = 0; } MemoryBlockReference(MemoryBlockReference& ref, size_t offset=0) { block_ = ref.block_; block_->addReference(); data_ = ref.data_ + offset; } MemoryBlockReference(size_t length, T_type* data, preexistingMemoryPolicy deletionPolicy) { // Create a memory block using already allocated memory. // Note: if the deletionPolicy is duplicateData, this must // be handled by the leaf class. In MemoryBlockReference, // this is treated as neverDeleteData; the leaf class (e.g. Array) // must duplicate the data. if ((deletionPolicy == neverDeleteData) || (deletionPolicy == duplicateData)) block_ = new UnownedMemoryBlock(length, data); else if (deletionPolicy == deleteDataWhenDone) block_ = new MemoryBlock(length, data); block_->addReference(); #ifdef BZ_DEBUG_LOG_ALLOCATIONS cout << "MemoryBlockReference: created MemoryBlock at " << ((void*)block_) << endl; #endif data_ = data; } explicit MemoryBlockReference(size_t items) { block_ = new MemoryBlock(items); block_->addReference(); data_ = block_->data(); #ifdef BZ_DEBUG_LOG_ALLOCATIONS cout << "MemoryBlockReference: created MemoryBlock at " << ((void*)block_) << endl; #endif } void blockRemoveReference() { int refcount = block_->removeReference(); if ((refcount == 0) && (block_ != &nullBlock_)) { #ifdef BZ_DEBUG_LOG_ALLOCATIONS cout << "MemoryBlock: no more refs, delete MemoryBlock object at " << ((void*)block_) << endl; #endif delete block_; } } ~MemoryBlockReference() { blockRemoveReference(); } int numReferences() const { return block_->references(); } protected: void changeToNullBlock() { blockRemoveReference(); block_ = &nullBlock_; block_->addReference(); data_ = 0; } void changeBlock(MemoryBlockReference& ref, size_t offset=0) { blockRemoveReference(); block_ = ref.block_; block_->addReference(); data_ = ref.data_ + offset; } void newBlock(size_t items) { blockRemoveReference(); block_ = new MemoryBlock(items); block_->addReference(); data_ = block_->data(); #ifdef BZ_DEBUG_LOG_ALLOCATIONS cout << "MemoryBlockReference: created MemoryBlock at " << ((void*)block_) << endl; #endif } private: void operator=(const MemoryBlockReference&) { } }; BZ_NAMESPACE_END #include #endif // BZ_MEMBLOCK_H