mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-22 16:43:03 +01:00
Merge pull request #6991 from FirebirdSQL/work/external-pool
Introduce the external memory pool.
This commit is contained in:
commit
e64c3aacad
@ -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
|
||||
|
25
extern/re2/re2/re2.cc
vendored
25
extern/re2/re2/re2.cc
vendored
@ -21,6 +21,7 @@
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@ -60,9 +61,9 @@ RE2::Options::Options(RE2::CannedOptions opt)
|
||||
|
||||
// static empty objects for use as const references.
|
||||
// To avoid global constructors, allocated in RE2::Init().
|
||||
static const std::string* empty_string;
|
||||
static 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::string> empty_string;
|
||||
static std::unique_ptr<const std::map<std::string, int>> empty_named_groups;
|
||||
static std::unique_ptr<const std::map<int, std::string>> empty_group_names;
|
||||
|
||||
// Converts from Regexp error code to RE2 error code.
|
||||
// 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) {
|
||||
static std::once_flag empty_once;
|
||||
std::call_once(empty_once, []() {
|
||||
empty_string = new std::string;
|
||||
empty_named_groups = new std::map<std::string, int>;
|
||||
empty_group_names = new std::map<int, std::string>;
|
||||
empty_string.reset(new std::string);
|
||||
empty_named_groups.reset(new std::map<std::string, int>);
|
||||
empty_group_names.reset(new std::map<int, std::string>);
|
||||
});
|
||||
|
||||
pattern_.assign(pattern.data(), pattern.size());
|
||||
options_.Copy(options);
|
||||
entire_regexp_ = NULL;
|
||||
error_ = empty_string;
|
||||
error_ = empty_string.get();
|
||||
error_code_ = NoError;
|
||||
error_arg_.clear();
|
||||
prefix_.clear();
|
||||
@ -267,11 +268,11 @@ RE2::~RE2() {
|
||||
entire_regexp_->Decref();
|
||||
delete prog_;
|
||||
delete rprog_;
|
||||
if (error_ != empty_string)
|
||||
if (error_ != empty_string.get())
|
||||
delete error_;
|
||||
if (named_groups_ != NULL && named_groups_ != empty_named_groups)
|
||||
if (named_groups_ != NULL && named_groups_ != empty_named_groups.get())
|
||||
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_;
|
||||
}
|
||||
|
||||
@ -352,7 +353,7 @@ const std::map<std::string, int>& RE2::NamedCapturingGroups() const {
|
||||
if (re->suffix_regexp_ != NULL)
|
||||
re->named_groups_ = re->suffix_regexp_->NamedCaptures();
|
||||
if (re->named_groups_ == NULL)
|
||||
re->named_groups_ = empty_named_groups;
|
||||
re->named_groups_ = empty_named_groups.get();
|
||||
}, this);
|
||||
return *named_groups_;
|
||||
}
|
||||
@ -363,7 +364,7 @@ const std::map<int, std::string>& RE2::CapturingGroupNames() const {
|
||||
if (re->suffix_regexp_ != NULL)
|
||||
re->group_names_ = re->suffix_regexp_->CaptureNames();
|
||||
if (re->group_names_ == NULL)
|
||||
re->group_names_ = empty_group_names;
|
||||
re->group_names_ = empty_group_names.get();
|
||||
}, this);
|
||||
return *group_names_;
|
||||
}
|
||||
|
@ -234,7 +234,7 @@ private:
|
||||
{
|
||||
unsigned l = aMeta->getMessageLength(&statusWrapper);
|
||||
check(&statusWrapper);
|
||||
buffer = new unsigned char[l];
|
||||
buffer = FB_NEW unsigned char[l];
|
||||
}
|
||||
|
||||
public:
|
||||
@ -336,7 +336,7 @@ public:
|
||||
|
||||
if (!charBuffer)
|
||||
{
|
||||
charBuffer = new char[size + 1];
|
||||
charBuffer = FB_NEW char[size + 1];
|
||||
}
|
||||
getStrValue(charBuffer);
|
||||
return charBuffer;
|
||||
|
@ -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);
|
||||
|
@ -11519,7 +11519,7 @@ void GrantRevokeNode::grantRevoke(thread_db* tdbb, jrd_tra* transaction, const G
|
||||
const MetaName objName(object ? object->second : "");
|
||||
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 : "");
|
||||
|
||||
if (strcmp(privileges, "A") == 0)
|
||||
|
@ -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