// -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- // vim:tabstop=4:shiftwidth=4:expandtab: /* * Copyright (C) 2004-2008 Wu Yongwei * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any * damages arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute * it freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must * not claim that you wrote the original software. If you use this * software in a product, an acknowledgement in the product * documentation would be appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must * not be misrepresented as being the original software. * 3. This notice may not be removed or altered from any source * distribution. * * This file is part of Stones of Nvwa: * http://sourceforge.net/projects/nvwa * */ /*! * \file static_mem_pool.h * \ingroup QxMemLeak * * Header file for the `static' memory pool. * * \version 1.20, 2007/10/20 * \author Wu Yongwei * */ #ifndef QT_NO_DEBUG #ifndef _QX_MODE_RELEASE #if _QX_USE_MEM_LEAK_DETECTION #ifndef _STATIC_MEM_POOL_H #define _STATIC_MEM_POOL_H #ifdef _MSC_VER #pragma once #endif #include #include #include #include #include #include #include "class_level_lock.h" #include "mem_pool_base.h" /* Defines Work-around for Microsoft Visual C++ 6.0 and Borland C++ 5.5.1 */ # if (defined(_MSC_VER) && _MSC_VER < 1300) \ || (defined(__BORLANDC__) && __BORLANDC__ < 0x600) # define __PRIVATE public # else # define __PRIVATE private # endif /* Defines the macro for debugging output */ # ifdef _STATIC_MEM_POOL_DEBUG # include # define _STATIC_MEM_POOL_TRACE(_Lck, _Msg) \ { \ if (_Lck) { \ static_mem_pool_set::lock __guard; \ std::cerr << "static_mem_pool: " << _Msg << std::endl; \ } else { \ std::cerr << "static_mem_pool: " << _Msg << std::endl; \ } \ } # else # define _STATIC_MEM_POOL_TRACE(_Lck, _Msg) \ ((void)0) # endif namespace qx { namespace memory { /** * Singleton class to maintain a set of existing instantiations of * static_mem_pool. */ class QX_DLL_EXPORT static_mem_pool_set { public: typedef class_level_lock::lock lock; static static_mem_pool_set& instance(); void recycle(); void add(mem_pool_base* __memory_pool_p); __PRIVATE: ~static_mem_pool_set(); private: static_mem_pool_set(); typedef std::vector container_type; container_type _M_memory_pool_set; /* Forbid their use */ static_mem_pool_set(const static_mem_pool_set&); const static_mem_pool_set& operator=(const static_mem_pool_set&); }; /** * Singleton class template to manage the allocation/deallocation of * memory blocks of one specific size. * * @param _Sz size of elements in the static_mem_pool * @param _Gid group id of a static_mem_pool: if it is negative, * simultaneous accesses to this static_mem_pool will be * protected from each other; otherwise no protection is * given */ template class static_mem_pool : public mem_pool_base { typedef typename class_level_lock, (_Gid < 0)> ::lock lock; public: /** * Gets the instance of the static memory pool. It will create the * instance if it does not already exist. Generally this function * is now not needed. * * @return reference to the instance of the static memory pool * @see instance_known */ static static_mem_pool& instance() { lock __guard; if (!_S_instance_p) { _S_instance_p = _S_create_instance(); } return *_S_instance_p; } /** * Gets the known instance of the static memory pool. The instance * must already exist. Generally the static initializer of the * template guarantees it. * * @return reference to the instance of the static memory pool */ static static_mem_pool& instance_known() { assert(_S_instance_p != NULL); return *_S_instance_p; } /** * Allocates memory and returns its pointer. The template will try * to get it from the memory pool first, and request memory from the * system if there is no free memory in the pool. * * @return pointer to allocated memory if successful; \c NULL * otherwise */ void* allocate() { { lock __guard; if (_S_memory_block_p) { void* __result = _S_memory_block_p; _S_memory_block_p = _S_memory_block_p->_M_next; return __result; } } return _S_alloc_sys(_S_align(_Sz)); } /** * Deallocates memory by putting the memory block into the pool. * * @param __ptr pointer to memory to be deallocated */ void deallocate(void* __ptr) { assert(__ptr != NULL); lock __guard; _Block_list* __block_ = reinterpret_cast<_Block_list*>(__ptr); __block_->_M_next = _S_memory_block_p; _S_memory_block_p = __block_; } virtual void recycle(); private: static_mem_pool() { _STATIC_MEM_POOL_TRACE(true, "static_mem_pool<" << _Sz << ',' << _Gid << "> is created"); } ~static_mem_pool() { #ifndef _QX_MODE_RELEASE #ifndef QT_NO_DEBUG // Empty the pool to avoid false memory leakage alarms. This is // generally not necessary for release binaries. _Block_list* __block_ = _S_memory_block_p; while (__block_) { _Block_list* __next_ = __block_->_M_next; dealloc_sys(__block_); __block_ = __next_; } _S_memory_block_p = NULL; #endif // QT_NO_DEBUG #endif // _QX_MODE_RELEASE _S_instance_p = NULL; _S_destroyed = true; _STATIC_MEM_POOL_TRACE(false, "static_mem_pool<" << _Sz << ',' << _Gid << "> is destroyed"); } static size_t _S_align(size_t __size) { return __size >= sizeof(_Block_list) ? __size : sizeof(_Block_list); } static void* _S_alloc_sys(size_t __size); static static_mem_pool* _S_create_instance(); static bool _S_destroyed; static static_mem_pool* _S_instance_p; static mem_pool_base::_Block_list* _S_memory_block_p; /* Forbid their use */ static_mem_pool(const static_mem_pool&); const static_mem_pool& operator=(const static_mem_pool&); }; template bool static_mem_pool<_Sz, _Gid>::_S_destroyed = false; template mem_pool_base::_Block_list* static_mem_pool<_Sz, _Gid>::_S_memory_block_p = NULL; template static_mem_pool<_Sz, _Gid>* static_mem_pool<_Sz, _Gid>::_S_instance_p = _S_create_instance(); /** * Recycles half of the free memory blocks in the memory pool to the * system. It is called when a memory request to the system (in other * instances of the static memory pool) fails. */ template void static_mem_pool<_Sz, _Gid>::recycle() { // Only here the global lock in static_mem_pool_set is obtained // before the pool-specific lock. However, no race conditions are // found so far. lock __guard; _Block_list* __block_ = _S_memory_block_p; while (__block_) { if (_Block_list* __temp_ = __block_->_M_next) { _Block_list* __next_ = __temp_->_M_next; __block_->_M_next = __next_; dealloc_sys(__temp_); __block_ = __next_; } else { break; } } _STATIC_MEM_POOL_TRACE(false, "static_mem_pool<" << _Sz << ',' << _Gid << "> is recycled"); } template void* static_mem_pool<_Sz, _Gid>::_S_alloc_sys(size_t __size) { static_mem_pool_set::lock __guard; void* __result = mem_pool_base::alloc_sys(__size); if (!__result) { static_mem_pool_set::instance().recycle(); __result = mem_pool_base::alloc_sys(__size); } return __result; } template static_mem_pool<_Sz, _Gid>* static_mem_pool<_Sz, _Gid>::_S_create_instance() { if (_S_destroyed) throw std::runtime_error("dead reference detected"); static_mem_pool_set::instance(); // Force its creation static_mem_pool* __inst_p = new static_mem_pool(); try { static_mem_pool_set::instance().add(__inst_p); } catch (...) { _STATIC_MEM_POOL_TRACE(true, "Exception occurs in static_mem_pool_set::add"); // The strange cast below is to work around a bug in GCC 2.95.3 delete static_cast(__inst_p); throw; } return __inst_p; } } // namespace memory } // namespace qx #define DECLARE_STATIC_MEM_POOL(_Cls) \ public: \ static void* operator new(size_t __size) \ { \ assert(__size == sizeof(_Cls)); \ void* __ptr; \ __ptr = static_mem_pool:: \ instance_known().allocate(); \ if (__ptr == NULL) \ throw std::bad_alloc(); \ return __ptr; \ } \ static void operator delete(void* __ptr) \ { \ if (__ptr) \ static_mem_pool:: \ instance_known().deallocate(__ptr); \ } #define DECLARE_STATIC_MEM_POOL__NOTHROW(_Cls) \ public: \ static void* operator new(size_t __size) throw() \ { \ assert(__size == sizeof(_Cls)); \ return static_mem_pool:: \ instance_known().allocate(); \ } \ static void operator delete(void* __ptr) \ { \ if (__ptr) \ static_mem_pool:: \ instance_known().deallocate(__ptr); \ } #define DECLARE_STATIC_MEM_POOL_GROUPED(_Cls, _Gid) \ public: \ static void* operator new(size_t __size) \ { \ assert(__size == sizeof(_Cls)); \ void* __ptr; \ __ptr = static_mem_pool:: \ instance_known().allocate(); \ if (__ptr == NULL) \ throw std::bad_alloc(); \ return __ptr; \ } \ static void operator delete(void* __ptr) \ { \ if (__ptr) \ static_mem_pool:: \ instance_known().deallocate(__ptr); \ } #define DECLARE_STATIC_MEM_POOL_GROUPED__NOTHROW(_Cls, _Gid) \ public: \ static void* operator new(size_t __size) throw() \ { \ assert(__size == sizeof(_Cls)); \ return static_mem_pool:: \ instance_known().allocate(); \ } \ static void operator delete(void* __ptr) \ { \ if (__ptr) \ static_mem_pool:: \ instance_known().deallocate(__ptr); \ } // OBSOLETE: no longer needed #define PREPARE_STATIC_MEM_POOL(_Cls) \ std::cerr << "PREPARE_STATIC_MEM_POOL is obsolete!\n"; // OBSOLETE: no longer needed #define PREPARE_STATIC_MEM_POOL_GROUPED(_Cls, _Gid) \ std::cerr << "PREPARE_STATIC_MEM_POOL_GROUPED is obsolete!\n"; #undef __PRIVATE #endif // _STATIC_MEM_POOL_H #endif // _QX_USE_MEM_LEAK_DETECTION #endif // _QX_MODE_RELEASE #endif // QT_NO_DEBUG