mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-16 07:30:42 +01:00
Core/Scripting: Use the systems temporary path for caching shared libs
* Disables the shared library caching on platforms completely which never block files on usage (like linux).
This commit is contained in:
@@ -56,6 +56,7 @@ ScriptReloadMgr* ScriptReloadMgr::instance()
|
||||
#include "Config.h"
|
||||
#include "BuiltInConfig.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "SHA1.h"
|
||||
#include "StartProcess.h"
|
||||
#include "MPSCQueue.h"
|
||||
#include "GitRevision.h"
|
||||
@@ -64,8 +65,10 @@ namespace fs = boost::filesystem;
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#define HOTSWAP_PLATFORM_REQUIRES_CACHING
|
||||
#else // Posix
|
||||
#include <dlfcn.h>
|
||||
// #define HOTSWAP_PLATFORM_REQUIRES_CACHING
|
||||
#endif
|
||||
|
||||
// Promote the sScriptReloadMgr to a HotSwapScriptReloadMgr
|
||||
@@ -102,10 +105,10 @@ typedef void* HandleType;
|
||||
class SharedLibraryUnloader
|
||||
{
|
||||
public:
|
||||
SharedLibraryUnloader()
|
||||
: _path() { }
|
||||
explicit SharedLibraryUnloader(fs::path const& path)
|
||||
: _path(path) { }
|
||||
explicit SharedLibraryUnloader(fs::path path)
|
||||
: path_(std::move(path)) { }
|
||||
SharedLibraryUnloader(fs::path path, Optional<fs::path> cache_path)
|
||||
: path_(std::move(path)), cache_path_(std::move(cache_path)) { }
|
||||
|
||||
void operator() (HandleType handle) const
|
||||
{
|
||||
@@ -119,26 +122,37 @@ public:
|
||||
if (!success)
|
||||
{
|
||||
TC_LOG_ERROR("scripts.hotswap", "Failed to unload (syscall) the shared library \"%s\".",
|
||||
_path.generic_string().c_str());
|
||||
path_.generic_string().c_str());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
boost::system::error_code error;
|
||||
if (fs::remove(_path, error))
|
||||
/// When the shared library was cached delete it's shared version
|
||||
if (cache_path_)
|
||||
{
|
||||
TC_LOG_TRACE("scripts.hotswap", "Lazy unloaded and deleted the shared library \"%s\".",
|
||||
_path.generic_string().c_str());
|
||||
boost::system::error_code error;
|
||||
if (!fs::remove(*cache_path_, error))
|
||||
{
|
||||
TC_LOG_ERROR("scripts.hotswap", "Failed to delete the cached shared library \"%s\" (%s).",
|
||||
cache_path_->generic_string().c_str(), error.message().c_str());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
TC_LOG_TRACE("scripts.hotswap", "Lazy unloaded the shared library \"%s\" "
|
||||
"and deleted it's cached version at \"%s\"",
|
||||
path_.generic_string().c_str(), cache_path_->generic_string().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
TC_LOG_ERROR("scripts.hotswap", "Failed to delete the shared library \"%s\" (%s).",
|
||||
_path.generic_string().c_str(), error.message().c_str());
|
||||
TC_LOG_TRACE("scripts.hotswap", "Lazy unloaded the shared library \"%s\".",
|
||||
path_.generic_string().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
fs::path _path;
|
||||
fs::path const path_;
|
||||
Optional<fs::path> const cache_path_;
|
||||
};
|
||||
|
||||
typedef std::unique_ptr<typename std::remove_pointer<HandleType>::type, SharedLibraryUnloader> HandleHolder;
|
||||
@@ -165,7 +179,8 @@ public:
|
||||
ScriptModule& operator= (ScriptModule const&) = delete;
|
||||
ScriptModule& operator= (ScriptModule&& right) = delete;
|
||||
|
||||
static Optional<std::shared_ptr<ScriptModule>> CreateFromPath(fs::path const& path);
|
||||
static Optional<std::shared_ptr<ScriptModule>>
|
||||
CreateFromPath(fs::path const& path, Optional<fs::path> cache_path);
|
||||
|
||||
char const* GetScriptModuleRevisionHash() const override
|
||||
{
|
||||
@@ -215,23 +230,41 @@ static bool GetFunctionFromSharedLibrary(HandleType handle, std::string const& n
|
||||
}
|
||||
|
||||
// Load a shared library from the given path.
|
||||
Optional<std::shared_ptr<ScriptModule>> ScriptModule::CreateFromPath(fs::path const& path)
|
||||
Optional<std::shared_ptr<ScriptModule>>
|
||||
ScriptModule::CreateFromPath(fs::path const& path, Optional<fs::path> cache_path)
|
||||
{
|
||||
auto const load_path = [&] () -> fs::path {
|
||||
if (cache_path)
|
||||
return *cache_path;
|
||||
else
|
||||
return path;
|
||||
}();
|
||||
|
||||
#ifdef _WIN32
|
||||
HandleType handle = LoadLibrary(path.generic_string().c_str());
|
||||
HandleType handle = LoadLibrary(load_path.generic_string().c_str());
|
||||
#else // Posix
|
||||
HandleType handle = dlopen(path.c_str(), RTLD_LAZY);
|
||||
HandleType handle = dlopen(load_path.generic_string().c_str(), RTLD_LAZY);
|
||||
#endif
|
||||
|
||||
if (!handle)
|
||||
{
|
||||
TC_LOG_ERROR("scripts.hotswap", "Could not load the shared library \"%s\" for reading.",
|
||||
path.generic_string().c_str());
|
||||
if (cache_path)
|
||||
{
|
||||
TC_LOG_ERROR("scripts.hotswap", "Could not dynamic load the shared library \"%s\" "
|
||||
"(the library is cached at %s)",
|
||||
path.generic_string().c_str(), cache_path->generic_string().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
TC_LOG_ERROR("scripts.hotswap", "Could not dynamic load the shared library \"%s\".",
|
||||
path.generic_string().c_str());
|
||||
}
|
||||
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
// Use RAII to release the library on failure.
|
||||
HandleHolder holder(handle, SharedLibraryUnloader(path));
|
||||
HandleHolder holder(handle, SharedLibraryUnloader(path, std::move(cache_path)));
|
||||
|
||||
GetScriptModuleRevisionHashType getScriptModuleRevisionHash;
|
||||
AddScriptsType addScripts;
|
||||
@@ -357,7 +390,7 @@ static bool IsDebuggerBlockingRebuild()
|
||||
///
|
||||
/// This class manages shared library loading/unloading through watching
|
||||
/// the script module directory. Loaded shared libraries are mirrored
|
||||
/// into a .cache subdirectory to allow lazy unloading as long as
|
||||
/// into a cache subdirectory to allow lazy unloading as long as
|
||||
/// the shared library is still used which is useful for scripts
|
||||
/// which can't be instantly replaced like spells or instances.
|
||||
/// Several modules which reference different versions can be kept loaded
|
||||
@@ -545,28 +578,28 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Get the cache directory path
|
||||
fs::path const cache_path = []
|
||||
{
|
||||
auto path = fs::absolute(sScriptReloadMgr->GetLibraryDirectory());
|
||||
path /= ".cache";
|
||||
return path;
|
||||
}();
|
||||
#ifdef HOTSWAP_PLATFORM_REQUIRES_CACHING
|
||||
|
||||
temporary_cache_path_ = CalculateTemporaryCachePath();
|
||||
|
||||
// We use the boost filesystem function versions which accept
|
||||
// an error code to prevent it from throwing exceptions.
|
||||
boost::system::error_code code;
|
||||
if ((!fs::exists(cache_path, code) || (fs::remove_all(cache_path, code) > 0)) &&
|
||||
!fs::create_directory(cache_path, code))
|
||||
if ((!fs::exists(temporary_cache_path_, code)
|
||||
|| (fs::remove_all(temporary_cache_path_, code) > 0)) &&
|
||||
!fs::create_directory(temporary_cache_path_, code))
|
||||
{
|
||||
TC_LOG_ERROR("scripts.hotswap", "Couldn't create the cache directory \"%s\".",
|
||||
cache_path.generic_string().c_str());
|
||||
TC_LOG_ERROR("scripts.hotswap", "Couldn't create the cache directory at \"%s\".",
|
||||
temporary_cache_path_.generic_string().c_str());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Used to silent compiler warnings
|
||||
(void)code;
|
||||
|
||||
#endif // #ifdef HOTSWAP_PLATFORM_REQUIRES_CACHING
|
||||
|
||||
// Correct the CMake prefix when needed
|
||||
if (sWorld->getBoolConfig(CONFIG_HOTSWAP_PREFIX_CORRECTION_ENABLED))
|
||||
DoCMakePrefixCorrectionIfNeeded();
|
||||
@@ -698,6 +731,32 @@ private:
|
||||
_fileWatcher.watch();
|
||||
}
|
||||
|
||||
static fs::path CalculateTemporaryCachePath()
|
||||
{
|
||||
auto path = fs::temp_directory_path();
|
||||
|
||||
path /= Trinity::StringFormat("tc_script_cache_%s_%s",
|
||||
GitRevision::GetBranch(),
|
||||
CalculateSHA1Hash(sConfigMgr->GetFilename()).c_str());
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
fs::path GenerateUniquePathForLibraryInCache(fs::path path)
|
||||
{
|
||||
ASSERT(!temporary_cache_path_.empty(),
|
||||
"The temporary cache path wasn't set!");
|
||||
|
||||
// Create the cache path and increment the library counter to use an unique name for each library
|
||||
auto cache_path = temporary_cache_path_;
|
||||
cache_path /= Trinity::StringFormat("%s.%u%s",
|
||||
path.stem().generic_string().c_str(),
|
||||
_unique_library_name_counter++,
|
||||
path.extension().generic_string().c_str());
|
||||
|
||||
return cache_path;
|
||||
}
|
||||
|
||||
/// Updates the current state of the given source path
|
||||
void UpdateSourceChangeRequest(std::string const& module_name,
|
||||
fs::path const& path,
|
||||
@@ -778,31 +837,37 @@ private:
|
||||
ASSERT(_running_script_module_names.find(path) == _running_script_module_names.end(),
|
||||
"Can't load a module which is running already!");
|
||||
|
||||
// Create the cache path and increment the library counter to use an unique name for each library
|
||||
fs::path cache_path = fs::absolute(sScriptReloadMgr->GetLibraryDirectory());
|
||||
cache_path /= ".cache";
|
||||
cache_path /= Trinity::StringFormat("%s.%u%s",
|
||||
path.stem().generic_string().c_str(),
|
||||
_unique_library_name_counter++,
|
||||
path.extension().generic_string().c_str());
|
||||
Optional<fs::path> cache_path;
|
||||
|
||||
#ifdef HOTSWAP_PLATFORM_REQUIRES_CACHING
|
||||
|
||||
// Copy the shared library into a cache on platforms which lock files on use (windows).
|
||||
cache_path = GenerateUniquePathForLibraryInCache(path);
|
||||
|
||||
boost::system::error_code code;
|
||||
fs::copy_file(path, cache_path, fs::copy_option::fail_if_exists, code);
|
||||
if (code)
|
||||
{
|
||||
TC_LOG_FATAL("scripts.hotswap", ">> Failed to create cache entry for module "
|
||||
"\"%s\" at \"%s\" with reason (\"%s\")!",
|
||||
path.filename().generic_string().c_str(), cache_path.generic_string().c_str(),
|
||||
code.message().c_str());
|
||||
boost::system::error_code code;
|
||||
fs::copy_file(path, *cache_path, fs::copy_option::fail_if_exists, code);
|
||||
if (code)
|
||||
{
|
||||
TC_LOG_FATAL("scripts.hotswap", ">> Failed to create cache entry for module "
|
||||
"\"%s\" at \"%s\" with reason (\"%s\")!",
|
||||
path.filename().generic_string().c_str(), cache_path->generic_string().c_str(),
|
||||
code.message().c_str());
|
||||
|
||||
// Find a better solution for this but it's much better
|
||||
// to start the core without scripts
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
ABORT();
|
||||
return;
|
||||
// Find a better solution for this but it's much better
|
||||
// to start the core without scripts
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
ABORT();
|
||||
return;
|
||||
}
|
||||
|
||||
TC_LOG_TRACE("scripts.hotswap", ">> Copied the shared library \"%s\" to \"%s\" for caching.",
|
||||
path.filename().generic_string().c_str(), cache_path->generic_string().c_str());
|
||||
}
|
||||
|
||||
auto module = ScriptModule::CreateFromPath(cache_path);
|
||||
#endif // #ifdef HOTSWAP_PLATFORM_REQUIRES_CACHING
|
||||
|
||||
auto module = ScriptModule::CreateFromPath(path, cache_path);
|
||||
if (!module)
|
||||
{
|
||||
TC_LOG_FATAL("scripts.hotswap", ">> Failed to load script module \"%s\"!",
|
||||
@@ -1348,6 +1413,9 @@ private:
|
||||
// Is true when the build job dispatcher should stop after
|
||||
// the current job has finished
|
||||
bool terminate_early;
|
||||
|
||||
// The path to the tc_scripts temporary cache
|
||||
fs::path temporary_cache_path_;
|
||||
};
|
||||
|
||||
/// Maps efsw actions to strings
|
||||
|
||||
Reference in New Issue
Block a user