8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-22 20:03:02 +01:00

Merge pull request #6991 from FirebirdSQL/work/external-pool

Introduce the external memory pool.
This commit is contained in:
Adriano dos Santos Fernandes 2021-10-30 20:08:24 -03:00 committed by GitHub
commit e64c3aacad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 375 additions and 185 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

25
extern/re2/re2/re2.cc vendored
View File

@ -21,6 +21,7 @@
#include <algorithm> #include <algorithm>
#include <atomic> #include <atomic>
#include <iterator> #include <iterator>
#include <memory>
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <utility> #include <utility>
@ -60,9 +61,9 @@ RE2::Options::Options(RE2::CannedOptions opt)
// static empty objects for use as const references. // static empty objects for use as const references.
// To avoid global constructors, allocated in RE2::Init(). // To avoid global constructors, allocated in RE2::Init().
static const std::string* empty_string; static std::unique_ptr<const std::string> empty_string;
static const std::map<std::string, int>* empty_named_groups; static std::unique_ptr<const std::map<std::string, int>> empty_named_groups;
static const std::map<int, std::string>* empty_group_names; static std::unique_ptr<const std::map<int, std::string>> empty_group_names;
// Converts from Regexp error code to RE2 error code. // Converts from Regexp error code to RE2 error code.
// Maybe some day they will diverge. In any event, this // Maybe some day they will diverge. In any event, this
@ -173,15 +174,15 @@ int RE2::Options::ParseFlags() const {
void RE2::Init(const StringPiece& pattern, const Options& options) { void RE2::Init(const StringPiece& pattern, const Options& options) {
static std::once_flag empty_once; static std::once_flag empty_once;
std::call_once(empty_once, []() { std::call_once(empty_once, []() {
empty_string = new std::string; empty_string.reset(new std::string);
empty_named_groups = new std::map<std::string, int>; empty_named_groups.reset(new std::map<std::string, int>);
empty_group_names = new std::map<int, std::string>; empty_group_names.reset(new std::map<int, std::string>);
}); });
pattern_.assign(pattern.data(), pattern.size()); pattern_.assign(pattern.data(), pattern.size());
options_.Copy(options); options_.Copy(options);
entire_regexp_ = NULL; entire_regexp_ = NULL;
error_ = empty_string; error_ = empty_string.get();
error_code_ = NoError; error_code_ = NoError;
error_arg_.clear(); error_arg_.clear();
prefix_.clear(); prefix_.clear();
@ -267,11 +268,11 @@ RE2::~RE2() {
entire_regexp_->Decref(); entire_regexp_->Decref();
delete prog_; delete prog_;
delete rprog_; delete rprog_;
if (error_ != empty_string) if (error_ != empty_string.get())
delete error_; delete error_;
if (named_groups_ != NULL && named_groups_ != empty_named_groups) if (named_groups_ != NULL && named_groups_ != empty_named_groups.get())
delete named_groups_; delete named_groups_;
if (group_names_ != NULL && group_names_ != empty_group_names) if (group_names_ != NULL && group_names_ != empty_group_names.get())
delete group_names_; delete group_names_;
} }
@ -352,7 +353,7 @@ const std::map<std::string, int>& RE2::NamedCapturingGroups() const {
if (re->suffix_regexp_ != NULL) if (re->suffix_regexp_ != NULL)
re->named_groups_ = re->suffix_regexp_->NamedCaptures(); re->named_groups_ = re->suffix_regexp_->NamedCaptures();
if (re->named_groups_ == NULL) if (re->named_groups_ == NULL)
re->named_groups_ = empty_named_groups; re->named_groups_ = empty_named_groups.get();
}, this); }, this);
return *named_groups_; return *named_groups_;
} }
@ -363,7 +364,7 @@ const std::map<int, std::string>& RE2::CapturingGroupNames() const {
if (re->suffix_regexp_ != NULL) if (re->suffix_regexp_ != NULL)
re->group_names_ = re->suffix_regexp_->CaptureNames(); re->group_names_ = re->suffix_regexp_->CaptureNames();
if (re->group_names_ == NULL) if (re->group_names_ == NULL)
re->group_names_ = empty_group_names; re->group_names_ = empty_group_names.get();
}, this); }, this);
return *group_names_; return *group_names_;
} }

View File

@ -234,7 +234,7 @@ private:
{ {
unsigned l = aMeta->getMessageLength(&statusWrapper); unsigned l = aMeta->getMessageLength(&statusWrapper);
check(&statusWrapper); check(&statusWrapper);
buffer = new unsigned char[l]; buffer = FB_NEW unsigned char[l];
} }
public: public:
@ -336,7 +336,7 @@ public:
if (!charBuffer) if (!charBuffer)
{ {
charBuffer = new char[size + 1]; charBuffer = FB_NEW char[size + 1];
} }
getStrValue(charBuffer); getStrValue(charBuffer);
return charBuffer; return charBuffer;

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

@ -11519,7 +11519,7 @@ void GrantRevokeNode::grantRevoke(thread_db* tdbb, jrd_tra* transaction, const G
const MetaName objName(object ? object->second : ""); const MetaName objName(object ? object->second : "");
bool crdb = false; bool crdb = false;
AutoPtr<char, ArrayDelete> privileges(new char[MAX(strlen(ALL_PRIVILEGES), strlen(privs ? privs : "")) + 1]); AutoPtr<char, ArrayDelete> privileges(FB_NEW char[MAX(strlen(ALL_PRIVILEGES), strlen(privs ? privs : "")) + 1]);
strcpy(privileges, privs ? privs : ""); strcpy(privileges, privs ? privs : "");
if (strcmp(privileges, "A") == 0) if (strcmp(privileges, "A") == 0)

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: