timed memory allocator for: Steam_Remote_Storage::GetFileNameAndSize(), Steam_Matchmaking_Servers::GetServerDetails(), Steam_Remote_Storage::GetUGCDetails() to avoid leaking memory

This commit is contained in:
a 2024-12-10 23:20:37 +02:00
parent e02cc9b366
commit 804b57a7ed
8 changed files with 136 additions and 21 deletions

View File

@ -81,7 +81,7 @@ public:
unsigned int data_settings_size(std::string file);
int get_data_settings(std::string file, char *data, unsigned int max_length);
int count_files(std::string folder);
bool iterate_file(std::string folder, int index, char *output_filename, int32 *output_size);
bool iterate_file(std::string folder, int index, std::string &output_filename, int32 *output_size);
bool file_exists(std::string folder, std::string file);
unsigned int file_size(std::string folder, std::string file);
bool file_delete(std::string folder, std::string file);

View File

@ -19,6 +19,7 @@
#define __INCLUDED_STEAM_MATCHMAKING_SERVERS_H__
#include "base.h"
#include "common_helpers/forgettable_memory.hpp"
#include <ssq/a2s.h>
struct Steam_Matchmaking_Servers_Direct_IP_Request {
@ -68,6 +69,8 @@ public ISteamMatchmakingServers
std::vector <struct Steam_Matchmaking_Request> requests{};
std::vector <struct Steam_Matchmaking_Servers_Direct_IP_Request> direct_ip_requests{};
common_helpers::ForgettableMemory<gameserveritem_t> requests_from_GetServerDetails{};
HServerListRequest RequestServerList(AppId_t iApp, ISteamMatchmakingServerListResponse *pRequestServersResponse, EMatchMakingType type);
void RequestOldServerList(AppId_t iApp, ISteamMatchmakingServerListResponse001 *pRequestServersResponse, EMatchMakingType type);

View File

@ -20,6 +20,7 @@
#include "base.h"
#include "ugc_remote_storage_bridge.h"
#include "common_helpers/forgettable_memory.hpp"
struct Async_Read {
SteamAPICall_t api_call{};
@ -86,6 +87,7 @@ private:
class Local_Storage *local_storage{};
class SteamCallResults *callback_results{};
class SteamCallBacks *callbacks{};
class RunEveryRunCB *run_every_runcb{};
std::vector<struct Async_Read> async_reads{};
std::vector<struct Stream_Write> stream_writes{};
@ -94,9 +96,16 @@ private:
bool steam_cloud_enabled = true;
common_helpers::ForgettableMemory<std::string> requests_GetFileNameAndSize{};
common_helpers::ForgettableMemory<std::string> requests_GetUGCDetails{};
static void steam_run_every_runcb(void *object);
void RunCallbacks();
public:
Steam_Remote_Storage(class Settings *settings, class Ugc_Remote_Storage_Bridge *ugc_bridge, class Local_Storage *local_storage, class SteamCallResults *callback_results, class SteamCallBacks *callbacks);
Steam_Remote_Storage(class Settings *settings, class Ugc_Remote_Storage_Bridge *ugc_bridge, class Local_Storage *local_storage, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb);
~Steam_Remote_Storage();
// NOTE
//

View File

@ -170,7 +170,7 @@ uint64_t Local_Storage::file_timestamp(std::string folder, std::string file)
return 0;
}
bool Local_Storage::iterate_file(std::string folder, int index, char *output_filename, int32 *output_size)
bool Local_Storage::iterate_file(std::string folder, int index, std::string &output_filename, int32 *output_size)
{
return false;
}
@ -766,8 +766,11 @@ uint64_t Local_Storage::file_timestamp(std::string folder, std::string file)
return buffer.st_mtime;
}
bool Local_Storage::iterate_file(std::string folder, int index, char *output_filename, int32 *output_size)
bool Local_Storage::iterate_file(std::string folder, int index, std::string &output_filename, int32 *output_size)
{
output_filename.clear();
if (output_size) *output_size = 0;
if (folder.size() && folder.back() != *PATH_SEPARATOR) {
folder.append(PATH_SEPARATOR);
}
@ -777,10 +780,12 @@ bool Local_Storage::iterate_file(std::string folder, int index, char *output_fil
std::string name(desanitize_file_name(files[index].name));
if (output_size) *output_size = file_size(folder, name);
#if defined(STEAM_WIN32)
name = replace_with(name, PATH_SEPARATOR, "/");
#endif
strcpy(output_filename, name.c_str());
output_filename = std::move(name);
return true;
}

View File

@ -100,7 +100,7 @@ Steam_Client::Steam_Client()
steam_user_stats = new Steam_User_Stats(settings_client, network, local_storage, callback_results_client, callbacks_client, run_every_runcb, steam_overlay);
steam_apps = new Steam_Apps(settings_client, callback_results_client, callbacks_client);
steam_networking = new Steam_Networking(settings_client, network, callbacks_client, run_every_runcb);
steam_remote_storage = new Steam_Remote_Storage(settings_client, ugc_bridge, local_storage, callback_results_client, callbacks_client);
steam_remote_storage = new Steam_Remote_Storage(settings_client, ugc_bridge, local_storage, callback_results_client, callbacks_client, run_every_runcb);
steam_screenshots = new Steam_Screenshots(local_storage, callbacks_client);
steam_http = new Steam_HTTP(settings_client, network, callback_results_client, callbacks_client);
steam_controller = new Steam_Controller(settings_client, callback_results_client, callbacks_client, run_every_runcb);

View File

@ -395,10 +395,10 @@ gameserveritem_t *Steam_Matchmaking_Servers::GetServerDetails( HServerListReques
}
Gameserver *gs = &gameservers_filtered[iServer].server;
gameserveritem_t *server = new gameserveritem_t(); //TODO: is the new here ok?
server_details(gs, server);
auto &server = requests_from_GetServerDetails.create(std::chrono::hours(1));
server_details(gs, &server);
PRINT_DEBUG(" Returned server details");
return server;
return &server;
}
@ -910,6 +910,8 @@ void Steam_Matchmaking_Servers::RunCallbacks()
if (r.players_response) r.players_response->PlayersRefreshComplete();
if (r.ping_response) r.ping_response->ServerFailedToRespond();
}
requests_from_GetServerDetails.cleanup();
}
void Steam_Matchmaking_Servers::Callback(Common_Message *msg)

View File

@ -38,19 +38,35 @@ static void copy_file(const std::string &src_filepath, const std::string &dst_fi
const auto dst_p(std::filesystem::u8path(dst_filepath));
std::filesystem::create_directories(dst_p.parent_path()); // make the folder tree if needed
std::filesystem::copy_file(src_p, dst_p, std::filesystem::copy_options::overwrite_existing);
std::filesystem::copy_file(src_p, dst_p, std::filesystem::copy_options::overwrite_existing | std::filesystem::copy_options::copy_symlinks);
} catch(...) {}
}
Steam_Remote_Storage::Steam_Remote_Storage(class Settings *settings, class Ugc_Remote_Storage_Bridge *ugc_bridge, class Local_Storage *local_storage, class SteamCallResults *callback_results, class SteamCallBacks *callbacks)
void Steam_Remote_Storage::steam_run_every_runcb(void *object)
{
// PRINT_DEBUG_ENTRY();
auto inst = reinterpret_cast<Steam_Remote_Storage *>(object);
inst->RunCallbacks();
}
Steam_Remote_Storage::Steam_Remote_Storage(class Settings *settings, class Ugc_Remote_Storage_Bridge *ugc_bridge, class Local_Storage *local_storage, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
{
this->settings = settings;
this->ugc_bridge = ugc_bridge;
this->local_storage = local_storage;
this->callback_results = callback_results;
this->callbacks = callbacks;
this->run_every_runcb = run_every_runcb;
steam_cloud_enabled = true;
this->run_every_runcb->add(&Steam_Remote_Storage::steam_run_every_runcb, this);
}
Steam_Remote_Storage::~Steam_Remote_Storage()
{
this->run_every_runcb->remove(&Steam_Remote_Storage::steam_run_every_runcb, this);
}
// NOTE
@ -329,16 +345,17 @@ int32 Steam_Remote_Storage::GetFileCount()
const char* Steam_Remote_Storage::GetFileNameAndSize( int iFile, int32 *pnFileSizeInBytes )
{
PRINT_DEBUG("%i", iFile);
PRINT_DEBUG("%i %p", iFile, pnFileSizeInBytes);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
static char output_filename[MAX_FILENAME_LENGTH];
std::string output_filename{};
if (local_storage->iterate_file(Local_Storage::remote_storage_folder, iFile, output_filename, pnFileSizeInBytes)) {
PRINT_DEBUG("|%s|, size: %i", output_filename, pnFileSizeInBytes ? *pnFileSizeInBytes : 0);
return output_filename;
} else {
return "";
auto &request = requests_GetFileNameAndSize.create(std::chrono::minutes(15), std::move(output_filename));
PRINT_DEBUG("file '%s', size: %i", request.c_str(), pnFileSizeInBytes ? *pnFileSizeInBytes : 0);
return request.c_str();
}
return "";
}
@ -504,8 +521,8 @@ bool Steam_Remote_Storage::GetUGCDetails( UGCHandle_t hContent, AppId_t *pnAppID
if (ppchName) *ppchName = nullptr;
if (auto query_res = ugc_bridge->get_ugc_query_result(hContent)) {
auto mod = settings->getMod(query_res.value().mod_id);
auto &mod_name = query_res.value().is_primary_file
const auto mod = settings->getMod(query_res.value().mod_id);
const auto &mod_name = query_res.value().is_primary_file
? mod.primaryFileName
: mod.previewFileName;
int32 mod_size = query_res.value().is_primary_file
@ -513,8 +530,8 @@ bool Steam_Remote_Storage::GetUGCDetails( UGCHandle_t hContent, AppId_t *pnAppID
: mod.previewFileSize;
if (ppchName) {
*ppchName = new char[mod_name.size() + 1];
std::strcpy(*ppchName, mod_name.c_str());
auto &new_str = requests_GetUGCDetails.create(std::chrono::minutes(30), mod_name); // this will make a copy of mod_name
*ppchName = const_cast<char *>(new_str.c_str());
}
if (pnFileSizeInBytes) *pnFileSizeInBytes = mod_size;
if (pSteamIDOwner) *pSteamIDOwner = mod.steamIDOwner;
@ -1238,3 +1255,10 @@ bool Steam_Remote_Storage::EndFileWriteBatch()
return true;
}
void Steam_Remote_Storage::RunCallbacks()
{
requests_GetFileNameAndSize.cleanup();
requests_GetUGCDetails.cleanup();
}

View File

@ -0,0 +1,72 @@
#pragma once
#include <chrono>
#include <mutex>
#include <forward_list>
#include <utility>
#include <algorithm>
namespace common_helpers
{
template<typename Ty>
class ForgettableMemory {
struct ForgettableBlock {
Ty block;
std::chrono::high_resolution_clock::time_point due_time;
template<typename Rep, typename Period, class... Args>
ForgettableBlock(std::chrono::duration<Rep, Period> duration, Args&&... args)
: due_time(std::chrono::high_resolution_clock::now() + duration),
block( std::forward<Args>(args)... )
{ }
};
std::recursive_mutex mtx{};
std::forward_list<ForgettableBlock> storage{};
public:
template<typename Rep, typename Period, class... Args>
Ty& create(std::chrono::duration<Rep, Period> duration, Args&&... args) {
std::lock_guard lock(mtx);
auto& new_ele = storage.emplace_front(duration, std::forward<Args>(args)...);
return new_ele.block;
}
bool is_alive(const Ty& block) {
std::lock_guard lock(mtx);
auto ele_it = std::find_if(storage.begin(), storage.end(), [&block](const ForgettableBlock &item){
return &item.block == &block;
});
return storage.end() != ele_it;
}
void destroy(const Ty& block) {
std::lock_guard lock(mtx);
storage.remove_if([&block](const ForgettableBlock &item){
return &item.block == &block;
});
}
void destroy_all() {
std::lock_guard lock(mtx);
storage.clear();
}
void cleanup() {
std::lock_guard lock(mtx);
const auto now = std::chrono::high_resolution_clock::now();
storage.remove_if([&now](const ForgettableBlock &item){
return now > item.due_time;
});
}
};
}