upgrade core add header check and incrace upload speed

This commit is contained in:
UnknownObject
2026-08-05 17:35:27 +08:00
parent 45af94e535
commit 1f11a9d348
76 changed files with 2059 additions and 642 deletions
+397
View File
@@ -0,0 +1,397 @@
#include "TempFileManager.h"
#include <thread>
#include <filesystem>
#include <shared_mutex>
#include <system_error>
#include <unordered_map>
#include "ServerLogger.h"
class TempFileManager::Impl
{
public:
struct FileControlBlock
{
WebFileInfo info;
std::chrono::steady_clock::time_point expire_time;
std::chrono::steady_clock::time_point active_start_time;
std::chrono::seconds remaining_timeout;
std::chrono::seconds max_processing_timeout;
bool is_active = false;
bool is_permanent = false; // 是否永久保留
bool is_invalid = false;
};
std::string base_dir;
std::unordered_map<std::string, FileControlBlock> file_map;
mutable std::shared_mutex rw_mutex;
// C++20 jthread
std::jthread cleanup_thread;
public:
Impl() noexcept
{
}
~Impl() noexcept
{
// 1. 必须第一步:发送停止信号并阻塞等待后台清理线程彻底退出
// 这样能确保后面遍历 file_map 时,绝对没有第二个线程在并发访问它
SCLOG_INFO("TempFileManager Destruction Begin");
cleanup_thread.request_stop();
if (cleanup_thread.joinable())
cleanup_thread.join();
// 2. 此时属于单线程环境,无需加锁。遍历并销毁所有非永久文件
std::error_code ec;
for (const auto& [_, fcb] : file_map)
{
if (!fcb.is_permanent && !base_dir.empty())
{
std::string path = fcb.info.MakePath(base_dir);
// 使用无异常重载版本,即使磁盘物理删除失败(如文件被外层强行独占锁死)也绝不抛出异常
std::filesystem::remove(path, ec);
if (!ec)
SCLOGF_INFO("Temp File {} Deleted.", path);
else
SCLOGF_WARNING("Temp File {} Delete Failed: {}({})", path, ec.value(), ec.message());
}
// 如果 is_permanent 为 true(永久化文件),则跳过不处理,物理文件将安全留在磁盘上
}
// 3. 析构结束,file_map 内存控制块会自动退栈销毁
SCLOG_INFO("TempFileManager Destruction Finished");
}
public:
void CleanupLoop(std::stop_token st) noexcept
{
while (!st.stop_requested())
{
// 改进:引入分段休眠(10次*100ms),让析构函数调用 join() 时能在最大 100ms 内瞬间响应退出
// 避免传统的 sleep_for(1s) 导致服务器内核关闭时卡顿 1 秒
for (int i = 0; i < 10; i++)
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
if (st.stop_requested())
return; // 随时收到终止信号随时退出
}
std::unique_lock lock(rw_mutex);
if (base_dir.empty())
continue;
auto now = std::chrono::steady_clock::now();
for (auto it = file_map.begin(); it != file_map.end(); )
{
bool should_delete = false;
if (it->second.is_invalid)
should_delete = true; //手动设置的无条件立即删除
if (!it->second.is_permanent)
{
if (it->second.is_active)
{
if ((it->second.max_processing_timeout > std::chrono::seconds(0)) && ((now - it->second.active_start_time) > it->second.max_processing_timeout))
should_delete = true;
}
else
{
if (now > it->second.expire_time)
should_delete = true;
}
}
if (should_delete)
{
std::error_code ec;
std::string path = it->second.info.MakePath(base_dir);
std::filesystem::remove(path, ec);
if (!ec)
SCLOGF_INFO("Temp File {} {}, Deleted.", path, (it->second.is_invalid ? "Invalid" : "Expried"));
else
SCLOGF_WARNING("Temp File {} {}, Delete Failed: {}({})", path, (it->second.is_invalid ? "Invalid" : "Expried"), ec.value(), ec.message());
it = file_map.erase(it);
}
else
++it;
}
}
}
};
TempFileManager::TempFileManager() noexcept : pimpl(std::make_unique<Impl>())
{
}
TempFileManager::~TempFileManager() noexcept = default;
// ================= 以下其余基础业务接口保持不变 =================
bool TempFileManager::SetBaseDirectory(std::string dir) noexcept
{
std::unique_lock lock(pimpl->rw_mutex); // 加上写锁,防止与其他文件操作并发
pimpl->base_dir = std::move(dir);
std::error_code ec;
// 物理创建目录(无异常版本)
std::filesystem::create_directories(pimpl->base_dir, ec);
if (ec)
SCLOGF_ERROR("TempFileManager: create_directories error: {}({})", ec.value(), ec.message());
// 只有在线程未启动时才启动后台清理线程,确保整个生命周期只启动一次
if (!pimpl->cleanup_thread.joinable() && !ec)
{
pimpl->cleanup_thread = std::jthread([this] (std::stop_token st)
{
this->pimpl->CleanupLoop(st);
});
SCLOGF_INFO("TempFileManager: Thread Started.");
}
else
SCLOGF_ERROR("TempFileManager: Failed To Start Thread (Thread Joinable: {}, Error: {})", pimpl->cleanup_thread.joinable(), ec.message());
return !ec;
}
bool TempFileManager::RegisterFile(const WebFileInfo& info, std::chrono::seconds timeout, std::chrono::seconds max_proc_timeout) noexcept
{
try
{
std::unique_lock lock(pimpl->rw_mutex);
if (pimpl->file_map.contains(info.GetStorageFileName()))
{
SCLOGF_WARNING("RegisterFile Failed: File {} Already Exists", info.GetStorageFileName());
return false;
}
Impl::FileControlBlock fcb
{
.info = info,
.expire_time = std::chrono::steady_clock::now() + timeout,
.remaining_timeout = timeout,
.max_processing_timeout = max_proc_timeout
};
pimpl->file_map[info.GetStorageFileName()] = std::move(fcb);
SCLOGF_INFO("File {}({}) Registered", info.GetOriginalFileName(), info.GetStorageFileName());
return true;
}
catch (...)
{
SCLOG_WARNING("RegisterFile Failed: Exception");
return false;
}
}
bool TempFileManager::InvalidateFile(const WebFileInfo& info)
{
std::unique_lock lock(pimpl->rw_mutex);
if (pimpl->file_map.contains(info.GetStorageFileName()))
{
pimpl->file_map[info.GetStorageFileName()].is_invalid = true;
SCLOGF_INFO("File [{}]({}) Marked AS Invalid", info.GetStorageFileName(), info.GetOriginalFileName());
return true;
}
else
SCLOGF_WARNING("InvalidateFile({}/{}) Failed: Not Found", info.GetOriginalFileName(), info.GetStorageFileName());
return false;
}
bool TempFileManager::InvalidateFile(const std::map<std::string, std::string>& info)
{
size_t succ_cnt = 0;
std::unique_lock lock(pimpl->rw_mutex);
for (const auto& [on, sn] : info)
{
if (pimpl->file_map.contains(sn))
{
pimpl->file_map[sn].is_invalid = true;
succ_cnt++;
SCLOGF_INFO("File [{}]({}) Marked AS Invalid", sn, on);
}
else
SCLOGF_WARNING("InvalidateFile({}/{}) Failed: Not Found", on, sn);
}
return (succ_cnt == info.size());
}
bool TempFileManager::CopyFileTo(const std::string& storage_name, const std::string& dest_absolute_path) noexcept
{
std::shared_lock lock(pimpl->rw_mutex);
auto it = pimpl->file_map.find(storage_name);
auto dest_file = std::filesystem::path(dest_absolute_path) / storage_name;
if (it == pimpl->file_map.end())
{
SCLOGF_WARNING("CopyFileTo({}, {}) Failed: File Not Exists", storage_name, dest_file);
return false;
}
std::error_code ec;
std::filesystem::copy(it->second.info.MakePath(pimpl->base_dir), dest_file, std::filesystem::copy_options::overwrite_existing, ec);
if (ec)
SCLOGF_WARNING("CopyFileTo({}, {}) Failed: {}({})", storage_name, dest_file, ec.value(), ec.message());
else
SCLOGF_INFO("CopyFileTo({}, {}) Success", storage_name, dest_file);
return !ec;
}
bool TempFileManager::CutFileTo(const std::string& storage_name, const std::string& dest_absolute_path) noexcept
{
std::unique_lock lock(pimpl->rw_mutex);
auto it = pimpl->file_map.find(storage_name);
auto dest_file = std::filesystem::path(dest_absolute_path) / storage_name;
if (it == pimpl->file_map.end())
{
SCLOGF_WARNING("CutFileTo({}, {}) Failed: File Not Exists", storage_name, dest_file);
return false;
}
std::error_code ec;
std::filesystem::rename(it->second.info.MakePath(pimpl->base_dir), dest_file, ec);
if (ec)
SCLOGF_WARNING("CutFileTo({}, {}) Failed: {}({})", storage_name, dest_file, ec.value(), ec.message());
else
SCLOGF_INFO("CutFileTo({}, {}) Success", storage_name, dest_file);
if (ec)
return false;
pimpl->file_map.erase(it);
return true;
}
bool TempFileManager::RenameFile(const std::string& old_storage_name, const std::string& new_storage_name) noexcept
{
try
{
std::unique_lock lock(pimpl->rw_mutex);
auto it = pimpl->file_map.find(old_storage_name);
if ((it == pimpl->file_map.end()) || pimpl->file_map.contains(new_storage_name))
{
SCLOGF_WARNING("RenameFile({}, {}) Failed: File Not Exists", old_storage_name, new_storage_name);
return false;
}
std::error_code ec;
std::string old_path = it->second.info.MakePath(pimpl->base_dir);
Impl::FileControlBlock fcb = std::move(it->second);
fcb.info.SetStorageFileName(new_storage_name);
std::string new_path = fcb.info.MakePath(pimpl->base_dir);
std::filesystem::rename(old_path, new_path, ec);
if (ec)
SCLOGF_WARNING("RenameFile({}, {}) Failed: {}({})", old_storage_name, new_storage_name, ec.value(), ec.message());
else
SCLOGF_INFO("RenameFile({}, {}) Success", old_storage_name, new_storage_name);
if (ec)
return false;
pimpl->file_map.erase(it);
pimpl->file_map[new_storage_name] = std::move(fcb);
return true;
}
catch (...)
{
SCLOG_WARNING("RenameFile Failed: Exception");
return false;
}
}
bool TempFileManager::DeleteFile(const std::string& storage_name) noexcept
{
std::unique_lock lock(pimpl->rw_mutex);
auto it = pimpl->file_map.find(storage_name);
if (it == pimpl->file_map.end())
{
SCLOGF_WARNING("DeleteFile({}) Failed: File Not Exists", storage_name);
return false;
}
std::error_code ec;
std::filesystem::remove(it->second.info.MakePath(pimpl->base_dir), ec);
if (ec)
SCLOGF_WARNING("DeleteFile({}) Failed: {}({})", storage_name, ec.value(), ec.message());
else
SCLOGF_INFO("DeleteFile({}) Success", storage_name);
if (ec)
return false;
pimpl->file_map.erase(it);
return true;
}
bool TempFileManager::ActiveFile(const std::string& storage_name) noexcept
{
std::unique_lock lock(pimpl->rw_mutex);
auto it = pimpl->file_map.find(storage_name);
if ((it == pimpl->file_map.end()) || it->second.is_active)
{
SCLOGF_WARNING("ActiveFile({}) Failed: {}", storage_name, (it->second.is_active ? "Already Actived" : "File Not Exists"));
return false;
}
auto now = std::chrono::steady_clock::now();
if (it->second.expire_time > now)
it->second.remaining_timeout = std::chrono::duration_cast<std::chrono::seconds>(it->second.expire_time - now);
else
it->second.remaining_timeout = std::chrono::seconds(0);
it->second.is_active = true;
it->second.active_start_time = now;
SCLOGF_INFO("ActiveFile({}) Success", storage_name);
return true;
}
bool TempFileManager::DeactiveFile(const std::string& storage_name) noexcept
{
std::unique_lock lock(pimpl->rw_mutex);
auto it = pimpl->file_map.find(storage_name);
if ((it == pimpl->file_map.end()) || !it->second.is_active)
{
SCLOGF_WARNING("DeactiveFile({}) Failed: {}", storage_name, ((it != pimpl->file_map.end()) ? "Already Deactived" : "File Not Exists"));
return false;
}
it->second.is_active = false;
it->second.expire_time = std::chrono::steady_clock::now() + it->second.remaining_timeout;
SCLOGF_INFO("DeactiveFile({}) Success", storage_name);
return true;
}
bool TempFileManager::SetPermanent(const std::string& storage_name, bool permanent) noexcept
{
std::unique_lock lock(pimpl->rw_mutex);
auto it = pimpl->file_map.find(storage_name);
if (it == pimpl->file_map.end())
{
SCLOGF_WARNING("SetPermanent({}) Failed: File Not Exists", storage_name);
return false;
}
it->second.is_permanent = permanent;
SCLOGF_INFO("SetPermanent({}) Success", storage_name);
return true;
}
bool TempFileManager::FileExists(const std::string& storage_name) const noexcept
{
std::shared_lock lock(pimpl->rw_mutex);
return pimpl->file_map.contains(storage_name);
}
bool TempFileManager::GetFileInfo(const std::string& storage_name, WebFileInfo& out_info) const noexcept
{
try
{
std::shared_lock lock(pimpl->rw_mutex);
auto it = pimpl->file_map.find(storage_name);
if (it == pimpl->file_map.end())
return false;
out_info = it->second.info;
return true;
}
catch (...)
{
SCLOGF_WARNING("GetFileInfo({}) Failed: Exception", storage_name);
return false;
}
}
size_t TempFileManager::GetFileCount() const noexcept
{
return pimpl->file_map.size();
}
std::vector<WebFileInfo> TempFileManager::GetAllFileInfos() const noexcept
{
std::vector<WebFileInfo> list;
try
{
std::shared_lock lock(pimpl->rw_mutex); // 申请读锁,支持高并发并发读取
list.reserve(pimpl->file_map.size()); // 提前预留空间,减少内存重分配次数
for (const auto& [_, fcb] : pimpl->file_map)
list.push_back(fcb.info); // 拷贝文件元数据到外部
}
catch (...)
{
list.clear(); // 极端内存崩溃(bad_alloc)时,清空并安全返回空数组
}
return list;
}