/* * PROGRAM: Client/Server Common Code * MODULE: alloc.h * DESCRIPTION: Memory Pool Manager (based on B+ tree) * * The contents of this file are subject to the Interbase Public * License Version 1.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy * of the License at http://www.Inprise.com/IPL.html * * Software distributed under the License is distributed on an * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express * or implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code was created by Inprise Corporation * and its predecessors. Portions created by Inprise Corporation are * Copyright (C) Inprise Corporation. * * Created by: Nickolay Samofatov * * STL allocator is based on one by Mike Nordell and John Bellardo * * All Rights Reserved. * Contributor(s): ______________________________________. */ #ifndef ALLOC_H #define ALLOC_H #ifdef HAVE_MALLOC_H #include #endif #include "../../include/fb_types.h" #include "../../include/firebird.h" #include "../jrd/common.h" #include "../jrd/ib_stdio.h" #include "tree.h" #include "locks.h" #define MAX_TREE_DEPTH 4 // Must be a power of 2 #define ALLOC_ALIGNMENT 4 #define MEM_ALIGN(X) FB_ALIGN(X,ALLOC_ALIGNMENT) namespace Firebird { struct MemoryBlock /* 16 bytes of block header is not too much I think */ { class MemoryPool *pool; bool used; bool last; SSHORT type; size_t length; /* Includes only actual block size, header not included */ struct MemoryBlock *prev; #ifdef DEBUG_GDS_ALLOC char *file; int line; #endif }; #define TYPE_POOL -1 #define TYPE_EXTENT -2 #define TYPE_LEAFPAGE -3 #define TYPE_TREEPAGE -4 // We store BlkInfo structures instead of BlkHeader pointers to get benefits from // processor cache-hit optimizations struct BlockInfo { MemoryBlock *block; size_t length; static bool compare(const BlockInfo& i1, const BlockInfo& i2) { return (i1.length > i2.length) || (i1.length == i2.length && i1.block > i2.block); } }; struct MemoryExtent { MemoryExtent *next; }; struct PendingFreeBlock { PendingFreeBlock *next; }; // Memory pool based on B+ tree of free memory blocks // We are going to have two target architectures: // 1. Multi-process server with customizable lock manager // 2. Multi-threaded server with single process (SUPERSERVER) // // MemoryPool inheritance looks weird because we cannot use // any pointers to functions in shared memory. VMT usage in // MemoryPool and its descendants is prohibited class MemoryPool { private: class InternalAllocator { public: void* allocate(size_t size) { return ((MemoryPool*)this)->tree_alloc(size); } void deallocate(void* block) { ((MemoryPool*)this)->tree_free(block); } }; typedef BePlusTree, BlockInfo> FreeBlocksTree; FreeBlocksTree freeBlocks; // B+ tree ordered by (length,address) MemoryExtent *extents; // Linked list of all memory extents Vector spareLeafs; Vector spareNodes; bool needSpare; PendingFreeBlock *pendingFree; #ifdef SUPERSERVER Spinlock lock; #else SharedSpinlock lock; #endif int extents_memory; // Sum of memory in allocated extents minus size of extents headers int used_memory; // Size of used memory blocks including block headers /* Returns NULL in case it cannot allocate requested chunk */ static void* external_alloc(size_t size); static void external_free(void *blk); void* tree_alloc(size_t size); void tree_free(void* block); void updateSpare(); void addFreeBlock(MemoryBlock *blk); void removeFreeBlock(MemoryBlock *blk); void free_blk_extent(MemoryBlock *blk); // does all the stuff except locking and exceptions void* internal_alloc(size_t size, SSHORT type = 0 #ifdef DEBUG_GDS_ALLOC , char *file = NULL, int line = 0 #endif ); protected: int *cur_memory; int *max_memory; // Do not allow to create and destroy pool directly from outside MemoryPool(void *first_extent, void *root_page, int* cur_mem = NULL, int* max_mem = NULL); // This should never be called ~MemoryPool() { } static MemoryPool* internal_create(size_t instance_size, int *cur_mem = NULL, int *max_mem = NULL); public: static int process_max_memory; static int process_current_memory; // Move usage stats to another location void moveStats(int *cur_mem, int *max_mem) { *cur_mem = *cur_memory; *max_mem = *max_memory; cur_memory = cur_mem; max_memory = max_mem; } static MemoryPool* createPool() { return internal_create(sizeof(MemoryPool)); } static void deletePool(MemoryPool* pool); void* allocate(size_t size, SSHORT type = 0 #ifdef DEBUG_GDS_ALLOC , char *file = NULL, int line = 0 #endif ); void deallocate(void *block); bool verify_pool(); void print_contents(IB_FILE *, bool = false); static void globalFree(void *block) { if (block) ((MemoryBlock*)((char*)block-MEM_ALIGN(sizeof(MemoryBlock))))->pool->deallocate(block); } void* calloc(size_t size, SSHORT type = 0 #ifdef DEBUG_GDS_ALLOC , char *file = NULL, int line = 0 #endif ) { void* result = allocate(size, type #ifdef DEBUG_GDS_ALLOC , file, line #endif ); memset(result,0,size); return result; } /// Returns the type associated with the allocated memory. static SSHORT blk_type(const void* mem) { return ((MemoryBlock*)((char *)mem - MEM_ALIGN(sizeof(MemoryBlock))))->type; } /// Returns the pool the memory was allocated from. static MemoryPool* blk_pool(const void* mem) { return ((MemoryBlock*)((char *)mem - MEM_ALIGN(sizeof(MemoryBlock))))->pool; } friend class InternalAllocator; }; }; // namespace Firebird #ifndef TESTING_ONLY using Firebird::MemoryPool; MemoryPool* getDefaultMemoryPool(); // Global versions of operator new() for compatibility with crappy libraries void* operator new(size_t); void* operator new[](size_t); #ifdef DEBUG_GDS_ALLOC void* operator new(size_t s, Firebird::MemoryPool& pool, char* file, int line); void* operator new[](size_t s, Firebird::MemoryPool& pool, char* file, int line); #define FB_NEW(pool) new(pool,__FILE__,__LINE__) #define FB_NEW_RPT(pool,count) new(pool,count,__FILE__,__LINE__) #else void* operator new(size_t s, Firebird::MemoryPool& pool) throw(std::bad_alloc); void* operator new[](size_t s, Firebird::MemoryPool& pool) throw(std::bad_alloc); #define FB_NEW(pool) new(pool) #define FB_NEW_RPT(pool,count) new(pool,count) #endif // We cannot use inline versions because we have to replace STL delete defined in header // One more performance pain we have to take because of STL usage :(( void operator delete(void* mem) throw(); void operator delete[](void* mem) throw(); /** This is the allocator template provided to be used with the STL. Since the STL is the client of this class look to its documentation to determine what the individual functions and typedefs do. In order to use the allocator class you need to instansiate the C++ container template with the allocator. For example if you want to use a std::vector the declaration would be: std::vector > The allocator, by default, allocates all memory from the process wide pool FB_MemoryPool. Typically this is NOT the behavior you want. Selection of the correct pool to allocate your memory from is important. If you select a pool to far down in (a statement pool, for example) you memory may be freed before you are done with it. On the other hand if you always use the global pool you will either leak memory or have to make sure you always delete the objects you create. If you decide to allocate the memory from a pool other than the global pool you need to pass an allocator object to the constructor for the STL object. For example: std::vector > vec(MemoryPool::allocator(poolRef, type)); The type is an optional parameter that defaults to 0. **/ namespace Firebird { template class allocator { public: typedef size_t size_type; typedef ptrdiff_t difference_type; typedef T* pointer; typedef const T* const_pointer; typedef T& reference; typedef const T& const_reference; typedef T value_type; allocator(MemoryPool& p, SSHORT t = 0) : pool(&p), type(t) {} allocator(MemoryPool *p = getDefaultMemoryPool(), SSHORT t = 0) : pool(p), type(t) {} template allocator(const allocator &alloc) : pool(alloc.getPool()), type(alloc.getType()) { } #ifdef DEBUG_GDS_ALLOC pointer allocate(size_type s, const void * = 0) { return (pointer) pool->allocate(sizeof(T) * s, 0, __FILE__, __LINE__); } char *_Charalloc(size_type n) { return (char*) pool->allocate(n, 0, __FILE__, __LINE__); } #else pointer allocate(size_type s, const void * = 0) { return (pointer) pool->allocate(sizeof(T) * s, 0); } char *_Charalloc(size_type n) { return (char*) pool->allocate(n, 0); } #endif /*#ifdef DEBUG_GDS_ALLOC pointer allocate(size_type s, const void * = 0) { return (pointer) pool->calloc(sizeof(T) * s, 0, __FILE__, __LINE__); } char *_Charalloc(size_type n) { return (char*) pool->calloc(n, 0, __FILE__, __LINE__); } #else pointer allocate(size_type s, const void * = 0) { return (pointer) pool->calloc(sizeof(T) * s, 0); } char *_Charalloc(size_type n) { return (char*) pool->calloc(n, 0); } #endif*/ void deallocate(pointer p, size_type s) { pool->deallocate(p); } void deallocate(void* p, size_type s) { pool->deallocate(p); } void construct(pointer p, const T& v) { new(p) T(v); } void destroy(pointer p) { p->~T(); } size_type max_size() const { return (size_type)-1 / sizeof(T); } pointer address(reference X) const { return &X; } const_pointer address(const_reference X) const { return &X; } template struct rebind { typedef Firebird::allocator<_Tp1> other; }; bool operator==(const allocator& rhs) const { return pool == rhs.pool && type == rhs.type; } MemoryPool *getPool() const { return pool; } SSHORT getType() const { return type; } private: MemoryPool *pool; SSHORT type; }; }; #endif /*TESTING_ONLY*/ #endif