mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-22 16:43:03 +01:00
Introduce the external memory pool.
Standard new operator will alloc memory from the external pool. FB_NEW will alloc memory from the default pool. The difference of the external pool to the default pool is that the external pool is only freed during unload when there is no memory allocated from it. If the external pool destructor is called before objects that allocated memory from it, it's placed in special DYING state. When in DYING state its desallocation is deferred to the moment the last memory allocated is freed from it.
This commit is contained in:
parent
21307a7bf8
commit
ab42db972a
@ -21,7 +21,7 @@
|
||||
# Notes:
|
||||
|
||||
# Firebird requires the HP-UX Atomic APIs ("AtomicAPI" bundle), released as an
|
||||
# optional Software Pack (SPK) for HP-UX 11i v2 or v3.
|
||||
# optional Software Pack (SPK) for HP-UX 11i v2 or v3.
|
||||
|
||||
# Before attempting the HPUX build, you must export necessary
|
||||
# CFLAGS and CXXFLAGS to be picked up by the configure process and
|
||||
@ -60,15 +60,12 @@ COMMON_FLAGS= -DHP11 -DHPUX -D_XOPEN_SOURCE_EXTENDED -D_HP_ATOMIC_INLINE \
|
||||
|
||||
# Flags specific to production or debug build...
|
||||
# -z, disallow dereferencing of null pointers at runtime
|
||||
# -DLIBC_CALLS_NEW, Firebird macro, used when C++ runtime can call
|
||||
# redefined by us operator new before initialization of global variables.
|
||||
# Required on IA-64, but evidently only by debug build!?
|
||||
|
||||
ifeq ($(shell uname -m),ia64)
|
||||
# ...for IA-64
|
||||
PROD_FLAGS= +O2 +Onolimit +Ofltacc=strict +Ofenvaccess \
|
||||
$(COMMON_FLAGS)
|
||||
DEV_FLAGS= -g -z -DLIBC_CALLS_NEW \
|
||||
DEV_FLAGS= -g -z \
|
||||
$(COMMON_FLAGS)
|
||||
else
|
||||
# ...for PA-RISC
|
||||
|
@ -40,12 +40,22 @@
|
||||
#include "firebird.h"
|
||||
#include "../common/classes/alloc.h"
|
||||
|
||||
#ifndef WIN_NT
|
||||
#ifdef WIN_NT
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX MAX_PATH
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#endif
|
||||
|
||||
#include "../common/classes/fb_tls.h"
|
||||
@ -68,25 +78,10 @@
|
||||
#define VALGRIND_FIX_IT // overrides suspicious valgrind behavior
|
||||
#endif // USE_VALGRIND
|
||||
|
||||
void* operator new(size_t s ALLOC_PARAMS)
|
||||
{
|
||||
return MemoryPool::globalAlloc(s ALLOC_PASS_ARGS);
|
||||
}
|
||||
void* operator new[](size_t s ALLOC_PARAMS)
|
||||
{
|
||||
return MemoryPool::globalAlloc(s ALLOC_PASS_ARGS);
|
||||
}
|
||||
void operator delete(void* mem ALLOC_PARAMS) noexcept
|
||||
{
|
||||
MemoryPool::globalFree(mem);
|
||||
}
|
||||
void operator delete[](void* mem ALLOC_PARAMS) noexcept
|
||||
{
|
||||
MemoryPool::globalFree(mem);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
///#define MEM_DEBUG_EXTERNAL
|
||||
|
||||
/*** emergency debugging stuff
|
||||
static const char* lastFileName;
|
||||
static int lastLine;
|
||||
@ -147,7 +142,14 @@ typedef Firebird::AtomicCounter::counter_type StatInt;
|
||||
const int MAP_CACHE_SIZE = 16; // == 1 MB
|
||||
const size_t DEFAULT_ALLOCATION = 65536;
|
||||
|
||||
Firebird::Vector<void*, MAP_CACHE_SIZE> extents_cache;
|
||||
struct ExtentsCache // C++ aggregate - members are statically initialized to zeros
|
||||
{
|
||||
unsigned count;
|
||||
void* data[MAP_CACHE_SIZE];
|
||||
};
|
||||
|
||||
ExtentsCache defaultExtentsCache;
|
||||
ExtentsCache externalExtentsCache;
|
||||
|
||||
#ifndef WIN_NT
|
||||
struct FailedBlock
|
||||
@ -191,7 +193,7 @@ inline size_t get_map_page_size()
|
||||
static volatile size_t map_page_size = 0;
|
||||
if (!map_page_size)
|
||||
{
|
||||
Firebird::MutexLockGuard guard(*cache_mutex, "get_map_page_size");
|
||||
Firebird::MutexLockGuard guard(cache_mutex, "get_map_page_size");
|
||||
if (!map_page_size)
|
||||
map_page_size = get_page_size();
|
||||
}
|
||||
@ -1731,10 +1733,22 @@ private:
|
||||
public:
|
||||
static MemPool* defaultMemPool;
|
||||
|
||||
MemPool();
|
||||
MemPool(MemPool& parent, MemoryStats& stats);
|
||||
MemPool(MemoryStats& stats, ExtentsCache* extentsCache);
|
||||
MemPool(MemPool& parent, MemoryStats& stats, ExtentsCache* extentsCache);
|
||||
virtual ~MemPool(void);
|
||||
|
||||
public:
|
||||
static inline constexpr MemPool* getPoolFromPointer(void* ptr) noexcept
|
||||
{
|
||||
if (ptr)
|
||||
{
|
||||
auto block = (MemBlock*) ((UCHAR*) ptr - offsetof(MemBlock, body));
|
||||
return block->pool;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
static const size_t minAllocation = 65536;
|
||||
static const size_t roundingSize = ALLOC_ALIGNMENT;
|
||||
@ -1749,12 +1763,10 @@ private:
|
||||
int blocksActive;
|
||||
bool pool_destroying, parent_redirect;
|
||||
|
||||
// Statistics group for the pool
|
||||
MemoryStats* stats;
|
||||
// Parent pool if present
|
||||
MemPool* parent;
|
||||
// Memory used
|
||||
AtomicCounter used_memory, mapped_memory;
|
||||
MemoryStats* stats; // Statistics group for the pool
|
||||
MemPool* parent; // Parent pool if present
|
||||
ExtentsCache* extentsCache;
|
||||
AtomicCounter used_memory, mapped_memory; // Memory used
|
||||
|
||||
private:
|
||||
|
||||
@ -1810,7 +1822,7 @@ private:
|
||||
virtual void memoryIsExhausted(void);
|
||||
void* allocRaw(size_t length);
|
||||
static void releaseMemory(void* block, bool flagExtent) noexcept;
|
||||
static void releaseRaw(bool destroying, void *block, size_t size, bool use_cache = true) noexcept;
|
||||
static void releaseRaw(bool destroying, void *block, size_t size, ExtentsCache* extentsCache) noexcept;
|
||||
void* getExtent(size_t from, size_t& to);
|
||||
|
||||
public:
|
||||
@ -1843,23 +1855,36 @@ public:
|
||||
void setStatsGroup(MemoryStats& stats) noexcept;
|
||||
|
||||
// Initialize and finalize global memory pool
|
||||
static MemPool* init()
|
||||
static MemPool* initDefaultPool()
|
||||
{
|
||||
fb_assert(!defaultMemPool);
|
||||
|
||||
static char mpBuffer[sizeof(MemPool) + ALLOC_ALIGNMENT];
|
||||
defaultMemPool = new((void*)(IPTR) MEM_ALIGN((size_t)(IPTR) mpBuffer)) MemPool();
|
||||
alignas(alignof(MemPool)) static char mpBuffer[sizeof(MemPool)];
|
||||
defaultMemPool = new(mpBuffer) MemPool(*MemoryPool::default_stats_group, &defaultExtentsCache);
|
||||
return defaultMemPool;
|
||||
}
|
||||
|
||||
static void cleanup()
|
||||
static void cleanupDefaultPool()
|
||||
{
|
||||
defaultMemPool->~MemPool();
|
||||
defaultMemPool = NULL;
|
||||
|
||||
while (extents_cache.getCount())
|
||||
releaseRaw(true, extents_cache.pop(), DEFAULT_ALLOCATION, false);
|
||||
while (defaultExtentsCache.count)
|
||||
releaseRaw(true, defaultExtentsCache.data[--defaultExtentsCache.count], DEFAULT_ALLOCATION, nullptr);
|
||||
|
||||
cleanupFailedList();
|
||||
}
|
||||
|
||||
static void cleanupExternalPool()
|
||||
{
|
||||
while (externalExtentsCache.count)
|
||||
releaseRaw(true, externalExtentsCache.data[--externalExtentsCache.count], DEFAULT_ALLOCATION, nullptr);
|
||||
|
||||
cleanupFailedList();
|
||||
}
|
||||
|
||||
static void cleanupFailedList()
|
||||
{
|
||||
#ifndef WIN_NT
|
||||
unsigned oldCount = 0;
|
||||
|
||||
@ -1880,7 +1905,7 @@ public:
|
||||
++newCount;
|
||||
FailedBlock* fb = oldList;
|
||||
SemiDoubleLink::pop(oldList);
|
||||
releaseRaw(true, fb, fb->blockSize, false);
|
||||
releaseRaw(true, fb, fb->blockSize, nullptr);
|
||||
}
|
||||
|
||||
if (newCount == oldCount)
|
||||
@ -1889,7 +1914,6 @@ public:
|
||||
oldCount = newCount;
|
||||
}
|
||||
#endif // WIN_NT
|
||||
|
||||
}
|
||||
|
||||
// Statistics
|
||||
@ -2012,28 +2036,28 @@ GlobalPtr<Mutex> forceCreationOfDefaultMemoryPool;
|
||||
|
||||
MemoryPool* MemoryPool::defaultMemoryManager = NULL;
|
||||
MemoryStats* MemoryPool::default_stats_group = NULL;
|
||||
MemoryPool* MemoryPool::externalMemoryManager = NULL;
|
||||
MemPool* MemPool::defaultMemPool = NULL;
|
||||
|
||||
|
||||
// Initialize process memory pool (called from InstanceControl).
|
||||
|
||||
void MemoryPool::init()
|
||||
void MemoryPool::initDefaultPool()
|
||||
{
|
||||
static char mtxBuffer[sizeof(Mutex) + ALLOC_ALIGNMENT];
|
||||
cache_mutex = new((void*)(IPTR) MEM_ALIGN((size_t)(IPTR) mtxBuffer)) Mutex;
|
||||
alignas(alignof(Mutex)) static char mtxBuffer[sizeof(Mutex)];
|
||||
cache_mutex = new(mtxBuffer) Mutex;
|
||||
|
||||
static char msBuffer[sizeof(MemoryStats) + ALLOC_ALIGNMENT];
|
||||
default_stats_group =
|
||||
new((void*)(IPTR) MEM_ALIGN((size_t)(IPTR) msBuffer)) MemoryStats;
|
||||
alignas(alignof(MemoryStats)) static char msBuffer[sizeof(MemoryStats)];
|
||||
default_stats_group = new(msBuffer) MemoryStats;
|
||||
|
||||
static char mpBuffer[sizeof(MemoryPool) + ALLOC_ALIGNMENT];
|
||||
defaultMemoryManager = new((void*)(IPTR) MEM_ALIGN((size_t)(IPTR) mpBuffer)) MemoryPool(MemPool::init());
|
||||
alignas(alignof(MemoryPool)) static char mpBuffer[sizeof(MemoryPool)];
|
||||
defaultMemoryManager = new(mpBuffer) MemoryPool(MemPool::initDefaultPool());
|
||||
}
|
||||
|
||||
// Should be last routine, called by InstanceControl,
|
||||
// being therefore the very last routine in firebird module.
|
||||
|
||||
void MemoryPool::cleanup()
|
||||
void MemoryPool::cleanupDefaultPool()
|
||||
{
|
||||
#ifdef VALGRIND_FIX_IT
|
||||
VALGRIND_DISCARD(
|
||||
@ -2047,7 +2071,7 @@ void MemoryPool::cleanup()
|
||||
if (defaultMemoryManager)
|
||||
{
|
||||
//defaultMemoryManager->~MemoryPool();
|
||||
MemPool::cleanup();
|
||||
MemPool::cleanupDefaultPool();
|
||||
defaultMemoryManager = NULL;
|
||||
}
|
||||
|
||||
@ -2065,15 +2089,23 @@ void MemoryPool::cleanup()
|
||||
}
|
||||
|
||||
|
||||
MemPool::MemPool()
|
||||
: pool_destroying(false), parent_redirect(false), stats(MemoryPool::default_stats_group), parent(NULL)
|
||||
MemPool::MemPool(MemoryStats& s, ExtentsCache* cache)
|
||||
: pool_destroying(false),
|
||||
parent_redirect(false),
|
||||
stats(&s),
|
||||
parent(NULL),
|
||||
extentsCache(cache)
|
||||
{
|
||||
fb_assert(offsetof(MemBlock, body) == MEM_ALIGN(offsetof(MemBlock, body)));
|
||||
initialize();
|
||||
}
|
||||
|
||||
MemPool::MemPool(MemPool& p, MemoryStats& s)
|
||||
: pool_destroying(false), parent_redirect(true), stats(&s), parent(&p)
|
||||
MemPool::MemPool(MemPool& p, MemoryStats& s, ExtentsCache* cache)
|
||||
: pool_destroying(false),
|
||||
parent_redirect(true),
|
||||
stats(&s),
|
||||
parent(&p),
|
||||
extentsCache(cache)
|
||||
{
|
||||
initialize();
|
||||
}
|
||||
@ -2133,7 +2165,7 @@ MemPool::~MemPool(void)
|
||||
{
|
||||
MemBigHunk* hunk = bigHunks;
|
||||
bigHunks = hunk->next;
|
||||
releaseRaw(pool_destroying, hunk, hunk->length);
|
||||
releaseRaw(pool_destroying, hunk, hunk->length, extentsCache);
|
||||
}
|
||||
|
||||
if (parent)
|
||||
@ -2210,7 +2242,7 @@ MemoryPool* MemoryPool::createPool(MemoryPool* parentPool, MemoryStats& stats)
|
||||
if (!parentPool)
|
||||
parentPool = getDefaultMemoryPool();
|
||||
|
||||
MemPool* p = FB_NEW_POOL(*parentPool) MemPool(*(parentPool->pool), stats);
|
||||
MemPool* p = FB_NEW_POOL(*parentPool) MemPool(*(parentPool->pool), stats, &defaultExtentsCache);
|
||||
return FB_NEW_POOL(*parentPool) MemoryPool(p);
|
||||
}
|
||||
|
||||
@ -2461,7 +2493,7 @@ void MemPool::releaseBlock(MemBlock* block, bool decrUsage) noexcept
|
||||
MemBigHunk* hunk = (MemBigHunk*)(((UCHAR*)block) - MemBigHunk::hdrSize());
|
||||
SemiDoubleLink::remove(hunk);
|
||||
decrement_mapping(FB_ALIGN(hunk->length, get_map_page_size()));
|
||||
releaseRaw(pool_destroying, hunk, hunk->length, false);
|
||||
releaseRaw(pool_destroying, hunk, hunk->length, nullptr);
|
||||
}
|
||||
|
||||
void MemPool::memoryIsExhausted(void)
|
||||
@ -2474,12 +2506,12 @@ void* MemPool::allocRaw(size_t size)
|
||||
#ifndef USE_VALGRIND
|
||||
if (size == DEFAULT_ALLOCATION)
|
||||
{
|
||||
MutexLockGuard guard(*cache_mutex, "MemPool::allocRaw");
|
||||
if (extents_cache.hasData())
|
||||
MutexLockGuard guard(cache_mutex, "MemPool::allocRaw");
|
||||
if (extentsCache->count)
|
||||
{
|
||||
// Use most recently used object to encourage caching
|
||||
increment_mapping(size);
|
||||
return extents_cache.pop();
|
||||
return extentsCache->data[--extentsCache->count];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -2497,7 +2529,7 @@ void* MemPool::allocRaw(size_t size)
|
||||
void* result = NULL;
|
||||
if (failedList)
|
||||
{
|
||||
MutexLockGuard guard(*cache_mutex, "MemPool::allocRaw");
|
||||
MutexLockGuard guard(cache_mutex, "MemPool::allocRaw");
|
||||
for (FailedBlock* fb = failedList; fb; fb = fb->next)
|
||||
{
|
||||
if (fb->blockSize == size)
|
||||
@ -2568,20 +2600,20 @@ void MemPool::releaseExtent(bool destroying, void* block, size_t size, MemPool*
|
||||
{
|
||||
if (pool)
|
||||
pool->decrement_mapping(size);
|
||||
releaseRaw(true, block, size, pool);
|
||||
releaseRaw(true, block, size, (pool ? pool->extentsCache : nullptr));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MemPool::releaseRaw(bool destroying, void* block, size_t size, bool use_cache) noexcept
|
||||
void MemPool::releaseRaw(bool destroying, void* block, size_t size, ExtentsCache* extentsCache) noexcept
|
||||
{
|
||||
#ifndef USE_VALGRIND
|
||||
if (use_cache && (size == DEFAULT_ALLOCATION))
|
||||
if (extentsCache && (size == DEFAULT_ALLOCATION))
|
||||
{
|
||||
MutexLockGuard guard(*cache_mutex, "MemPool::releaseRaw");
|
||||
if (extents_cache.getCount() < extents_cache.getCapacity())
|
||||
MutexLockGuard guard(cache_mutex, "MemPool::releaseRaw");
|
||||
if (extentsCache->count < MAP_CACHE_SIZE)
|
||||
{
|
||||
extents_cache.push(block);
|
||||
extentsCache->data[extentsCache->count++] = block;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -2599,7 +2631,7 @@ void MemPool::releaseRaw(bool destroying, void* block, size_t size, bool use_cac
|
||||
if (destroying)
|
||||
{
|
||||
// Synchronize delayed free queue using extents mutex
|
||||
MutexLockGuard guard(*cache_mutex, "MemPool::releaseRaw");
|
||||
MutexLockGuard guard(cache_mutex, "MemPool::releaseRaw");
|
||||
|
||||
// Extend circular buffer if possible
|
||||
if (delayedExtentCount < FB_NELEM(delayedExtents))
|
||||
@ -2656,7 +2688,7 @@ void MemPool::releaseRaw(bool destroying, void* block, size_t size, bool use_cac
|
||||
FailedBlock* failed = (FailedBlock*) block;
|
||||
failed->blockSize = size;
|
||||
|
||||
MutexLockGuard guard(*cache_mutex, "MemPool::releaseRaw");
|
||||
MutexLockGuard guard(cache_mutex, "MemPool::releaseRaw");
|
||||
SemiDoubleLink::push(&failedList, failed);
|
||||
|
||||
return;
|
||||
@ -2817,25 +2849,6 @@ MemoryPool& AutoStorage::getAutoMemoryPool()
|
||||
return *p;
|
||||
}
|
||||
|
||||
#ifdef LIBC_CALLS_NEW
|
||||
void* MemoryPool::globalAlloc(size_t s ALLOC_PARAMS)
|
||||
{
|
||||
if (!defaultMemoryManager)
|
||||
{
|
||||
// this will do all required init, including processMemoryPool creation
|
||||
static Firebird::InstanceControl dummy;
|
||||
fb_assert(defaultMemoryManager);
|
||||
}
|
||||
|
||||
return defaultMemoryManager->allocate(s ALLOC_PASS_ARGS);
|
||||
}
|
||||
#endif // LIBC_CALLS_NEW
|
||||
|
||||
void MemoryPool::globalFree(void* block) noexcept
|
||||
{
|
||||
MemPool::globalFree(block);
|
||||
}
|
||||
|
||||
void* MemoryPool::allocate(size_t size ALLOC_PARAMS)
|
||||
{
|
||||
return pool->allocate(size ALLOC_PASS_ARGS);
|
||||
@ -2924,6 +2937,159 @@ void MemoryPool::unregisterFinalizer(Finalizer*& finalizer)
|
||||
}
|
||||
|
||||
|
||||
class ExternalMemoryHandler
|
||||
{
|
||||
friend class MemoryPool;
|
||||
|
||||
private:
|
||||
struct Objects
|
||||
{
|
||||
MemoryStats memoryStats;
|
||||
MemPool memPool{memoryStats, &externalExtentsCache};
|
||||
MemoryPool memoryPool{&memPool};
|
||||
};
|
||||
|
||||
static ExternalMemoryHandler* instance;
|
||||
|
||||
public:
|
||||
enum class State : UCHAR
|
||||
{
|
||||
ALIVE,
|
||||
DEAD,
|
||||
DYING
|
||||
};
|
||||
|
||||
public:
|
||||
ExternalMemoryHandler()
|
||||
{
|
||||
Mutex::initMutexes();
|
||||
|
||||
#ifdef MEM_DEBUG_EXTERNAL
|
||||
printf("ExternalMemoryHandler::ExternalMemoryHandler()\n");
|
||||
#endif
|
||||
|
||||
instance = this;
|
||||
|
||||
new(objectsBuffer) Objects();
|
||||
|
||||
MemoryPool::externalMemoryManager = &objects().memoryPool;
|
||||
|
||||
atexit([] {
|
||||
const auto currentUsage = instance->objects().memoryStats.getCurrentUsage();
|
||||
|
||||
#ifdef MEM_DEBUG_EXTERNAL
|
||||
printf("ExternalMemoryHandler atexit: %" SIZEFORMAT "\n", currentUsage);
|
||||
#endif
|
||||
|
||||
ExternalMemoryHandler::printContents("atexit");
|
||||
|
||||
if (currentUsage == 0)
|
||||
ExternalMemoryHandler::free();
|
||||
else
|
||||
instance->state = State::DYING;
|
||||
});
|
||||
}
|
||||
|
||||
void revive()
|
||||
{
|
||||
#ifdef MEM_DEBUG_EXTERNAL
|
||||
printf("ExternalMemoryHandler::revive()\n");
|
||||
#endif
|
||||
new(this) ExternalMemoryHandler();
|
||||
}
|
||||
|
||||
static void printContents(const char* moment)
|
||||
{
|
||||
#if defined(MEM_DEBUG) && defined(DEBUG_GDS_ALLOC)
|
||||
if (!MemoryPool::externalMemoryManager)
|
||||
return;
|
||||
|
||||
static bool alreadyPrinted = false;
|
||||
Firebird::AutoPtr<FILE> file;
|
||||
|
||||
{ // scope
|
||||
char name[PATH_MAX];
|
||||
|
||||
if (os_utils::getCurrentModulePath(name, sizeof(name)))
|
||||
strncat(name, ".memdebug.external.log", sizeof(name) - 1);
|
||||
else
|
||||
strcpy(name, "memdebug.external.log");
|
||||
|
||||
file = os_utils::fopen(name, alreadyPrinted ? "at" : "w+t");
|
||||
}
|
||||
|
||||
if (file)
|
||||
{
|
||||
fprintf(file, "********* Moment: %s\n", moment);
|
||||
|
||||
MemoryPool::externalMemoryManager->print_contents(file,
|
||||
Firebird::MemoryPool::PRINT_USED_ONLY | Firebird::MemoryPool::PRINT_RECURSIVE);
|
||||
file = NULL;
|
||||
alreadyPrinted = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void free()
|
||||
{
|
||||
if (instance->state != State::DEAD)
|
||||
{
|
||||
instance->state = State::DEAD;
|
||||
instance->objects().~Objects();
|
||||
instance->~ExternalMemoryHandler();
|
||||
instance = nullptr;
|
||||
|
||||
MemPool::cleanupExternalPool();
|
||||
}
|
||||
|
||||
MemoryPool::externalMemoryManager = nullptr;
|
||||
}
|
||||
|
||||
inline Objects& objects()
|
||||
{
|
||||
return *(Objects*) objectsBuffer;
|
||||
}
|
||||
|
||||
alignas(alignof(Objects)) char objectsBuffer[sizeof(Objects)];
|
||||
State state = State::ALIVE;
|
||||
};
|
||||
|
||||
ExternalMemoryHandler* ExternalMemoryHandler::instance = nullptr;
|
||||
|
||||
void initExternalMemoryPool()
|
||||
{
|
||||
static ExternalMemoryHandler handler;
|
||||
|
||||
if (handler.state == ExternalMemoryHandler::State::DEAD)
|
||||
handler.revive();
|
||||
}
|
||||
|
||||
|
||||
void MemoryPool::globalFree(void* block) noexcept
|
||||
{
|
||||
auto pool = MemPool::getPoolFromPointer(block);
|
||||
|
||||
MemPool::globalFree(block);
|
||||
|
||||
if (auto externalMemoryHandler = ExternalMemoryHandler::instance;
|
||||
externalMemoryHandler &&
|
||||
externalMemoryHandler->state == ExternalMemoryHandler::State::DYING &&
|
||||
pool == &externalMemoryHandler->objects().memPool)
|
||||
{
|
||||
const auto currentUsage = externalMemoryHandler->objects().memoryStats.getCurrentUsage();
|
||||
|
||||
#ifdef MEM_DEBUG_EXTERNAL
|
||||
printf("MemoryPool::globalFree() - dying ExternalMemoryHandler: %" SIZEFORMAT "\n", currentUsage);
|
||||
#endif
|
||||
|
||||
ExternalMemoryHandler::printContents("globalFree");
|
||||
|
||||
if (currentUsage == 0)
|
||||
ExternalMemoryHandler::free();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if defined(DEV_BUILD)
|
||||
void AutoStorage::ProbeStack() const
|
||||
{
|
||||
@ -2949,14 +3115,15 @@ void AutoStorage::ProbeStack() const
|
||||
// Global operator "delete" is always redefined by firebird,
|
||||
// in a case when we actually need "new" only with file/line information
|
||||
// this version should be also present as a pair for "delete".
|
||||
#ifdef DEBUG_GDS_ALLOC
|
||||
|
||||
void* operator new(size_t s)
|
||||
{
|
||||
return MemoryPool::globalAlloc(s ALLOC_ARGS);
|
||||
return getExternalMemoryPool()->allocate(s ALLOC_ARGS);
|
||||
}
|
||||
|
||||
void* operator new[](size_t s)
|
||||
{
|
||||
return MemoryPool::globalAlloc(s ALLOC_ARGS);
|
||||
return getExternalMemoryPool()->allocate(s ALLOC_ARGS);
|
||||
}
|
||||
|
||||
void operator delete(void* mem) noexcept
|
||||
@ -2968,5 +3135,3 @@ void operator delete[](void* mem) noexcept
|
||||
{
|
||||
MemoryPool::globalFree(mem);
|
||||
}
|
||||
|
||||
#endif // DEBUG_GDS_ALLOC
|
||||
|
@ -59,11 +59,11 @@
|
||||
#include <memory.h>
|
||||
|
||||
#ifdef DEBUG_GDS_ALLOC
|
||||
#define FB_NEW new(__FILE__, __LINE__)
|
||||
#define FB_NEW new(*getDefaultMemoryPool(), __FILE__, __LINE__)
|
||||
#define FB_NEW_POOL(pool) new(pool, __FILE__, __LINE__)
|
||||
#define FB_NEW_RPT(pool, count) new(pool, count, __FILE__, __LINE__)
|
||||
#else // DEBUG_GDS_ALLOC
|
||||
#define FB_NEW new
|
||||
#define FB_NEW new(*getDefaultMemoryPool())
|
||||
#define FB_NEW_POOL(pool) new(pool)
|
||||
#define FB_NEW_RPT(pool, count) new(pool, count)
|
||||
#endif // DEBUG_GDS_ALLOC
|
||||
@ -159,6 +159,8 @@ private:
|
||||
|
||||
class MemoryPool
|
||||
{
|
||||
friend class ExternalMemoryHandler;
|
||||
|
||||
private:
|
||||
MemPool* pool;
|
||||
|
||||
@ -174,6 +176,7 @@ public:
|
||||
enum RecommendedBufferSize { MAX_MEDIUM_BLOCK_SIZE = 64384 }; // MediumLimits::TOP_LIMIT - 128
|
||||
|
||||
static MemoryPool* defaultMemoryManager;
|
||||
static MemoryPool* externalMemoryManager;
|
||||
|
||||
public:
|
||||
// Create memory pool instance
|
||||
@ -193,14 +196,10 @@ public:
|
||||
|
||||
void* calloc(size_t size ALLOC_PARAMS);
|
||||
|
||||
#ifdef LIBC_CALLS_NEW
|
||||
static void* globalAlloc(size_t s ALLOC_PARAMS);
|
||||
#else
|
||||
static void* globalAlloc(size_t s ALLOC_PARAMS)
|
||||
{
|
||||
return defaultMemoryManager->allocate(s ALLOC_PASS_ARGS);
|
||||
}
|
||||
#endif // LIBC_CALLS_NEW
|
||||
|
||||
void* allocate(size_t size ALLOC_PARAMS);
|
||||
|
||||
@ -218,8 +217,8 @@ public:
|
||||
void setStatsGroup(MemoryStats& stats) noexcept;
|
||||
|
||||
// Initialize and finalize global memory pool
|
||||
static void init();
|
||||
static void cleanup();
|
||||
static void initDefaultPool();
|
||||
static void cleanupDefaultPool();
|
||||
|
||||
// Initialize context pool
|
||||
static void contextPoolInit();
|
||||
@ -282,6 +281,8 @@ private:
|
||||
friend class MemPool;
|
||||
};
|
||||
|
||||
void initExternalMemoryPool();
|
||||
|
||||
} // namespace Firebird
|
||||
|
||||
static inline Firebird::MemoryPool* getDefaultMemoryPool() noexcept
|
||||
@ -290,6 +291,16 @@ static inline Firebird::MemoryPool* getDefaultMemoryPool() noexcept
|
||||
return Firebird::MemoryPool::defaultMemoryManager;
|
||||
}
|
||||
|
||||
static inline Firebird::MemoryPool* getExternalMemoryPool() noexcept
|
||||
{
|
||||
using namespace Firebird;
|
||||
|
||||
if (!MemoryPool::externalMemoryManager)
|
||||
initExternalMemoryPool();
|
||||
|
||||
return MemoryPool::externalMemoryManager;
|
||||
}
|
||||
|
||||
namespace Firebird {
|
||||
|
||||
// Class intended to manage execution context pool stack
|
||||
@ -341,16 +352,12 @@ private:
|
||||
using Firebird::MemoryPool;
|
||||
|
||||
// operators new and delete
|
||||
extern void* operator new(size_t s ALLOC_PARAMS);
|
||||
extern void* operator new[](size_t s ALLOC_PARAMS);
|
||||
extern void operator delete(void* mem ALLOC_PARAMS) noexcept;
|
||||
extern void operator delete[](void* mem ALLOC_PARAMS) noexcept;
|
||||
|
||||
|
||||
inline void* operator new(size_t s, Firebird::MemoryPool& pool ALLOC_PARAMS)
|
||||
{
|
||||
return pool.allocate(s ALLOC_PASS_ARGS);
|
||||
}
|
||||
|
||||
inline void* operator new[](size_t s, Firebird::MemoryPool& pool ALLOC_PARAMS)
|
||||
{
|
||||
return pool.allocate(s ALLOC_PASS_ARGS);
|
||||
@ -360,6 +367,7 @@ inline void operator delete(void* mem, Firebird::MemoryPool& pool ALLOC_PARAMS)
|
||||
{
|
||||
MemoryPool::globalFree(mem);
|
||||
}
|
||||
|
||||
inline void operator delete[](void* mem, Firebird::MemoryPool& pool ALLOC_PARAMS) noexcept
|
||||
{
|
||||
MemoryPool::globalFree(mem);
|
||||
@ -370,6 +378,7 @@ inline void operator delete(void* mem, std::size_t s ALLOC_PARAMS) noexcept
|
||||
{
|
||||
MemoryPool::globalFree(mem);
|
||||
}
|
||||
|
||||
inline void operator delete[](void* mem, std::size_t s ALLOC_PARAMS) noexcept
|
||||
{
|
||||
MemoryPool::globalFree(mem);
|
||||
@ -390,16 +399,6 @@ namespace Firebird
|
||||
class GlobalStorage
|
||||
{
|
||||
public:
|
||||
void* operator new(size_t size ALLOC_PARAMS)
|
||||
{
|
||||
return getDefaultMemoryPool()->allocate(size ALLOC_PASS_ARGS);
|
||||
}
|
||||
|
||||
void operator delete(void* mem)
|
||||
{
|
||||
getDefaultMemoryPool()->deallocate(mem);
|
||||
}
|
||||
|
||||
MemoryPool& getPool() const
|
||||
{
|
||||
return *getDefaultMemoryPool();
|
||||
|
@ -31,12 +31,13 @@
|
||||
#include "../common/dllinst.h"
|
||||
#include "../common/os/os_utils.h"
|
||||
|
||||
#ifdef HAVE_DLADDR
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#ifdef WIN_NT
|
||||
#include <windows.h>
|
||||
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX MAX_PATH
|
||||
#endif
|
||||
#endif
|
||||
#include <dlfcn.h>
|
||||
#endif // HAVE_DLADDR
|
||||
|
||||
// Setting this define helps (with AV at exit time) detect globals
|
||||
// with destructors, declared not using InstanceControl.
|
||||
@ -96,36 +97,14 @@ namespace
|
||||
Firebird::AutoPtr<FILE> file;
|
||||
|
||||
{ // scope
|
||||
Firebird::PathName name = "memdebug.log";
|
||||
#ifdef HAVE_DLADDR
|
||||
Dl_info path;
|
||||
if (dladdr((void*) &allClean, &path))
|
||||
{
|
||||
name = path.dli_fname;
|
||||
name += ".memdebug.log";
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "dladdr: %s\n", dlerror());
|
||||
}
|
||||
#elif defined(WIN_NT)
|
||||
HMODULE hmod = 0;
|
||||
GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
|
||||
(LPCTSTR) &allClean,
|
||||
&hmod);
|
||||
char name[PATH_MAX];
|
||||
|
||||
if (hmod)
|
||||
{
|
||||
char moduleName[MAX_PATH];
|
||||
DWORD len = GetModuleFileName(hmod, moduleName, MAX_PATH);
|
||||
if (len < MAX_PATH)
|
||||
{
|
||||
name = moduleName;
|
||||
name += ".memdebug.log";
|
||||
}
|
||||
}
|
||||
#endif // HAVE_DLADDR
|
||||
file = os_utils::fopen(name.c_str(), "w+t");
|
||||
if (os_utils::getCurrentModulePath(name, sizeof(name)))
|
||||
strncat(name, ".memdebug.log", sizeof(name) - 1);
|
||||
else
|
||||
strcpy(name, "memdebug.log");
|
||||
|
||||
file = os_utils::fopen(name, "w+t");
|
||||
}
|
||||
#endif // DEBUG_GDS_ALLOC
|
||||
|
||||
@ -154,7 +133,7 @@ namespace
|
||||
file = NULL;
|
||||
}
|
||||
#endif
|
||||
Firebird::MemoryPool::cleanup();
|
||||
Firebird::MemoryPool::cleanupDefaultPool();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@ -193,7 +172,7 @@ namespace
|
||||
}
|
||||
|
||||
Firebird::Mutex::initMutexes();
|
||||
Firebird::MemoryPool::init();
|
||||
Firebird::MemoryPool::initDefaultPool();
|
||||
Firebird::StaticMutex::create();
|
||||
|
||||
#ifdef DEBUG_INIT
|
||||
|
@ -304,12 +304,12 @@ template <typename T>
|
||||
class StaticInstanceAllocator
|
||||
{
|
||||
private:
|
||||
char buf[sizeof(T) + FB_ALIGNMENT];
|
||||
alignas(alignof(T)) char buf[sizeof(T)];
|
||||
|
||||
public:
|
||||
T* create()
|
||||
{
|
||||
return new((void*) FB_ALIGN(buf, FB_ALIGNMENT)) T();
|
||||
return new(buf) T();
|
||||
}
|
||||
|
||||
static void destroy(T*)
|
||||
|
@ -33,6 +33,7 @@
|
||||
|
||||
#include "../../common/classes/locks.h"
|
||||
#include "../../common/ThreadStart.h"
|
||||
#include <mutex>
|
||||
|
||||
|
||||
namespace Firebird {
|
||||
@ -50,15 +51,18 @@ pthread_mutexattr_t Mutex::attr;
|
||||
|
||||
void Mutex::initMutexes()
|
||||
{
|
||||
// Throw exceptions on errors, but they will not be processed in init
|
||||
// (first constructor). Better logging facilities are required here.
|
||||
int rc = pthread_mutexattr_init(&attr);
|
||||
if (rc < 0)
|
||||
system_call_failed::raise("pthread_mutexattr_init", rc);
|
||||
static std::once_flag onceFlag;
|
||||
std::call_once(onceFlag, [] {
|
||||
// Throw exceptions on errors, but they will not be processed in init
|
||||
// (first constructor). Better logging facilities are required here.
|
||||
int rc = pthread_mutexattr_init(&attr);
|
||||
if (rc < 0)
|
||||
system_call_failed::raise("pthread_mutexattr_init", rc);
|
||||
|
||||
rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||
if (rc < 0)
|
||||
system_call_failed::raise("pthread_mutexattr_settype", rc);
|
||||
rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||
if (rc < 0)
|
||||
system_call_failed::raise("pthread_mutexattr_settype", rc);
|
||||
});
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -331,6 +331,13 @@ public:
|
||||
lock->enter(aReason);
|
||||
}
|
||||
|
||||
RaiiLockGuard(M* aLock, const char* aReason)
|
||||
: lock(aLock)
|
||||
{
|
||||
if (lock)
|
||||
lock->enter(aReason);
|
||||
}
|
||||
|
||||
~RaiiLockGuard()
|
||||
{
|
||||
try
|
||||
|
@ -81,6 +81,8 @@ namespace os_utils
|
||||
|
||||
bool isIPv6supported();
|
||||
|
||||
bool getCurrentModulePath(char* buffer, size_t bufferSize);
|
||||
|
||||
// force descriptor to have O_CLOEXEC set
|
||||
int open(const char* pathname, int flags, mode_t mode = DEFAULT_OPEN_MODE);
|
||||
void setCloseOnExec(int fd); // posix only
|
||||
|
@ -43,6 +43,14 @@
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DLADDR
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
#include <dlfcn.h>
|
||||
#endif // HAVE_DLADDR
|
||||
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
@ -335,6 +343,21 @@ bool isIPv6supported()
|
||||
#endif
|
||||
}
|
||||
|
||||
bool getCurrentModulePath(char* buffer, size_t bufferSize)
|
||||
{
|
||||
#ifdef HAVE_DLADDR
|
||||
Dl_info path;
|
||||
|
||||
if (dladdr((void*) &getCurrentModulePath, &path))
|
||||
{
|
||||
strncpy(buffer, path.dli_fname, bufferSize);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// setting flag is not absolutely required, therefore ignore errors here
|
||||
void setCloseOnExec(int fd)
|
||||
{
|
||||
|
@ -380,6 +380,23 @@ bool isIPv6supported()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getCurrentModulePath(char* buffer, size_t bufferSize)
|
||||
{
|
||||
HMODULE hmod = 0;
|
||||
|
||||
GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
|
||||
(LPCTSTR) &getCurrentModulePath,
|
||||
&hmod);
|
||||
|
||||
if (hmod)
|
||||
{
|
||||
GetModuleFileName(hmod, buffer, bufferSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int open(const char* pathname, int flags, mode_t mode)
|
||||
{
|
||||
return ::_open(pathname, flags, mode);
|
||||
|
@ -137,12 +137,8 @@ private:
|
||||
|
||||
// Fool-proof requested by Alex
|
||||
// Private memory operators to be sure that this class is used in heap only with launcher
|
||||
#ifdef DEBUG_GDS_ALLOC
|
||||
void* operator new (size_t s, const char* file, int line) { return MemoryPool::globalAlloc(s, file, line); }
|
||||
void operator delete (void* mem, const char* file, int line) { MemoryPool::globalFree(mem); }
|
||||
#else
|
||||
void* operator new (size_t s) { return MemoryPool::globalAlloc(s); }
|
||||
#endif
|
||||
void* operator new (size_t s, Firebird::MemoryPool& pool ALLOC_PARAMS) { return pool.allocate(s ALLOC_PASS_ARGS); }
|
||||
void operator delete (void* mem, Firebird::MemoryPool& ALLOC_PARAMS) { MemoryPool::globalFree(mem); }
|
||||
void operator delete (void* mem) { MemoryPool::globalFree(mem); }
|
||||
|
||||
public:
|
||||
|
Loading…
Reference in New Issue
Block a user