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:
1 parent
7a827fe435
commit
2ec212fdd1
1 file changed
+119
-51
@@ -56,6 +56,7 @@ ScriptReloadMgr* ScriptReloadMgr::instance()
|
|||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "BuiltInConfig.h"
|
#include "BuiltInConfig.h"
|
||||||
#include "ScriptMgr.h"
|
#include "ScriptMgr.h"
|
||||||
|
#include "SHA1.h"
|
||||||
#include "StartProcess.h"
|
#include "StartProcess.h"
|
||||||
#include "MPSCQueue.h"
|
#include "MPSCQueue.h"
|
||||||
#include "GitRevision.h"
|
#include "GitRevision.h"
|
||||||
@@ -64,8 +65,10 @@ namespace fs = boost::filesystem;
|
|||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#define HOTSWAP_PLATFORM_REQUIRES_CACHING
|
||||||
#else // Posix
|
#else // Posix
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
// #define HOTSWAP_PLATFORM_REQUIRES_CACHING
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Promote the sScriptReloadMgr to a HotSwapScriptReloadMgr
|
// Promote the sScriptReloadMgr to a HotSwapScriptReloadMgr
|
||||||
@@ -102,10 +105,10 @@ typedef void* HandleType;
|
|||||||
class SharedLibraryUnloader
|
class SharedLibraryUnloader
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SharedLibraryUnloader()
|
explicit SharedLibraryUnloader(fs::path path)
|
||||||
: _path() { }
|
: path_(std::move(path)) { }
|
||||||
explicit SharedLibraryUnloader(fs::path const& path)
|
SharedLibraryUnloader(fs::path path, Optional<fs::path> cache_path)
|
||||||
: _path(path) { }
|
: path_(std::move(path)), cache_path_(std::move(cache_path)) { }
|
||||||
|
|
||||||
void operator() (HandleType handle) const
|
void operator() (HandleType handle) const
|
||||||
{
|
{
|
||||||
@@ -119,26 +122,37 @@ public:
|
|||||||
if (!success)
|
if (!success)
|
||||||
{
|
{
|
||||||
TC_LOG_ERROR("scripts.hotswap", "Failed to unload (syscall) the shared library \"%s\".",
|
TC_LOG_ERROR("scripts.hotswap", "Failed to unload (syscall) the shared library \"%s\".",
|
||||||
_path.generic_string().c_str());
|
path_.generic_string().c_str());
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::system::error_code error;
|
/// When the shared library was cached delete it's shared version
|
||||||
if (fs::remove(_path, error))
|
if (cache_path_)
|
||||||
{
|
{
|
||||||
TC_LOG_TRACE("scripts.hotswap", "Lazy unloaded and deleted the shared library \"%s\".",
|
boost::system::error_code error;
|
||||||
_path.generic_string().c_str());
|
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
|
else
|
||||||
{
|
{
|
||||||
TC_LOG_ERROR("scripts.hotswap", "Failed to delete the shared library \"%s\" (%s).",
|
TC_LOG_TRACE("scripts.hotswap", "Lazy unloaded the shared library \"%s\".",
|
||||||
_path.generic_string().c_str(), error.message().c_str());
|
path_.generic_string().c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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;
|
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 const&) = delete;
|
||||||
ScriptModule& operator= (ScriptModule&& right) = 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
|
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.
|
// 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
|
#ifdef _WIN32
|
||||||
HandleType handle = LoadLibrary(path.generic_string().c_str());
|
HandleType handle = LoadLibrary(load_path.generic_string().c_str());
|
||||||
#else // Posix
|
#else // Posix
|
||||||
HandleType handle = dlopen(path.c_str(), RTLD_LAZY);
|
HandleType handle = dlopen(load_path.generic_string().c_str(), RTLD_LAZY);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!handle)
|
if (!handle)
|
||||||
{
|
{
|
||||||
TC_LOG_ERROR("scripts.hotswap", "Could not load the shared library \"%s\" for reading.",
|
if (cache_path)
|
||||||
path.generic_string().c_str());
|
{
|
||||||
|
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;
|
return boost::none;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use RAII to release the library on failure.
|
// Use RAII to release the library on failure.
|
||||||
HandleHolder holder(handle, SharedLibraryUnloader(path));
|
HandleHolder holder(handle, SharedLibraryUnloader(path, std::move(cache_path)));
|
||||||
|
|
||||||
GetScriptModuleRevisionHashType getScriptModuleRevisionHash;
|
GetScriptModuleRevisionHashType getScriptModuleRevisionHash;
|
||||||
AddScriptsType addScripts;
|
AddScriptsType addScripts;
|
||||||
@@ -366,7 +399,7 @@ static bool IsDebuggerBlockingRebuild()
|
|||||||
///
|
///
|
||||||
/// This class manages shared library loading/unloading through watching
|
/// This class manages shared library loading/unloading through watching
|
||||||
/// the script module directory. Loaded shared libraries are mirrored
|
/// 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
|
/// the shared library is still used which is useful for scripts
|
||||||
/// which can't be instantly replaced like spells or instances.
|
/// which can't be instantly replaced like spells or instances.
|
||||||
/// Several modules which reference different versions can be kept loaded
|
/// Several modules which reference different versions can be kept loaded
|
||||||
@@ -543,28 +576,28 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the cache directory path
|
#ifdef HOTSWAP_PLATFORM_REQUIRES_CACHING
|
||||||
fs::path const cache_path = []
|
|
||||||
{
|
temporary_cache_path_ = CalculateTemporaryCachePath();
|
||||||
auto path = fs::absolute(sScriptReloadMgr->GetLibraryDirectory());
|
|
||||||
path /= ".cache";
|
|
||||||
return path;
|
|
||||||
}();
|
|
||||||
|
|
||||||
// We use the boost filesystem function versions which accept
|
// We use the boost filesystem function versions which accept
|
||||||
// an error code to prevent it from throwing exceptions.
|
// an error code to prevent it from throwing exceptions.
|
||||||
boost::system::error_code code;
|
boost::system::error_code code;
|
||||||
if ((!fs::exists(cache_path, code) || (fs::remove_all(cache_path, code) > 0)) &&
|
if ((!fs::exists(temporary_cache_path_, code)
|
||||||
!fs::create_directory(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\".",
|
TC_LOG_ERROR("scripts.hotswap", "Couldn't create the cache directory at \"%s\".",
|
||||||
cache_path.generic_string().c_str());
|
temporary_cache_path_.generic_string().c_str());
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used to silent compiler warnings
|
// Used to silent compiler warnings
|
||||||
(void)code;
|
(void)code;
|
||||||
|
|
||||||
|
#endif // #ifdef HOTSWAP_PLATFORM_REQUIRES_CACHING
|
||||||
|
|
||||||
// Correct the CMake prefix when needed
|
// Correct the CMake prefix when needed
|
||||||
if (sWorld->getBoolConfig(CONFIG_HOTSWAP_PREFIX_CORRECTION_ENABLED))
|
if (sWorld->getBoolConfig(CONFIG_HOTSWAP_PREFIX_CORRECTION_ENABLED))
|
||||||
DoCMakePrefixCorrectionIfNeeded();
|
DoCMakePrefixCorrectionIfNeeded();
|
||||||
@@ -696,6 +729,32 @@ private:
|
|||||||
_fileWatcher.watch();
|
_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
|
/// Updates the current state of the given source path
|
||||||
void UpdateSourceChangeRequest(std::string const& module_name,
|
void UpdateSourceChangeRequest(std::string const& module_name,
|
||||||
fs::path const& path,
|
fs::path const& path,
|
||||||
@@ -776,31 +835,37 @@ private:
|
|||||||
ASSERT(_running_script_module_names.find(path) == _running_script_module_names.end(),
|
ASSERT(_running_script_module_names.find(path) == _running_script_module_names.end(),
|
||||||
"Can't load a module which is running already!");
|
"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
|
Optional<fs::path> cache_path;
|
||||||
fs::path cache_path = fs::absolute(sScriptReloadMgr->GetLibraryDirectory());
|
|
||||||
cache_path /= ".cache";
|
#ifdef HOTSWAP_PLATFORM_REQUIRES_CACHING
|
||||||
cache_path /= Trinity::StringFormat("%s.%u%s",
|
|
||||||
path.stem().generic_string().c_str(),
|
// Copy the shared library into a cache on platforms which lock files on use (windows).
|
||||||
_unique_library_name_counter++,
|
cache_path = GenerateUniquePathForLibraryInCache(path);
|
||||||
path.extension().generic_string().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 "
|
boost::system::error_code code;
|
||||||
"\"%s\" at \"%s\" with reason (\"%s\")!",
|
fs::copy_file(path, *cache_path, fs::copy_option::fail_if_exists, code);
|
||||||
path.filename().generic_string().c_str(), cache_path.generic_string().c_str(),
|
if (code)
|
||||||
code.message().c_str());
|
{
|
||||||
|
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
|
// Find a better solution for this but it's much better
|
||||||
// to start the core without scripts
|
// to start the core without scripts
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||||
ABORT();
|
ABORT();
|
||||||
return;
|
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)
|
if (!module)
|
||||||
{
|
{
|
||||||
TC_LOG_FATAL("scripts.hotswap", ">> Failed to load script module \"%s\"!",
|
TC_LOG_FATAL("scripts.hotswap", ">> Failed to load script module \"%s\"!",
|
||||||
@@ -1344,6 +1409,9 @@ private:
|
|||||||
// Is true when the build job dispatcher should stop after
|
// Is true when the build job dispatcher should stop after
|
||||||
// the current job has finished
|
// the current job has finished
|
||||||
bool terminate_early;
|
bool terminate_early;
|
||||||
|
|
||||||
|
// The path to the tc_scripts temporary cache
|
||||||
|
fs::path temporary_cache_path_;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Maps efsw actions to strings
|
/// Maps efsw actions to strings
|
||||||
|
|||||||
Reference in new issue
Block a user