mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-24 02:03:03 +01:00
Memory pages manager
This commit is contained in:
parent
9938e3d01b
commit
4b5428f833
@ -32,15 +32,21 @@
|
|||||||
* Contributor(s):
|
* Contributor(s):
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* $Id: alloc.cpp,v 1.38 2004-02-20 06:42:35 robocop Exp $
|
* $Id: alloc.cpp,v 1.39 2004-03-01 03:18:42 skidder Exp $
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "../../include/firebird.h"
|
#include "../../include/firebird.h"
|
||||||
#include "alloc.h"
|
#include "alloc.h"
|
||||||
|
#ifdef HAVE_MMAP
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
// Size in bytes, must be aligned according to ALLOC_ALIGNMENT
|
// Size in bytes, must be aligned according to ALLOC_ALIGNMENT
|
||||||
#define MIN_EXTENT_SIZE 16384
|
// It should also be a multiply of page size
|
||||||
|
const size_t MIN_EXTENT_SIZE = 16384;
|
||||||
|
// We cache this amount of extents to avoid memory mapping overhead
|
||||||
|
const int MAP_CACHE_SIZE = 64;
|
||||||
|
|
||||||
#define FB_MAX(M,N) ((M)>(N)?(M):(N))
|
#define FB_MAX(M,N) ((M)>(N)?(M):(N))
|
||||||
|
|
||||||
@ -48,9 +54,9 @@
|
|||||||
#define ALLOC_PATTERN 0xFEEDABED
|
#define ALLOC_PATTERN 0xFEEDABED
|
||||||
//#define ALLOC_PATTERN 0x0
|
//#define ALLOC_PATTERN 0x0
|
||||||
#ifdef DEBUG_GDS_ALLOC
|
#ifdef DEBUG_GDS_ALLOC
|
||||||
#define PATTERN_FILL(ptr,size,pattern) for (size_t _i=0;_i< size>>2;_i++) ((unsigned int*)(ptr))[_i]=(pattern)
|
# define PATTERN_FILL(ptr,size,pattern) for (size_t _i=0;_i< size>>2;_i++) ((unsigned int*)(ptr))[_i]=(pattern)
|
||||||
#else
|
#else
|
||||||
#define PATTERN_FILL(ptr,size,pattern) ((void)0)
|
# define PATTERN_FILL(ptr,size,pattern) ((void)0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// TODO (in order of importance):
|
// TODO (in order of importance):
|
||||||
@ -121,13 +127,73 @@ void MemoryPool::updateSpare() {
|
|||||||
} while (needSpare);
|
} while (needSpare);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
|
||||||
|
# define MAP_ANONYMOUS MAP_ANON
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(HAVE_MMAP) && !defined(MAP_ANONYMOUS)
|
||||||
|
static int dev_zero_fd = -1; /* Cached file descriptor for /dev/zero. */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(WIN_NT) || defined(HAVE_MMAP)
|
||||||
|
static Firebird::Vector<void*, MAP_CACHE_SIZE> extents_cache;
|
||||||
|
static Mutex cache_mutex;
|
||||||
|
#endif
|
||||||
|
|
||||||
void* MemoryPool::external_alloc(size_t size) {
|
void* MemoryPool::external_alloc(size_t size) {
|
||||||
// This method is assumed to return NULL in case it cannot alloc
|
// This method is assumed to return NULL in case it cannot alloc
|
||||||
|
#if defined(WIN_NT) || defined(HAVE_MMAP)
|
||||||
|
if (size == MIN_EXTENT_SIZE) {
|
||||||
|
cache_mutex.enter();
|
||||||
|
void* result = NULL;
|
||||||
|
if (extents_cache.getCount()) {
|
||||||
|
// Use recently used object object to encourage caching
|
||||||
|
result = extents_cache[extents_cache.getCount()-1];
|
||||||
|
extents_cache.shrink(extents_cache.getCount()-1);
|
||||||
|
}
|
||||||
|
cache_mutex.leave();
|
||||||
|
if (result) return result;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if defined WIN_NT
|
||||||
|
return VirtualAlloc(NULL, size, MEM_COMMIT,
|
||||||
|
PAGE_READWRITE);
|
||||||
|
#elif defined HAVE_MMAP
|
||||||
|
# ifdef MAP_ANONYMOUS
|
||||||
|
return mmap(NULL, size, PROT_READ | PROT_WRITE,
|
||||||
|
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||||
|
# else
|
||||||
|
// This code is needed for Solaris 2.6, AFAIK
|
||||||
|
if (dev_zero_fd < 0) dev_zero_fd = open("/dev/zero", O_RDWR);
|
||||||
|
return mmap(NULL, size, PROT_READ | PROT_WRITE,
|
||||||
|
MAP_PRIVATE | MAP_ANONYMOUS, dev_zero_fd, 0);
|
||||||
|
# endif
|
||||||
|
#else
|
||||||
return malloc(size);
|
return malloc(size);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void MemoryPool::external_free(void *blk) {
|
void MemoryPool::external_free(void *blk, size_t size) {
|
||||||
|
#if defined(WIN_NT) || defined(HAVE_MMAP)
|
||||||
|
if (size == MIN_EXTENT_SIZE) {
|
||||||
|
cache_mutex.enter();
|
||||||
|
if (extents_cache.getCount() < extents_cache.getCapacity()) {
|
||||||
|
extents_cache.add(blk);
|
||||||
|
cache_mutex.leave();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cache_mutex.leave();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if defined WIN_NT
|
||||||
|
if (!VirtualFree(blk, 0, MEM_RELEASE))
|
||||||
|
system_call_failed::raise("VirtualFree");
|
||||||
|
#elif defined HAVE_MMAP
|
||||||
|
if (munmap(blk, size))
|
||||||
|
system_call_failed::raise("munmap");
|
||||||
|
#else
|
||||||
::free(blk);
|
::free(blk);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void* MemoryPool::tree_alloc(size_t size) {
|
void* MemoryPool::tree_alloc(size_t size) {
|
||||||
@ -192,7 +258,7 @@ bool MemoryPool::verify_pool() {
|
|||||||
;
|
;
|
||||||
blk = (MemoryBlock *)((char*)blk+MEM_ALIGN(sizeof(MemoryBlock))+blk->length))
|
blk = (MemoryBlock *)((char*)blk+MEM_ALIGN(sizeof(MemoryBlock))+blk->length))
|
||||||
{
|
{
|
||||||
#ifndef NDEBUG
|
# ifndef NDEBUG
|
||||||
fb_assert(blk->pool == this); // Pool is correct ?
|
fb_assert(blk->pool == this); // Pool is correct ?
|
||||||
fb_assert(blk->prev == prev); // Prev is correct ?
|
fb_assert(blk->prev == prev); // Prev is correct ?
|
||||||
BlockInfo temp = {blk, blk->length};
|
BlockInfo temp = {blk, blk->length};
|
||||||
@ -210,7 +276,7 @@ bool MemoryPool::verify_pool() {
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
fb_assert(!foundTree && !foundPending); // Block is not free. Should not be in free lists
|
fb_assert(!foundTree && !foundPending); // Block is not free. Should not be in free lists
|
||||||
#endif
|
# endif
|
||||||
prev = blk;
|
prev = blk;
|
||||||
if (blk->last) break;
|
if (blk->last) break;
|
||||||
}
|
}
|
||||||
@ -277,6 +343,7 @@ MemoryPool* MemoryPool::internal_create(size_t instance_size, int *cur_mem, int
|
|||||||
char* mem = (char *)external_alloc(alloc_size);
|
char* mem = (char *)external_alloc(alloc_size);
|
||||||
if (!mem) pool_out_of_memory();
|
if (!mem) pool_out_of_memory();
|
||||||
((MemoryExtent *)mem)->next = NULL;
|
((MemoryExtent *)mem)->next = NULL;
|
||||||
|
((MemoryExtent *)mem)->extent_size = alloc_size;
|
||||||
MemoryPool* pool = new(mem +
|
MemoryPool* pool = new(mem +
|
||||||
MEM_ALIGN(sizeof(MemoryExtent)) +
|
MEM_ALIGN(sizeof(MemoryExtent)) +
|
||||||
MEM_ALIGN(sizeof(MemoryBlock)))
|
MEM_ALIGN(sizeof(MemoryBlock)))
|
||||||
@ -335,17 +402,13 @@ MemoryPool* MemoryPool::internal_create(size_t instance_size, int *cur_mem, int
|
|||||||
void MemoryPool::deletePool(MemoryPool* pool) {
|
void MemoryPool::deletePool(MemoryPool* pool) {
|
||||||
/* dimitr: I think we need an abstract base class or a global macro
|
/* dimitr: I think we need an abstract base class or a global macro
|
||||||
in locks.h to avoid these architecture checks. */
|
in locks.h to avoid these architecture checks. */
|
||||||
#ifdef MULTI_THREAD
|
pool->lock.~Mutex();
|
||||||
pool->lock.~Spinlock();
|
|
||||||
#else
|
|
||||||
pool->lock.~SharedSpinlock();
|
|
||||||
#endif
|
|
||||||
if (pool->cur_memory) *pool->cur_memory -= pool->used_memory;
|
if (pool->cur_memory) *pool->cur_memory -= pool->used_memory;
|
||||||
// Delete all extents now
|
// Delete all extents now
|
||||||
MemoryExtent *temp = pool->extents;
|
MemoryExtent *temp = pool->extents;
|
||||||
while (temp) {
|
while (temp) {
|
||||||
MemoryExtent *next = temp->next;
|
MemoryExtent *next = temp->next;
|
||||||
external_free(temp);
|
external_free(temp, temp->extent_size);
|
||||||
temp = next;
|
temp = next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -483,6 +546,7 @@ void* MemoryPool::internal_alloc(size_t size, SSHORT type
|
|||||||
}
|
}
|
||||||
extents_memory += alloc_size - MEM_ALIGN(sizeof(MemoryExtent));
|
extents_memory += alloc_size - MEM_ALIGN(sizeof(MemoryExtent));
|
||||||
extent->next = extents;
|
extent->next = extents;
|
||||||
|
extent->extent_size = alloc_size;
|
||||||
extents = extent;
|
extents = extent;
|
||||||
|
|
||||||
blk = (MemoryBlock *)((char*)extent+MEM_ALIGN(sizeof(MemoryExtent)));
|
blk = (MemoryBlock *)((char*)extent+MEM_ALIGN(sizeof(MemoryExtent)));
|
||||||
@ -583,7 +647,7 @@ void MemoryPool::free_blk_extent(MemoryBlock *blk) {
|
|||||||
fb_assert(itr); // We had to find it somewhere
|
fb_assert(itr); // We had to find it somewhere
|
||||||
}
|
}
|
||||||
extents_memory -= blk->length + MEM_ALIGN(sizeof(MemoryBlock));
|
extents_memory -= blk->length + MEM_ALIGN(sizeof(MemoryBlock));
|
||||||
external_free(extent);
|
external_free(extent, extent->extent_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MemoryPool::deallocate(void *block) {
|
void MemoryPool::deallocate(void *block) {
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
* Contributor(s):
|
* Contributor(s):
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* $Id: alloc.h,v 1.32 2004-02-20 06:42:35 robocop Exp $
|
* $Id: alloc.h,v 1.33 2004-03-01 03:18:42 skidder Exp $
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -98,6 +98,7 @@ struct BlockInfo {
|
|||||||
|
|
||||||
struct MemoryExtent {
|
struct MemoryExtent {
|
||||||
MemoryExtent *next;
|
MemoryExtent *next;
|
||||||
|
size_t extent_size; // Includes extent header size
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PendingFreeBlock {
|
struct PendingFreeBlock {
|
||||||
@ -133,18 +134,14 @@ private:
|
|||||||
Vector<void*, MAX_TREE_DEPTH + 1> spareNodes;
|
Vector<void*, MAX_TREE_DEPTH + 1> spareNodes;
|
||||||
bool needSpare;
|
bool needSpare;
|
||||||
PendingFreeBlock *pendingFree;
|
PendingFreeBlock *pendingFree;
|
||||||
#ifdef MULTI_THREAD
|
Mutex lock;
|
||||||
Spinlock lock;
|
|
||||||
#else
|
|
||||||
SharedSpinlock lock;
|
|
||||||
#endif
|
|
||||||
int extents_memory; // Sum of memory in allocated extents minus size of extents headers
|
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
|
int used_memory; // Size of used memory blocks including block headers
|
||||||
|
|
||||||
/* Returns NULL in case it cannot allocate requested chunk */
|
/* Returns NULL in case it cannot allocate requested chunk */
|
||||||
static void* external_alloc(size_t size);
|
static void* external_alloc(size_t size);
|
||||||
|
|
||||||
static void external_free(void* blk);
|
static void external_free(void* blk, size_t size);
|
||||||
|
|
||||||
void* tree_alloc(size_t size);
|
void* tree_alloc(size_t size);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user