* Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
#ifndef BOOST_SS_MEM_MANAGER_H
#define BOOST_SS_MEM_MANAGER_H
#include <array>
#include <atomic>
#include <functional>
#include <mutex>
#include <unordered_map>
#include <utility>
#include "allocator.h"
#include "bss_metric.h"
#include "common/bss_log.h"
#include "executor_service.h"
#include "include/bss_err.h"
#include "include/bss_types.h"
#include "include/config.h"
#include "include/ref.h"
namespace ock {
namespace bss {
constexpr size_t MEM_TYPE_NUM = static_cast<size_t>(MemoryType::MEM_TYPE_BUTT);
class MemManager : public Referable {
public:
explicit MemManager(AllocatorType type) : mAllocatorType(type), mInitialized(false){};
~MemManager() override
{
std::lock_guard<std::mutex> lk(mLock);
LOG_INFO("Memory info when delete memory manager: [File used:"
<< mTypeCurrentSize[GetIndex(MemoryType::FILE_STORE)].load()
<< ",Fresh used: " << mTypeCurrentSize[GetIndex(MemoryType::FRESH_TABLE)].load()
<< ",Slice used: " << mTypeCurrentSize[GetIndex(MemoryType::SLICE_TABLE)].load()
<< ",Snap used: " << mTypeCurrentSize[GetIndex(MemoryType::SNAPSHOT)].load()
<< ",Heap used: " << mHeapUsedSize.load() << "]");
if (mInitialized && (mAllocatorType == AllocatorType::MEM_MULTI) && mBaseAddr != nullptr) {
mAllocator->Destroy();
free(mBaseAddr);
mBaseAddr = nullptr;
}
mAllocator = nullptr;
if (mInstance != nullptr) {
mInstance = nullptr;
}
#if ENABLE_MEMORY_TRACKER
ReportLeaks();
#endif
}
inline ConfigRef GetConfig()
{
return mConfig;
}
inline size_t CalcSize(uint64_t size)
{
return size + sizeof(struct SegmentHeads);
}
inline size_t GetIndex(MemoryType type)
{
return static_cast<size_t>(type);
}
BResult Initialize(ConfigRef config, bool alignAddress = true);
void Exit()
{
}
BResult GetMemory(MemoryType type, uint64_t size, uintptr_t &mrAddress, bool force = true, uint32_t timeout = 0);
BResult GetMemoryDirect(MemoryType type, uint64_t size, uintptr_t &mrAddress);
BResult GetHeapMemory(const MemoryType &type, uint64_t size, uintptr_t &mrAddress);
BResult ReleaseMemory(uintptr_t &mrAddress);
uint64_t GetMemoryUseSize(MemoryType type);
uint64_t GetMemoryTypeMaxSize(MemoryType type);
std::string ToString();
bool CalMemoryTypeSize(uint64_t memoryLimit);
BResult InitAllocator();
BResult AddDbRefCount();
void DecDbRefCount();
uint32_t CalcFreshTableSize();
bool UpdateMemoryTypeSize();
void PrintUsage();
void Revoke(MemoryType type, uint64_t size);
static inline uint64_t ChangeHeapOverSize(uint64_t newSize)
{
uint64_t heapUsedSize;
if (newSize < (heapUsedSize = mHeapUsedSize.load())) {
newSize = heapUsedSize + IO_SIZE_4MB;
}
uint64_t oldSize = mHeapAvailableSize.load();
mHeapAvailableSize.store(newSize);
LOG_INFO("Resize heap available size from " << oldSize << " to " << newSize);
return newSize;
}
void RegisterLsmStoreRevoke(std::function<void(uint64_t)> func)
{
if (mFileStoreMemRevoke == nullptr) {
mFileStoreMemRevoke = func;
}
}
inline void RegisterMetric(uintptr_t dbAddr, BoostNativeMetricPtr metricPtr)
{
if (UNLIKELY(metricPtr == nullptr)) {
LOG_ERROR("RegisterMetric failed, metricPtr is nullptr.");
return;
}
metricPtr->SetUsedMemoryGetter([this](MemoryType type) -> uint64_t { return GetMemoryUseSize(type); });
metricPtr->SetMaxMemoryGetter([this](MemoryType type) -> uint64_t { return GetMemoryTypeMaxSize(type); });
mDBsMetricMap.emplace(dbAddr, metricPtr);
for (const auto &pair : mDBsMetricMap) {
BoostNativeMetricPtr metric = pair.second;
metric->SetMemoryTotalMax(mMemoryLimit);
metric->SetMemoryFreshMax(mTypeMaxSize[static_cast<size_t>(MemoryType::FRESH_TABLE)]);
metric->SetMemorySliceMax(mTypeMaxSize[static_cast<size_t>(MemoryType::SLICE_TABLE)]);
metric->SetMemoryFileMax(mTypeMaxSize[static_cast<size_t>(MemoryType::FILE_STORE)]);
metric->SetMemorySnapshotMax(mTypeMaxSize[static_cast<size_t>(MemoryType::SNAPSHOT)]);
LOG_INFO("Set memory limit info to metric.");
}
LOG_INFO("Register metric to memory manager success.");
}
inline void DeregisterMetric(uintptr_t dbAddr)
{
auto iter = mDBsMetricMap.find(dbAddr);
if (iter != mDBsMetricMap.end()) {
auto metric = iter->second;
metric->SetUsedMemoryGetter(nullptr);
metric->SetMaxMemoryGetter(nullptr);
mDBsMetricMap.erase(dbAddr);
}
LOG_INFO("Deregister metric from memory manager success.");
}
#if ENABLE_MEMORY_TRACKER
void ReportLeaks()
{
std::lock_guard<std::mutex> lk(mmAllocatedLock);
if (!mAllocated.empty()) {
LOG_ERROR("Memory leaks detected: " << mAllocated.size() << " blocks");
for (const auto &addr : mAllocated) {
auto head = reinterpret_cast<struct SegmentHeads *>(addr - sizeof(struct SegmentHeads));
LOG_ERROR("Leaked: << std::hex << addr << std::dec << , Size: " << head->size
<< ", Type: " << head->type);
}
} else {
LOG_INFO("No memory leaks detected.");
}
}
#endif
private:
static MemManager *mInstance;
static std::mutex mLock;
uint64_t mMemoryLimit = 0;
ConfigRef mConfig;
AllocatorType mAllocatorType;
std::atomic<bool> mInitialized{ false };
AllocatorPtr mAllocator;
void *mBaseAddr = nullptr;
std::function<void(uint64_t)> mFileStoreMemRevoke;
bool mAlignAddress = false;
bool inReleasePhase = false;
uint32_t mTaskSlotFlag = 0;
std::atomic<uint32_t> mDbRefCount{ 0 };
static std::atomic<uint64_t> mHeapAvailableSize;
static std::atomic<uint64_t> mHeapUsedSize;
std::array<uint64_t, MEM_TYPE_NUM> mTypeMaxSize;
std::array<std::atomic<uint64_t>, MEM_TYPE_NUM> mTypeCurrentSize;
std::unordered_map<uintptr_t, BoostNativeMetricPtr> mDBsMetricMap;
#if ENABLE_MEMORY_TRACKER
std::mutex mmAllocatedLock;
std::list<uintptr_t> mAllocated;
#endif
};
using MemManagerRef = std::shared_ptr<MemManager>;
#if ENABLE_MEMORY_MONITOR
class MemManagerMonitorTask : public Runnable {
public:
MemManagerMonitorTask(MemManagerRef memManager) : mMemManager(std::move(memManager))
{
}
~MemManagerMonitorTask() override
{
LOG_INFO("Delete MemManagerMonitorTask success");
}
void Run() override
{
while (true) {
if (mStopFlag) {
break;
}
mMemManager->PrintUsage();
std::this_thread::sleep_for(std::chrono::seconds(NO_3));
}
}
void StopTask()
{
mStopFlag = true;
}
private:
MemManagerRef mMemManager;
bool mStopFlag = false;
};
using MemManagerMonitorTaskRef = std::shared_ptr<MemManagerMonitorTask>;
#endif
}
}
#endif