修复退出时可能崩溃的BUG

This commit is contained in:
UnknownObject
2026-09-11 17:25:36 +08:00
parent 0d50588ea7
commit 7d33ec2ecc
4 changed files with 224 additions and 171 deletions
+52 -12
View File
@@ -12,9 +12,10 @@ class ServerCore::Impl
{
public:
int port = 0;
webcc::Server* ccServer = nullptr;
std::thread thServer;
IPTable BlockedIPs;
std::thread thServer;
webcc::Server* ccServer = nullptr;
std::atomic_bool server_started = false;
public:
explicit Impl(int port)
@@ -30,19 +31,28 @@ public:
{
if(ccServer != nullptr)
{
if(ccServer->IsRunning())
ccServer->Stop();
// if(ccServer->IsRunning())
// ccServer->Stop();
// delete ccServer;
//Fix Crash
ccServer->Stop();
if (thServer.joinable())
thServer.join();
delete ccServer;
ccServer = nullptr;
}
}
public:
static void ServerThreadFunction(webcc::Server* server, int worker_thread, int loop_thread)
static void ServerThreadFunction(webcc::Server* server, int worker_thread, int loop_thread, std::atomic_bool* started_flag)
{
if (server == nullptr)
return;
server->set_buffer_size(65535);
SCLOGF_INFO("ServerCore thread ready: {} Worker(s), {} Loop(s)", worker_thread, loop_thread);
if (started_flag)
started_flag->store(true, std::memory_order_release);
server->Run(worker_thread, loop_thread);
return;
}
@@ -88,17 +98,45 @@ void ServerCore::Run(int worker_thread, int loop_thread)
return;
}
void ServerCore::ThreadRun(int worker_thread, int loop_thread)
bool ServerCore::ThreadRun(int worker_thread, int loop_thread)
{
using namespace std::chrono;
if (pimpl->ccServer == nullptr)
return;
pimpl->thServer = std::thread(Impl::ServerThreadFunction, pimpl->ccServer, worker_thread, loop_thread);
pimpl->thServer.detach();
std::this_thread::sleep_for(100ms);
if (pimpl->ccServer->IsRunning())
return false;
if (pimpl->thServer.joinable())
{
SCLOG_ERROR("ServerCore::ThreadRun called while a server thread is already active");
return false; // 或直接 return,视你采纳下面的返回值方案而定
}
pimpl->server_started.store(false, std::memory_order_relaxed);
pimpl->thServer = std::thread(Impl::ServerThreadFunction, pimpl->ccServer, worker_thread, loop_thread, &pimpl->server_started);
bool reached_run = false;
for (int i = 0; i < 500; i++)
{
if (pimpl->server_started.load(std::memory_order_acquire))
{
reached_run = true;
break;
}
std::this_thread::sleep_for(1ms);
}
if (!reached_run)
{
SCLOGF_FATAL("ServerCore thread failed to schedule within 500ms (port {})", pimpl->port);
// 尽力而为的安全收尾:即使线程迟迟没被调度起来,它终究会跑到 Run(),
// 而 Run() 会一直阻塞直到 Stop() 生效——所以这里调用 Stop()+join()
// 依然能把这个半启动状态收拾干净,不会遗留一个不受控的线程。
pimpl->ccServer->Stop();
if (pimpl->thServer.joinable())
pimpl->thServer.join();
return false;
}
bool running = pimpl->ccServer->IsRunning();
if (running)
SCLOG_INFO("ServerCore Running");
return;
else
SCLOGF_ERROR("ServerCore failed to start listening on port {}", pimpl->port);
return running;
}
void ServerCore::Stop()
@@ -106,6 +144,8 @@ void ServerCore::Stop()
if (pimpl->ccServer == nullptr)
return;
pimpl->ccServer->Stop();
if (pimpl->thServer.joinable())
pimpl->thServer.join();
SCLOG_INFO("ServerCore Stopped");
return;
}
+1 -1
View File
@@ -31,7 +31,7 @@ public:
public:
void Run(int worker_thread = 1, int loop_thread = 1);
void ThreadRun(int worker_thread = 1, int loop_thread = 1);
bool ThreadRun(int worker_thread = 1, int loop_thread = 1);
void Stop();
bool Running();
void UpdateProcessor();
+168 -136
View File
@@ -1,115 +1,147 @@
#include "TempFileManager.h"
#include <thread>
#include <filesystem>
#include <shared_mutex>
#include <system_error>
#include <unordered_map>
#include "ServerLogger.h"
TempFileManager::TempFileManager() noexcept
class TempFileManager::Impl
{
}
// ================= 更新:核心析构函数实现 =================
TempFileManager::~TempFileManager() 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)
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
{
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_INFO("Temp File {} Delete Failed: {}({})", path, ec.value(), ec.message());
}
// 如果 is_permanent 为 true(永久化文件),则跳过不处理,物理文件将安全留在磁盘上
}
// 3. 析构结束,file_map 内存控制块会自动退栈销毁
SCLOG_INFO("TempFileManager Destruction Finished");
}
// ================= 更新:高灵敏度的后台扫描逻辑 =================
void TempFileManager::CleanupLoop(std::stop_token st) noexcept
{
while (!st.stop_requested())
~Impl() noexcept
{
// 改进:引入分段休眠(10次*100ms),让析构函数调用 join() 时能在最大 100ms 内瞬间响应退出
// 避免传统的 sleep_for(1s) 导致服务器内核关闭时卡顿 1 秒
for (int i = 0; i < 10; i++)
// 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)
{
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 (!fcb.is_permanent && !base_dir.empty())
{
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::string path = fcb.info.MakePath(base_dir);
// 使用无异常重载版本,即使磁盘物理删除失败(如文件被外层强行独占锁死)也绝不抛出异常
std::filesystem::remove(path, ec);
if (!ec)
SCLOGF_INFO("Temp File {} {}, Deleted.", path, (it->second.is_invalid ? "Invalid" : "Expried"));
SCLOGF_INFO("Temp File {} Deleted.", path);
else
SCLOGF_INFO("Temp File {} {}, Delete Failed: {}({})", path, (it->second.is_invalid ? "Invalid" : "Expried"), ec.value(), ec.message());
it = file_map.erase(it);
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;
}
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(rw_mutex); // 加上写锁,防止与其他文件操作并发
base_dir = std::move(dir);
std::unique_lock lock(pimpl->rw_mutex); // 加上写锁,防止与其他文件操作并发
pimpl->base_dir = std::move(dir);
std::error_code ec;
// 物理创建目录(无异常版本)
std::filesystem::create_directories(base_dir, ec);
std::filesystem::create_directories(pimpl->base_dir, ec);
if (ec)
SCLOGF_INFO("TempFileManager: create_directories error: {}({})", ec.value(), ec.message());
SCLOGF_ERROR("TempFileManager: create_directories error: {}({})", ec.value(), ec.message());
// 只有在线程未启动时才启动后台清理线程,确保整个生命周期只启动一次
if (!cleanup_thread.joinable() && !ec)
if (!pimpl->cleanup_thread.joinable() && !ec)
{
cleanup_thread = std::jthread([this] (std::stop_token st)
pimpl->cleanup_thread = std::jthread([this] (std::stop_token st)
{
this->CleanupLoop(st);
this->pimpl->CleanupLoop(st);
});
SCLOGF_INFO("TempFileManager: Thread Started.");
}
else
SCLOGF_ERROR("TempFileManager: Failed To Start Thread (Thread Joinable: {}, Error: {})", cleanup_thread.joinable(), ec.message());
SCLOGF_ERROR("TempFileManager: Failed To Start Thread (Thread Joinable: {}, Error: {})", pimpl->cleanup_thread.joinable(), ec.message());
return !ec;
}
@@ -117,36 +149,36 @@ bool TempFileManager::RegisterFile(const WebFileInfo& info, std::chrono::seconds
{
try
{
std::unique_lock lock(rw_mutex);
if (file_map.contains(info.GetStorageFileName()))
std::unique_lock lock(pimpl->rw_mutex);
if (pimpl->file_map.contains(info.GetStorageFileName()))
{
SCLOGF_INFO("RegisterFile Failed: File {} Already Exists", info.GetStorageFileName());
SCLOGF_WARNING("RegisterFile Failed: File {} Already Exists", info.GetStorageFileName());
return false;
}
FileControlBlock fcb
Impl::FileControlBlock fcb
{
.info = info,
.expire_time = std::chrono::steady_clock::now() + timeout,
.remaining_timeout = timeout,
.max_processing_timeout = max_proc_timeout
};
file_map[info.GetStorageFileName()] = std::move(fcb);
pimpl->file_map[info.GetStorageFileName()] = std::move(fcb);
SCLOGF_INFO("File {}({}) Registered", info.GetOriginalFileName(), info.GetStorageFileName());
return true;
}
catch (...)
{
SCLOG_INFO("RegisterFile Failed: Exception");
SCLOG_WARNING("RegisterFile Failed: Exception");
return false;
}
}
bool TempFileManager::InvalidateFile(const WebFileInfo& info)
{
std::unique_lock lock(rw_mutex);
if (file_map.contains(info.GetStorageFileName()))
std::unique_lock lock(pimpl->rw_mutex);
if (pimpl->file_map.contains(info.GetStorageFileName()))
{
file_map[info.GetStorageFileName()].is_invalid = true;
pimpl->file_map[info.GetStorageFileName()].is_invalid = true;
SCLOGF_INFO("File [{}]({}) Marked AS Invalid", info.GetStorageFileName(), info.GetOriginalFileName());
return true;
}
@@ -158,12 +190,12 @@ bool TempFileManager::InvalidateFile(const WebFileInfo& info)
bool TempFileManager::InvalidateFile(const std::map<std::string, std::string>& info)
{
size_t succ_cnt = 0;
std::unique_lock lock(rw_mutex);
std::unique_lock lock(pimpl->rw_mutex);
for (const auto& [on, sn] : info)
{
if (file_map.contains(sn))
if (pimpl->file_map.contains(sn))
{
file_map[sn].is_invalid = true;
pimpl->file_map[sn].is_invalid = true;
succ_cnt++;
SCLOGF_INFO("File [{}]({}) Marked AS Invalid", sn, on);
}
@@ -175,16 +207,16 @@ bool TempFileManager::InvalidateFile(const std::map<std::string, std::string>& i
bool TempFileManager::CopyFileTo(const std::string& storage_name, const std::string& dest_absolute_path) noexcept
{
std::shared_lock lock(rw_mutex);
auto it = file_map.find(storage_name);
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 == file_map.end())
if (it == pimpl->file_map.end())
{
SCLOGF_INFO("CopyFileTo({}, {}) Failed: File Not Exists", storage_name, dest_file);
SCLOGF_WARNING("CopyFileTo({}, {}) Failed: File Not Exists", storage_name, dest_file);
return false;
}
std::error_code ec;
std::filesystem::copy(it->second.info.MakePath(base_dir), dest_file, std::filesystem::copy_options::overwrite_existing, 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
@@ -194,23 +226,23 @@ bool TempFileManager::CopyFileTo(const std::string& storage_name, const std::str
bool TempFileManager::CutFileTo(const std::string& storage_name, const std::string& dest_absolute_path) noexcept
{
std::unique_lock lock(rw_mutex);
auto it = file_map.find(storage_name);
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 == file_map.end())
if (it == pimpl->file_map.end())
{
SCLOGF_INFO("CutFileTo({}, {}) Failed: File Not Exists", storage_name, dest_file);
SCLOGF_WARNING("CutFileTo({}, {}) Failed: File Not Exists", storage_name, dest_file);
return false;
}
std::error_code ec;
std::filesystem::rename(it->second.info.MakePath(base_dir), dest_file, 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;
file_map.erase(it);
pimpl->file_map.erase(it);
return true;
}
@@ -218,18 +250,18 @@ bool TempFileManager::RenameFile(const std::string& old_storage_name, const std:
{
try
{
std::unique_lock lock(rw_mutex);
auto it = file_map.find(old_storage_name);
if ((it == file_map.end()) || file_map.contains(new_storage_name))
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_INFO("RenameFile({}, {}) Failed: File Not Exists", old_storage_name, 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(base_dir);
FileControlBlock fcb = std::move(it->second);
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(base_dir);
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());
@@ -237,43 +269,43 @@ bool TempFileManager::RenameFile(const std::string& old_storage_name, const std:
SCLOGF_INFO("RenameFile({}, {}) Success", old_storage_name, new_storage_name);
if (ec)
return false;
file_map.erase(it);
file_map[new_storage_name] = std::move(fcb);
pimpl->file_map.erase(it);
pimpl->file_map[new_storage_name] = std::move(fcb);
return true;
}
catch (...)
{
SCLOG_INFO("RenameFile Failed: Exception");
SCLOG_WARNING("RenameFile Failed: Exception");
return false;
}
}
bool TempFileManager::DeleteFile(const std::string& storage_name) noexcept
{
std::unique_lock lock(rw_mutex);
auto it = file_map.find(storage_name);
if (it == file_map.end())
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(base_dir), 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;
file_map.erase(it);
pimpl->file_map.erase(it);
return true;
}
bool TempFileManager::ActiveFile(const std::string& storage_name) noexcept
{
std::unique_lock lock(rw_mutex);
auto it = file_map.find(storage_name);
if ((it == file_map.end()) || it->second.is_active)
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;
@@ -291,11 +323,11 @@ bool TempFileManager::ActiveFile(const std::string& storage_name) noexcept
bool TempFileManager::DeactiveFile(const std::string& storage_name) noexcept
{
std::unique_lock lock(rw_mutex);
auto it = file_map.find(storage_name);
if ((it == file_map.end()) || !it->second.is_active)
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 != file_map.end()) ? "Already Deactived" : "File Not Exists"));
SCLOGF_WARNING("DeactiveFile({}) Failed: {}", storage_name, ((it != pimpl->file_map.end()) ? "Already Deactived" : "File Not Exists"));
return false;
}
it->second.is_active = false;
@@ -306,9 +338,9 @@ bool TempFileManager::DeactiveFile(const std::string& storage_name) noexcept
bool TempFileManager::SetPermanent(const std::string& storage_name, bool permanent) noexcept
{
std::unique_lock lock(rw_mutex);
auto it = file_map.find(storage_name);
if (it == file_map.end())
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;
@@ -320,31 +352,31 @@ bool TempFileManager::SetPermanent(const std::string& storage_name, bool permane
bool TempFileManager::FileExists(const std::string& storage_name) const noexcept
{
std::shared_lock lock(rw_mutex);
return file_map.contains(storage_name);
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(rw_mutex);
auto it = file_map.find(storage_name);
if (it == file_map.end())
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_INFO("GetFileInfo({}) Failed: Exception", storage_name);
SCLOGF_WARNING("GetFileInfo({}) Failed: Exception", storage_name);
return false;
}
}
size_t TempFileManager::GetFileCount() const noexcept
{
return file_map.size();
return pimpl->file_map.size();
}
std::vector<WebFileInfo> TempFileManager::GetAllFileInfos() const noexcept
@@ -352,9 +384,9 @@ std::vector<WebFileInfo> TempFileManager::GetAllFileInfos() const noexcept
std::vector<WebFileInfo> list;
try
{
std::shared_lock lock(rw_mutex); // 申请读锁,支持高并发并发读取
list.reserve(file_map.size()); // 提前预留空间,减少内存重分配次数
for (const auto& [_, fcb] : file_map)
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 (...)
+3 -22
View File
@@ -1,12 +1,10 @@
#pragma once
#include <map>
#include <string>
#include <thread>
#include <chrono>
#include <memory>
#include "Export.h"
#include <shared_mutex>
#include "WebFileInfo.h"
#include <unordered_map>
using std::chrono::operator""s;
using std::chrono::operator""min;
@@ -14,26 +12,9 @@ using std::chrono::operator""min;
class UNSWSC_DLL_EXPORT TempFileManager
{
private:
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;
};
class Impl;
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;
void CleanupLoop(std::stop_token st) noexcept;
std::unique_ptr<Impl> pimpl;
public:
TempFileManager() noexcept;