2002-12-14 22:43:18 +01:00
|
|
|
/*
|
|
|
|
* 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 <skidder@bssys.com>
|
2003-01-16 18:47:10 +01:00
|
|
|
*
|
|
|
|
* STL allocator is based on one by Mike Nordell and John Bellardo
|
2002-12-14 22:43:18 +01:00
|
|
|
*
|
|
|
|
* All Rights Reserved.
|
|
|
|
* Contributor(s): ______________________________________.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef ALLOC_H
|
|
|
|
#define ALLOC_H
|
|
|
|
|
|
|
|
#include <malloc.h>
|
|
|
|
#include "../../include/fb_types.h"
|
2003-01-07 17:35:10 +01:00
|
|
|
#include "../../include/firebird.h"
|
|
|
|
#include "../jrd/common.h"
|
2003-01-09 20:47:46 +01:00
|
|
|
#include "../jrd/ib_stdio.h"
|
2002-12-14 22:43:18 +01:00
|
|
|
#include "tree.h"
|
2003-01-07 17:35:10 +01:00
|
|
|
#include "locks.h"
|
2002-12-14 22:43:18 +01:00
|
|
|
|
|
|
|
#define MAX_TREE_DEPTH 4
|
2003-01-07 17:35:10 +01:00
|
|
|
// Must be a power of 2
|
|
|
|
#define ALLOC_ALIGNMENT 4
|
|
|
|
|
|
|
|
#define MEM_ALIGN(X) FB_ALIGN(X,ALLOC_ALIGNMENT)
|
2002-12-14 22:43:18 +01:00
|
|
|
|
|
|
|
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;
|
2003-01-07 17:35:10 +01:00
|
|
|
#ifdef DEBUG_GDS_ALLOC
|
2002-12-14 22:43:18 +01:00
|
|
|
char *file;
|
|
|
|
int line;
|
2003-01-07 17:35:10 +01:00
|
|
|
#endif
|
2002-12-14 22:43:18 +01:00
|
|
|
};
|
|
|
|
|
2002-12-16 19:33:54 +01:00
|
|
|
#define TYPE_POOL -1
|
|
|
|
#define TYPE_EXTENT -2
|
|
|
|
#define TYPE_LEAFPAGE -3
|
|
|
|
#define TYPE_TREEPAGE -4
|
2002-12-14 22:43:18 +01:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
};
|
|
|
|
|
2003-01-18 22:45:24 +01:00
|
|
|
extern int process_current_memory, process_max_memory;
|
|
|
|
|
2002-12-14 22:43:18 +01:00
|
|
|
// Memory pool based on B+ tree of free memory blocks
|
2003-01-07 17:35:10 +01:00
|
|
|
|
|
|
|
// We are going to have two target architectures:
|
|
|
|
// 1. Multi-process server with customizable lock manager
|
|
|
|
// 2. Multi-threaded server with single process (SUPERSERVER)
|
2003-01-18 22:45:24 +01:00
|
|
|
//
|
|
|
|
// MemoryPool inheritance looks weird because we cannot use
|
|
|
|
// any pointers to functions in shared memory. VMT usage in
|
|
|
|
// MemoryPool and its descendants is prohibited
|
2002-12-14 22:43:18 +01:00
|
|
|
class MemoryPool {
|
|
|
|
private:
|
2003-01-09 20:47:46 +01:00
|
|
|
class InternalAllocator {
|
|
|
|
public:
|
2003-01-16 18:47:10 +01:00
|
|
|
void* allocate(size_t size) {
|
|
|
|
return ((MemoryPool*)this)->tree_alloc(size);
|
2003-01-10 22:37:18 +01:00
|
|
|
}
|
2003-01-16 18:47:10 +01:00
|
|
|
void deallocate(void* block) {
|
|
|
|
((MemoryPool*)this)->tree_free(block);
|
2003-01-10 22:37:18 +01:00
|
|
|
}
|
2003-01-09 20:47:46 +01:00
|
|
|
};
|
|
|
|
typedef BePlusTree<BlockInfo, BlockInfo, InternalAllocator,
|
2002-12-14 22:43:18 +01:00
|
|
|
DefaultKeyValue<BlockInfo>, BlockInfo> FreeBlocksTree;
|
|
|
|
FreeBlocksTree freeBlocks; // B+ tree ordered by (length,address)
|
|
|
|
MemoryExtent *extents; // Linked list of all memory extents
|
|
|
|
|
2003-01-03 17:03:30 +01:00
|
|
|
Vector<void*,2> spareLeafs;
|
|
|
|
Vector<void*,MAX_TREE_DEPTH+1> spareNodes;
|
2002-12-14 22:43:18 +01:00
|
|
|
bool needSpare;
|
|
|
|
PendingFreeBlock *pendingFree;
|
2003-01-07 17:35:10 +01:00
|
|
|
#ifdef SUPERSERVER
|
|
|
|
Spinlock lock;
|
|
|
|
#else
|
2003-01-10 22:37:18 +01:00
|
|
|
SharedSpinlock lock;
|
2003-01-07 17:35:10 +01:00
|
|
|
#endif
|
2003-01-18 22:45:24 +01:00
|
|
|
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
|
2003-01-10 13:27:57 +01:00
|
|
|
|
2003-01-18 19:02:12 +01:00
|
|
|
/* Returns NULL in case it cannot allocate requested chunk */
|
|
|
|
static void* external_alloc(size_t size);
|
|
|
|
|
|
|
|
static void external_free(void *blk);
|
|
|
|
|
2003-01-16 18:47:10 +01:00
|
|
|
void* tree_alloc(size_t size);
|
2003-01-10 13:27:57 +01:00
|
|
|
|
2003-01-16 18:47:10 +01:00
|
|
|
void tree_free(void* block);
|
2003-01-10 13:27:57 +01:00
|
|
|
|
2003-01-03 17:03:30 +01:00
|
|
|
void updateSpare();
|
|
|
|
|
|
|
|
void addFreeBlock(MemoryBlock *blk);
|
|
|
|
|
|
|
|
void removeFreeBlock(MemoryBlock *blk);
|
2003-01-09 20:47:46 +01:00
|
|
|
|
2003-01-18 22:45:24 +01:00
|
|
|
void free_blk_extent(MemoryBlock *blk);
|
|
|
|
|
2003-01-16 18:47:10 +01:00
|
|
|
// does all the stuff except locking and exceptions
|
|
|
|
void* internal_alloc(size_t size, SSHORT type = 0
|
2003-01-09 20:47:46 +01:00
|
|
|
#ifdef DEBUG_GDS_ALLOC
|
|
|
|
, char *file = NULL, int line = 0
|
|
|
|
#endif
|
|
|
|
);
|
2003-01-16 18:47:10 +01:00
|
|
|
protected:
|
2003-01-18 22:45:24 +01:00
|
|
|
int *cur_memory;
|
|
|
|
int *max_memory;
|
2003-01-16 18:47:10 +01:00
|
|
|
// Do not allow to create and destroy pool directly from outside
|
2003-01-18 22:45:24 +01:00
|
|
|
MemoryPool(void *first_extent, void *root_page, int* cur_mem = NULL, int* max_mem = NULL) :
|
2003-01-16 18:47:10 +01:00
|
|
|
freeBlocks((InternalAllocator*)this, root_page),
|
|
|
|
extents((MemoryExtent *)first_extent),
|
|
|
|
needSpare(false),
|
2003-01-18 22:45:24 +01:00
|
|
|
pendingFree(NULL),
|
|
|
|
/*extents_memory(0), - Initialized in internal_create() */
|
|
|
|
used_memory(0),
|
|
|
|
cur_memory(cur_mem),
|
|
|
|
max_memory(max_mem)
|
2003-01-16 18:47:10 +01:00
|
|
|
{
|
|
|
|
}
|
2003-01-09 20:47:46 +01:00
|
|
|
|
2003-01-16 18:47:10 +01:00
|
|
|
// This should never be called
|
|
|
|
~MemoryPool() {
|
|
|
|
}
|
2003-01-07 17:35:10 +01:00
|
|
|
|
2003-01-18 22:45:24 +01:00
|
|
|
static MemoryPool* internal_create(size_t instance_size,
|
|
|
|
int *cur_mem = &process_current_memory, int *max_mem = &process_max_memory);
|
2003-01-16 18:47:10 +01:00
|
|
|
public:
|
2003-01-18 22:45:24 +01:00
|
|
|
// 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;
|
|
|
|
}
|
2003-01-16 18:47:10 +01:00
|
|
|
static MemoryPool* createPool() {
|
|
|
|
return internal_create(sizeof(MemoryPool));
|
|
|
|
}
|
2002-12-14 22:43:18 +01:00
|
|
|
|
|
|
|
static void deletePool(MemoryPool* pool);
|
|
|
|
|
2003-01-16 18:47:10 +01:00
|
|
|
void* allocate(size_t size, SSHORT type = 0
|
2003-01-07 17:35:10 +01:00
|
|
|
#ifdef DEBUG_GDS_ALLOC
|
|
|
|
, char *file = NULL, int line = 0
|
|
|
|
#endif
|
|
|
|
);
|
2002-12-14 22:43:18 +01:00
|
|
|
|
2003-01-16 18:47:10 +01:00
|
|
|
void deallocate(void *block);
|
2002-12-16 19:33:54 +01:00
|
|
|
|
2003-01-16 18:47:10 +01:00
|
|
|
bool verify_pool();
|
2002-12-14 22:43:18 +01:00
|
|
|
|
2003-01-16 18:47:10 +01:00
|
|
|
void print_contents(IB_FILE *, bool = false);
|
2003-01-09 20:47:46 +01:00
|
|
|
|
2003-01-07 17:35:10 +01:00
|
|
|
static void globalFree(void *block) {
|
2003-01-16 18:47:10 +01:00
|
|
|
((MemoryBlock*)((char*)block-MEM_ALIGN(sizeof(MemoryBlock))))->pool->deallocate(block);
|
2003-01-07 17:35:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void* calloc(size_t size, SSHORT type = 0
|
|
|
|
#ifdef DEBUG_GDS_ALLOC
|
|
|
|
, char *file = NULL, int line = 0
|
|
|
|
#endif
|
|
|
|
) {
|
2003-01-16 18:47:10 +01:00
|
|
|
void* result = allocate(size, type
|
2003-01-07 17:35:10 +01:00
|
|
|
#ifdef DEBUG_GDS_ALLOC
|
|
|
|
, file, line
|
|
|
|
#endif
|
|
|
|
);
|
2003-01-16 18:47:10 +01:00
|
|
|
memset(result,0,size);
|
2003-01-09 20:47:46 +01:00
|
|
|
return result;
|
2003-01-07 17:35:10 +01:00
|
|
|
}
|
2003-01-10 13:27:57 +01:00
|
|
|
|
2003-01-16 18:47:10 +01:00
|
|
|
/// 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;
|
|
|
|
}
|
|
|
|
|
2003-01-10 13:27:57 +01:00
|
|
|
friend class InternalAllocator;
|
2002-12-14 22:43:18 +01:00
|
|
|
};
|
|
|
|
|
2003-01-07 17:35:10 +01:00
|
|
|
}; // namespace Firebird
|
|
|
|
|
|
|
|
#ifndef TESTING_ONLY
|
|
|
|
|
2003-01-16 18:47:10 +01:00
|
|
|
using Firebird::MemoryPool;
|
|
|
|
|
|
|
|
MemoryPool* getDefaultMemoryPool();
|
|
|
|
|
2003-01-07 17:35:10 +01:00
|
|
|
extern "C" {
|
|
|
|
#ifdef DEBUG_GDS_ALLOC
|
|
|
|
void* API_ROUTINE gds__alloc_debug(SLONG size_request,
|
|
|
|
TEXT* filename,
|
|
|
|
ULONG lineno);
|
|
|
|
#else
|
|
|
|
void* API_ROUTINE gds__alloc(SLONG size_request);
|
|
|
|
#endif
|
|
|
|
ULONG API_ROUTINE gds__free(void* blk);
|
2002-12-14 22:43:18 +01:00
|
|
|
};
|
|
|
|
|
2003-01-07 17:35:10 +01:00
|
|
|
// Global versions of operator new() for compatibility with crappy libraries
|
|
|
|
void* operator new(size_t);
|
|
|
|
void* operator new[](size_t);
|
|
|
|
|
|
|
|
#ifdef DEBUG_GDS_ALLOC
|
2003-01-16 18:47:10 +01:00
|
|
|
inline void* operator new(size_t s, Firebird::MemoryPool& pool, char* file, int line) {
|
|
|
|
return pool.allocate(s, 0, file, line);
|
|
|
|
// return pool.calloc(s, 0, file, line);
|
2003-01-07 17:35:10 +01:00
|
|
|
}
|
2003-01-16 18:47:10 +01:00
|
|
|
inline void* operator new[](size_t s, Firebird::MemoryPool& pool, char* file, int line) {
|
|
|
|
return pool.allocate(s, 0, file, line);
|
|
|
|
// return pool.calloc(s, 0, file, line);
|
2003-01-07 17:35:10 +01:00
|
|
|
}
|
|
|
|
#define FB_NEW(pool) new(pool,__FILE__,__LINE__)
|
|
|
|
#define FB_NEW_RPT(pool,count) new(pool,count,__FILE__,__LINE__)
|
|
|
|
#else
|
2003-01-16 18:47:10 +01:00
|
|
|
inline void* operator new(size_t s, Firebird::MemoryPool& pool) throw(std::bad_alloc) {
|
|
|
|
return pool.allocate(s);
|
|
|
|
// return pool.calloc(s);
|
2003-01-07 17:35:10 +01:00
|
|
|
}
|
2003-01-16 18:47:10 +01:00
|
|
|
inline void* operator new[](size_t s, Firebird::MemoryPool& pool) throw(std::bad_alloc) {
|
|
|
|
return pool.allocate(s);
|
|
|
|
// return pool.calloc(s);
|
2003-01-07 17:35:10 +01:00
|
|
|
}
|
|
|
|
#define FB_NEW(pool) new(pool)
|
|
|
|
#define FB_NEW_RPT(pool,count) new(pool,count)
|
|
|
|
#endif
|
|
|
|
|
2003-01-16 18:47:10 +01:00
|
|
|
// We cannot use inline versions because we have to replace STL delete defined in <new> 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<int> the declaration would be:
|
|
|
|
|
|
|
|
std::vector<int, MemoryPool::allocator<int> >
|
|
|
|
|
|
|
|
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<int, MemoryPool::allocator<int> > vec(MemoryPool::allocator<int>(poolRef, type));
|
|
|
|
The type is an optional parameter that defaults to 0.
|
|
|
|
**/
|
|
|
|
namespace Firebird
|
|
|
|
{
|
|
|
|
template <class T>
|
|
|
|
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 <class DST>
|
|
|
|
allocator(const allocator<DST> &alloc)
|
|
|
|
: pool(alloc.getPool()), type(alloc.getType()) { }
|
2003-01-07 17:35:10 +01:00
|
|
|
|
2003-01-16 18:47:10 +01:00
|
|
|
#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); }
|
2003-01-07 17:35:10 +01:00
|
|
|
#endif
|
2003-01-16 18:47:10 +01:00
|
|
|
/*#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 <class _Tp1> struct rebind {
|
|
|
|
typedef Firebird::allocator<_Tp1> other;
|
|
|
|
};
|
|
|
|
|
|
|
|
bool operator==(const allocator<T>& 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*/
|
2003-01-07 17:35:10 +01:00
|
|
|
|
2002-12-14 22:43:18 +01:00
|
|
|
#endif
|