8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-02-02 10:00:38 +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:
Adriano dos Santos Fernandes 2021-10-05 11:24:08 -03:00
parent 21307a7bf8
commit ab42db972a
11 changed files with 359 additions and 170 deletions

View File

@ -21,7 +21,7 @@
# Notes: # Notes:
# Firebird requires the HP-UX Atomic APIs ("AtomicAPI" bundle), released as an # 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 # Before attempting the HPUX build, you must export necessary
# CFLAGS and CXXFLAGS to be picked up by the configure process and # 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... # Flags specific to production or debug build...
# -z, disallow dereferencing of null pointers at runtime # -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) ifeq ($(shell uname -m),ia64)
# ...for IA-64 # ...for IA-64
PROD_FLAGS= +O2 +Onolimit +Ofltacc=strict +Ofenvaccess \ PROD_FLAGS= +O2 +Onolimit +Ofltacc=strict +Ofenvaccess \
$(COMMON_FLAGS) $(COMMON_FLAGS)
DEV_FLAGS= -g -z -DLIBC_CALLS_NEW \ DEV_FLAGS= -g -z \
$(COMMON_FLAGS) $(COMMON_FLAGS)
else else
# ...for PA-RISC # ...for PA-RISC

View File

@ -40,12 +40,22 @@
#include "firebird.h" #include "firebird.h"
#include "../common/classes/alloc.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 <unistd.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <fcntl.h> #include <fcntl.h>
#include <sys/mman.h> #include <sys/mman.h>
#endif #endif
#include "../common/classes/fb_tls.h" #include "../common/classes/fb_tls.h"
@ -68,25 +78,10 @@
#define VALGRIND_FIX_IT // overrides suspicious valgrind behavior #define VALGRIND_FIX_IT // overrides suspicious valgrind behavior
#endif // USE_VALGRIND #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 { namespace {
///#define MEM_DEBUG_EXTERNAL
/*** emergency debugging stuff /*** emergency debugging stuff
static const char* lastFileName; static const char* lastFileName;
static int lastLine; static int lastLine;
@ -147,7 +142,14 @@ typedef Firebird::AtomicCounter::counter_type StatInt;
const int MAP_CACHE_SIZE = 16; // == 1 MB const int MAP_CACHE_SIZE = 16; // == 1 MB
const size_t DEFAULT_ALLOCATION = 65536; 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 #ifndef WIN_NT
struct FailedBlock struct FailedBlock
@ -191,7 +193,7 @@ inline size_t get_map_page_size()
static volatile size_t map_page_size = 0; static volatile size_t map_page_size = 0;
if (!map_page_size) 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) if (!map_page_size)
map_page_size = get_page_size(); map_page_size = get_page_size();
} }
@ -1731,10 +1733,22 @@ private:
public: public:
static MemPool* defaultMemPool; static MemPool* defaultMemPool;
MemPool(); MemPool(MemoryStats& stats, ExtentsCache* extentsCache);
MemPool(MemPool& parent, MemoryStats& stats); MemPool(MemPool& parent, MemoryStats& stats, ExtentsCache* extentsCache);
virtual ~MemPool(void); 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: private:
static const size_t minAllocation = 65536; static const size_t minAllocation = 65536;
static const size_t roundingSize = ALLOC_ALIGNMENT; static const size_t roundingSize = ALLOC_ALIGNMENT;
@ -1749,12 +1763,10 @@ private:
int blocksActive; int blocksActive;
bool pool_destroying, parent_redirect; bool pool_destroying, parent_redirect;
// Statistics group for the pool MemoryStats* stats; // Statistics group for the pool
MemoryStats* stats; MemPool* parent; // Parent pool if present
// Parent pool if present ExtentsCache* extentsCache;
MemPool* parent; AtomicCounter used_memory, mapped_memory; // Memory used
// Memory used
AtomicCounter used_memory, mapped_memory;
private: private:
@ -1810,7 +1822,7 @@ private:
virtual void memoryIsExhausted(void); virtual void memoryIsExhausted(void);
void* allocRaw(size_t length); void* allocRaw(size_t length);
static void releaseMemory(void* block, bool flagExtent) noexcept; 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); void* getExtent(size_t from, size_t& to);
public: public:
@ -1843,23 +1855,36 @@ public:
void setStatsGroup(MemoryStats& stats) noexcept; void setStatsGroup(MemoryStats& stats) noexcept;
// Initialize and finalize global memory pool // Initialize and finalize global memory pool
static MemPool* init() static MemPool* initDefaultPool()
{ {
fb_assert(!defaultMemPool); fb_assert(!defaultMemPool);
static char mpBuffer[sizeof(MemPool) + ALLOC_ALIGNMENT]; alignas(alignof(MemPool)) static char mpBuffer[sizeof(MemPool)];
defaultMemPool = new((void*)(IPTR) MEM_ALIGN((size_t)(IPTR) mpBuffer)) MemPool(); defaultMemPool = new(mpBuffer) MemPool(*MemoryPool::default_stats_group, &defaultExtentsCache);
return defaultMemPool; return defaultMemPool;
} }
static void cleanup() static void cleanupDefaultPool()
{ {
defaultMemPool->~MemPool(); defaultMemPool->~MemPool();
defaultMemPool = NULL; defaultMemPool = NULL;
while (extents_cache.getCount()) while (defaultExtentsCache.count)
releaseRaw(true, extents_cache.pop(), DEFAULT_ALLOCATION, false); 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 #ifndef WIN_NT
unsigned oldCount = 0; unsigned oldCount = 0;
@ -1880,7 +1905,7 @@ public:
++newCount; ++newCount;
FailedBlock* fb = oldList; FailedBlock* fb = oldList;
SemiDoubleLink::pop(oldList); SemiDoubleLink::pop(oldList);
releaseRaw(true, fb, fb->blockSize, false); releaseRaw(true, fb, fb->blockSize, nullptr);
} }
if (newCount == oldCount) if (newCount == oldCount)
@ -1889,7 +1914,6 @@ public:
oldCount = newCount; oldCount = newCount;
} }
#endif // WIN_NT #endif // WIN_NT
} }
// Statistics // Statistics
@ -2012,28 +2036,28 @@ GlobalPtr<Mutex> forceCreationOfDefaultMemoryPool;
MemoryPool* MemoryPool::defaultMemoryManager = NULL; MemoryPool* MemoryPool::defaultMemoryManager = NULL;
MemoryStats* MemoryPool::default_stats_group = NULL; MemoryStats* MemoryPool::default_stats_group = NULL;
MemoryPool* MemoryPool::externalMemoryManager = NULL;
MemPool* MemPool::defaultMemPool = NULL; MemPool* MemPool::defaultMemPool = NULL;
// Initialize process memory pool (called from InstanceControl). // Initialize process memory pool (called from InstanceControl).
void MemoryPool::init() void MemoryPool::initDefaultPool()
{ {
static char mtxBuffer[sizeof(Mutex) + ALLOC_ALIGNMENT]; alignas(alignof(Mutex)) static char mtxBuffer[sizeof(Mutex)];
cache_mutex = new((void*)(IPTR) MEM_ALIGN((size_t)(IPTR) mtxBuffer)) Mutex; cache_mutex = new(mtxBuffer) Mutex;
static char msBuffer[sizeof(MemoryStats) + ALLOC_ALIGNMENT]; alignas(alignof(MemoryStats)) static char msBuffer[sizeof(MemoryStats)];
default_stats_group = default_stats_group = new(msBuffer) MemoryStats;
new((void*)(IPTR) MEM_ALIGN((size_t)(IPTR) msBuffer)) MemoryStats;
static char mpBuffer[sizeof(MemoryPool) + ALLOC_ALIGNMENT]; alignas(alignof(MemoryPool)) static char mpBuffer[sizeof(MemoryPool)];
defaultMemoryManager = new((void*)(IPTR) MEM_ALIGN((size_t)(IPTR) mpBuffer)) MemoryPool(MemPool::init()); defaultMemoryManager = new(mpBuffer) MemoryPool(MemPool::initDefaultPool());
} }
// Should be last routine, called by InstanceControl, // Should be last routine, called by InstanceControl,
// being therefore the very last routine in firebird module. // being therefore the very last routine in firebird module.
void MemoryPool::cleanup() void MemoryPool::cleanupDefaultPool()
{ {
#ifdef VALGRIND_FIX_IT #ifdef VALGRIND_FIX_IT
VALGRIND_DISCARD( VALGRIND_DISCARD(
@ -2047,7 +2071,7 @@ void MemoryPool::cleanup()
if (defaultMemoryManager) if (defaultMemoryManager)
{ {
//defaultMemoryManager->~MemoryPool(); //defaultMemoryManager->~MemoryPool();
MemPool::cleanup(); MemPool::cleanupDefaultPool();
defaultMemoryManager = NULL; defaultMemoryManager = NULL;
} }
@ -2065,15 +2089,23 @@ void MemoryPool::cleanup()
} }
MemPool::MemPool() MemPool::MemPool(MemoryStats& s, ExtentsCache* cache)
: pool_destroying(false), parent_redirect(false), stats(MemoryPool::default_stats_group), parent(NULL) : pool_destroying(false),
parent_redirect(false),
stats(&s),
parent(NULL),
extentsCache(cache)
{ {
fb_assert(offsetof(MemBlock, body) == MEM_ALIGN(offsetof(MemBlock, body))); fb_assert(offsetof(MemBlock, body) == MEM_ALIGN(offsetof(MemBlock, body)));
initialize(); initialize();
} }
MemPool::MemPool(MemPool& p, MemoryStats& s) MemPool::MemPool(MemPool& p, MemoryStats& s, ExtentsCache* cache)
: pool_destroying(false), parent_redirect(true), stats(&s), parent(&p) : pool_destroying(false),
parent_redirect(true),
stats(&s),
parent(&p),
extentsCache(cache)
{ {
initialize(); initialize();
} }
@ -2133,7 +2165,7 @@ MemPool::~MemPool(void)
{ {
MemBigHunk* hunk = bigHunks; MemBigHunk* hunk = bigHunks;
bigHunks = hunk->next; bigHunks = hunk->next;
releaseRaw(pool_destroying, hunk, hunk->length); releaseRaw(pool_destroying, hunk, hunk->length, extentsCache);
} }
if (parent) if (parent)
@ -2210,7 +2242,7 @@ MemoryPool* MemoryPool::createPool(MemoryPool* parentPool, MemoryStats& stats)
if (!parentPool) if (!parentPool)
parentPool = getDefaultMemoryPool(); 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); 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()); MemBigHunk* hunk = (MemBigHunk*)(((UCHAR*)block) - MemBigHunk::hdrSize());
SemiDoubleLink::remove(hunk); SemiDoubleLink::remove(hunk);
decrement_mapping(FB_ALIGN(hunk->length, get_map_page_size())); 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) void MemPool::memoryIsExhausted(void)
@ -2474,12 +2506,12 @@ void* MemPool::allocRaw(size_t size)
#ifndef USE_VALGRIND #ifndef USE_VALGRIND
if (size == DEFAULT_ALLOCATION) if (size == DEFAULT_ALLOCATION)
{ {
MutexLockGuard guard(*cache_mutex, "MemPool::allocRaw"); MutexLockGuard guard(cache_mutex, "MemPool::allocRaw");
if (extents_cache.hasData()) if (extentsCache->count)
{ {
// Use most recently used object to encourage caching // Use most recently used object to encourage caching
increment_mapping(size); increment_mapping(size);
return extents_cache.pop(); return extentsCache->data[--extentsCache->count];
} }
} }
#endif #endif
@ -2497,7 +2529,7 @@ void* MemPool::allocRaw(size_t size)
void* result = NULL; void* result = NULL;
if (failedList) if (failedList)
{ {
MutexLockGuard guard(*cache_mutex, "MemPool::allocRaw"); MutexLockGuard guard(cache_mutex, "MemPool::allocRaw");
for (FailedBlock* fb = failedList; fb; fb = fb->next) for (FailedBlock* fb = failedList; fb; fb = fb->next)
{ {
if (fb->blockSize == size) if (fb->blockSize == size)
@ -2568,20 +2600,20 @@ void MemPool::releaseExtent(bool destroying, void* block, size_t size, MemPool*
{ {
if (pool) if (pool)
pool->decrement_mapping(size); 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 #ifndef USE_VALGRIND
if (use_cache && (size == DEFAULT_ALLOCATION)) if (extentsCache && (size == DEFAULT_ALLOCATION))
{ {
MutexLockGuard guard(*cache_mutex, "MemPool::releaseRaw"); MutexLockGuard guard(cache_mutex, "MemPool::releaseRaw");
if (extents_cache.getCount() < extents_cache.getCapacity()) if (extentsCache->count < MAP_CACHE_SIZE)
{ {
extents_cache.push(block); extentsCache->data[extentsCache->count++] = block;
return; return;
} }
} }
@ -2599,7 +2631,7 @@ void MemPool::releaseRaw(bool destroying, void* block, size_t size, bool use_cac
if (destroying) if (destroying)
{ {
// Synchronize delayed free queue using extents mutex // Synchronize delayed free queue using extents mutex
MutexLockGuard guard(*cache_mutex, "MemPool::releaseRaw"); MutexLockGuard guard(cache_mutex, "MemPool::releaseRaw");
// Extend circular buffer if possible // Extend circular buffer if possible
if (delayedExtentCount < FB_NELEM(delayedExtents)) 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; FailedBlock* failed = (FailedBlock*) block;
failed->blockSize = size; failed->blockSize = size;
MutexLockGuard guard(*cache_mutex, "MemPool::releaseRaw"); MutexLockGuard guard(cache_mutex, "MemPool::releaseRaw");
SemiDoubleLink::push(&failedList, failed); SemiDoubleLink::push(&failedList, failed);
return; return;
@ -2817,25 +2849,6 @@ MemoryPool& AutoStorage::getAutoMemoryPool()
return *p; 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) void* MemoryPool::allocate(size_t size ALLOC_PARAMS)
{ {
return pool->allocate(size ALLOC_PASS_ARGS); 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) #if defined(DEV_BUILD)
void AutoStorage::ProbeStack() const void AutoStorage::ProbeStack() const
{ {
@ -2949,14 +3115,15 @@ void AutoStorage::ProbeStack() const
// Global operator "delete" is always redefined by firebird, // Global operator "delete" is always redefined by firebird,
// in a case when we actually need "new" only with file/line information // in a case when we actually need "new" only with file/line information
// this version should be also present as a pair for "delete". // this version should be also present as a pair for "delete".
#ifdef DEBUG_GDS_ALLOC
void* operator new(size_t s) void* operator new(size_t s)
{ {
return MemoryPool::globalAlloc(s ALLOC_ARGS); return getExternalMemoryPool()->allocate(s ALLOC_ARGS);
} }
void* operator new[](size_t s) void* operator new[](size_t s)
{ {
return MemoryPool::globalAlloc(s ALLOC_ARGS); return getExternalMemoryPool()->allocate(s ALLOC_ARGS);
} }
void operator delete(void* mem) noexcept void operator delete(void* mem) noexcept
@ -2968,5 +3135,3 @@ void operator delete[](void* mem) noexcept
{ {
MemoryPool::globalFree(mem); MemoryPool::globalFree(mem);
} }
#endif // DEBUG_GDS_ALLOC

View File

@ -59,11 +59,11 @@
#include <memory.h> #include <memory.h>
#ifdef DEBUG_GDS_ALLOC #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_POOL(pool) new(pool, __FILE__, __LINE__)
#define FB_NEW_RPT(pool, count) new(pool, count, __FILE__, __LINE__) #define FB_NEW_RPT(pool, count) new(pool, count, __FILE__, __LINE__)
#else // DEBUG_GDS_ALLOC #else // DEBUG_GDS_ALLOC
#define FB_NEW new #define FB_NEW new(*getDefaultMemoryPool())
#define FB_NEW_POOL(pool) new(pool) #define FB_NEW_POOL(pool) new(pool)
#define FB_NEW_RPT(pool, count) new(pool, count) #define FB_NEW_RPT(pool, count) new(pool, count)
#endif // DEBUG_GDS_ALLOC #endif // DEBUG_GDS_ALLOC
@ -159,6 +159,8 @@ private:
class MemoryPool class MemoryPool
{ {
friend class ExternalMemoryHandler;
private: private:
MemPool* pool; MemPool* pool;
@ -174,6 +176,7 @@ public:
enum RecommendedBufferSize { MAX_MEDIUM_BLOCK_SIZE = 64384 }; // MediumLimits::TOP_LIMIT - 128 enum RecommendedBufferSize { MAX_MEDIUM_BLOCK_SIZE = 64384 }; // MediumLimits::TOP_LIMIT - 128
static MemoryPool* defaultMemoryManager; static MemoryPool* defaultMemoryManager;
static MemoryPool* externalMemoryManager;
public: public:
// Create memory pool instance // Create memory pool instance
@ -193,14 +196,10 @@ public:
void* calloc(size_t size ALLOC_PARAMS); 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) static void* globalAlloc(size_t s ALLOC_PARAMS)
{ {
return defaultMemoryManager->allocate(s ALLOC_PASS_ARGS); return defaultMemoryManager->allocate(s ALLOC_PASS_ARGS);
} }
#endif // LIBC_CALLS_NEW
void* allocate(size_t size ALLOC_PARAMS); void* allocate(size_t size ALLOC_PARAMS);
@ -218,8 +217,8 @@ public:
void setStatsGroup(MemoryStats& stats) noexcept; void setStatsGroup(MemoryStats& stats) noexcept;
// Initialize and finalize global memory pool // Initialize and finalize global memory pool
static void init(); static void initDefaultPool();
static void cleanup(); static void cleanupDefaultPool();
// Initialize context pool // Initialize context pool
static void contextPoolInit(); static void contextPoolInit();
@ -282,6 +281,8 @@ private:
friend class MemPool; friend class MemPool;
}; };
void initExternalMemoryPool();
} // namespace Firebird } // namespace Firebird
static inline Firebird::MemoryPool* getDefaultMemoryPool() noexcept static inline Firebird::MemoryPool* getDefaultMemoryPool() noexcept
@ -290,6 +291,16 @@ static inline Firebird::MemoryPool* getDefaultMemoryPool() noexcept
return Firebird::MemoryPool::defaultMemoryManager; return Firebird::MemoryPool::defaultMemoryManager;
} }
static inline Firebird::MemoryPool* getExternalMemoryPool() noexcept
{
using namespace Firebird;
if (!MemoryPool::externalMemoryManager)
initExternalMemoryPool();
return MemoryPool::externalMemoryManager;
}
namespace Firebird { namespace Firebird {
// Class intended to manage execution context pool stack // Class intended to manage execution context pool stack
@ -341,16 +352,12 @@ private:
using Firebird::MemoryPool; using Firebird::MemoryPool;
// operators new and delete // 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) inline void* operator new(size_t s, Firebird::MemoryPool& pool ALLOC_PARAMS)
{ {
return pool.allocate(s ALLOC_PASS_ARGS); return pool.allocate(s ALLOC_PASS_ARGS);
} }
inline void* operator new[](size_t s, Firebird::MemoryPool& pool ALLOC_PARAMS) inline void* operator new[](size_t s, Firebird::MemoryPool& pool ALLOC_PARAMS)
{ {
return pool.allocate(s ALLOC_PASS_ARGS); 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); MemoryPool::globalFree(mem);
} }
inline void operator delete[](void* mem, Firebird::MemoryPool& pool ALLOC_PARAMS) noexcept inline void operator delete[](void* mem, Firebird::MemoryPool& pool ALLOC_PARAMS) noexcept
{ {
MemoryPool::globalFree(mem); MemoryPool::globalFree(mem);
@ -370,6 +378,7 @@ inline void operator delete(void* mem, std::size_t s ALLOC_PARAMS) noexcept
{ {
MemoryPool::globalFree(mem); MemoryPool::globalFree(mem);
} }
inline void operator delete[](void* mem, std::size_t s ALLOC_PARAMS) noexcept inline void operator delete[](void* mem, std::size_t s ALLOC_PARAMS) noexcept
{ {
MemoryPool::globalFree(mem); MemoryPool::globalFree(mem);
@ -390,16 +399,6 @@ namespace Firebird
class GlobalStorage class GlobalStorage
{ {
public: 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 MemoryPool& getPool() const
{ {
return *getDefaultMemoryPool(); return *getDefaultMemoryPool();

View File

@ -31,12 +31,13 @@
#include "../common/dllinst.h" #include "../common/dllinst.h"
#include "../common/os/os_utils.h" #include "../common/os/os_utils.h"
#ifdef HAVE_DLADDR #ifdef WIN_NT
#ifndef _GNU_SOURCE #include <windows.h>
#define _GNU_SOURCE
#ifndef PATH_MAX
#define PATH_MAX MAX_PATH
#endif
#endif #endif
#include <dlfcn.h>
#endif // HAVE_DLADDR
// Setting this define helps (with AV at exit time) detect globals // Setting this define helps (with AV at exit time) detect globals
// with destructors, declared not using InstanceControl. // with destructors, declared not using InstanceControl.
@ -96,36 +97,14 @@ namespace
Firebird::AutoPtr<FILE> file; Firebird::AutoPtr<FILE> file;
{ // scope { // scope
Firebird::PathName name = "memdebug.log"; char name[PATH_MAX];
#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);
if (hmod) if (os_utils::getCurrentModulePath(name, sizeof(name)))
{ strncat(name, ".memdebug.log", sizeof(name) - 1);
char moduleName[MAX_PATH]; else
DWORD len = GetModuleFileName(hmod, moduleName, MAX_PATH); strcpy(name, "memdebug.log");
if (len < MAX_PATH)
{ file = os_utils::fopen(name, "w+t");
name = moduleName;
name += ".memdebug.log";
}
}
#endif // HAVE_DLADDR
file = os_utils::fopen(name.c_str(), "w+t");
} }
#endif // DEBUG_GDS_ALLOC #endif // DEBUG_GDS_ALLOC
@ -154,7 +133,7 @@ namespace
file = NULL; file = NULL;
} }
#endif #endif
Firebird::MemoryPool::cleanup(); Firebird::MemoryPool::cleanupDefaultPool();
} }
catch (...) catch (...)
{ {
@ -193,7 +172,7 @@ namespace
} }
Firebird::Mutex::initMutexes(); Firebird::Mutex::initMutexes();
Firebird::MemoryPool::init(); Firebird::MemoryPool::initDefaultPool();
Firebird::StaticMutex::create(); Firebird::StaticMutex::create();
#ifdef DEBUG_INIT #ifdef DEBUG_INIT

View File

@ -304,12 +304,12 @@ template <typename T>
class StaticInstanceAllocator class StaticInstanceAllocator
{ {
private: private:
char buf[sizeof(T) + FB_ALIGNMENT]; alignas(alignof(T)) char buf[sizeof(T)];
public: public:
T* create() T* create()
{ {
return new((void*) FB_ALIGN(buf, FB_ALIGNMENT)) T(); return new(buf) T();
} }
static void destroy(T*) static void destroy(T*)

View File

@ -33,6 +33,7 @@
#include "../../common/classes/locks.h" #include "../../common/classes/locks.h"
#include "../../common/ThreadStart.h" #include "../../common/ThreadStart.h"
#include <mutex>
namespace Firebird { namespace Firebird {
@ -50,15 +51,18 @@ pthread_mutexattr_t Mutex::attr;
void Mutex::initMutexes() void Mutex::initMutexes()
{ {
// Throw exceptions on errors, but they will not be processed in init static std::once_flag onceFlag;
// (first constructor). Better logging facilities are required here. std::call_once(onceFlag, [] {
int rc = pthread_mutexattr_init(&attr); // Throw exceptions on errors, but they will not be processed in init
if (rc < 0) // (first constructor). Better logging facilities are required here.
system_call_failed::raise("pthread_mutexattr_init", rc); 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); rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
if (rc < 0) if (rc < 0)
system_call_failed::raise("pthread_mutexattr_settype", rc); system_call_failed::raise("pthread_mutexattr_settype", rc);
});
} }
#endif #endif

View File

@ -331,6 +331,13 @@ public:
lock->enter(aReason); lock->enter(aReason);
} }
RaiiLockGuard(M* aLock, const char* aReason)
: lock(aLock)
{
if (lock)
lock->enter(aReason);
}
~RaiiLockGuard() ~RaiiLockGuard()
{ {
try try

View File

@ -81,6 +81,8 @@ namespace os_utils
bool isIPv6supported(); bool isIPv6supported();
bool getCurrentModulePath(char* buffer, size_t bufferSize);
// force descriptor to have O_CLOEXEC set // force descriptor to have O_CLOEXEC set
int open(const char* pathname, int flags, mode_t mode = DEFAULT_OPEN_MODE); int open(const char* pathname, int flags, mode_t mode = DEFAULT_OPEN_MODE);
void setCloseOnExec(int fd); // posix only void setCloseOnExec(int fd); // posix only

View File

@ -43,6 +43,14 @@
#ifdef HAVE_SYS_STAT_H #ifdef HAVE_SYS_STAT_H
#include <sys/stat.h> #include <sys/stat.h>
#endif #endif
#ifdef HAVE_DLADDR
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <dlfcn.h>
#endif // HAVE_DLADDR
#ifdef HAVE_FCNTL_H #ifdef HAVE_FCNTL_H
#include <fcntl.h> #include <fcntl.h>
#endif #endif
@ -335,6 +343,21 @@ bool isIPv6supported()
#endif #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 // setting flag is not absolutely required, therefore ignore errors here
void setCloseOnExec(int fd) void setCloseOnExec(int fd)
{ {

View File

@ -380,6 +380,23 @@ bool isIPv6supported()
return false; 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) int open(const char* pathname, int flags, mode_t mode)
{ {
return ::_open(pathname, flags, mode); return ::_open(pathname, flags, mode);

View File

@ -137,12 +137,8 @@ private:
// Fool-proof requested by Alex // Fool-proof requested by Alex
// Private memory operators to be sure that this class is used in heap only with launcher // 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, Firebird::MemoryPool& pool ALLOC_PARAMS) { return pool.allocate(s ALLOC_PASS_ARGS); }
void* operator new (size_t s, const char* file, int line) { return MemoryPool::globalAlloc(s, file, line); } void operator delete (void* mem, Firebird::MemoryPool& ALLOC_PARAMS) { MemoryPool::globalFree(mem); }
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 delete (void* mem) { MemoryPool::globalFree(mem); } void operator delete (void* mem) { MemoryPool::globalFree(mem); }
public: public: