diff --git a/hecl/blender/BlenderConnection.cpp b/hecl/blender/BlenderConnection.cpp index 6a94981a1..79a2c1e85 100644 --- a/hecl/blender/BlenderConnection.cpp +++ b/hecl/blender/BlenderConnection.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include diff --git a/hecl/blender/BlenderConnection.hpp b/hecl/blender/BlenderConnection.hpp index eb7df41b7..77ff9367e 100644 --- a/hecl/blender/BlenderConnection.hpp +++ b/hecl/blender/BlenderConnection.hpp @@ -68,6 +68,7 @@ private: size_t _writeBuf(const void* buf, size_t len); void _closePipe(); void _blenderDied(); + public: BlenderConnection(int verbosityLevel=1); ~BlenderConnection(); diff --git a/hecl/blender/hecl_blendershell.py b/hecl/blender/hecl_blendershell.py index 06303304d..57d35745b 100644 --- a/hecl/blender/hecl_blendershell.py +++ b/hecl/blender/hecl_blendershell.py @@ -370,6 +370,7 @@ try: elif cmdargs[0] == 'SAVE': bpy.context.user_preferences.filepaths.save_version = 0 + print('SAVING %s' % loaded_blend) if loaded_blend: if 'FINISHED' in bpy.ops.wm.save_as_mainfile(filepath=loaded_blend, check_existing=False, compress=True): writepipeline(b'FINISHED') diff --git a/hecl/include/hecl/hecl.hpp b/hecl/include/hecl/hecl.hpp index 75c6836dc..dee1601ab 100644 --- a/hecl/include/hecl/hecl.hpp +++ b/hecl/include/hecl/hecl.hpp @@ -44,7 +44,6 @@ class Project; struct DataSpecEntry; } - extern unsigned VerbosityLevel; extern logvisor::Module LogModule; @@ -1180,6 +1179,28 @@ public: }; +/** + * @brief Mutex-style centralized resource-path tracking + * + * Provides a means to safely parallelize resource processing; detecting when another + * thread is working on the same resource. + */ +class ResourceLock +{ + static bool SetThreadRes(const ProjectPath& path); + static void ClearThreadRes(); + bool good; +public: + operator bool() const { return good; } + static bool InProgress(const ProjectPath& path); + ResourceLock(const ProjectPath& path) { good = SetThreadRes(path); } + ~ResourceLock() { if (good) ClearThreadRes(); } + ResourceLock(const ResourceLock&) = delete; + ResourceLock& operator=(const ResourceLock&) = delete; + ResourceLock(ResourceLock&&) = delete; + ResourceLock& operator=(ResourceLock&&) = delete; +}; + /** * @brief Search from within provided directory for the project root * @param path absolute or relative file path to search from diff --git a/hecl/lib/hecl.cpp b/hecl/lib/hecl.cpp index ff9eb0ba7..0fcfb8dc4 100644 --- a/hecl/lib/hecl.cpp +++ b/hecl/lib/hecl.cpp @@ -1,4 +1,7 @@ #include "hecl/hecl.hpp" +#include +#include +#include #ifdef WIN32 #include @@ -84,6 +87,38 @@ void SanitizePath(std::wstring& path) }); } +static std::mutex PathsMutex; +static std::unordered_map PathsInProgress; + +bool ResourceLock::InProgress(const ProjectPath& path) +{ + std::unique_lock lk(PathsMutex); + for (const auto& p : PathsInProgress) + if (p.second == path) + return true; + return false; +} + +bool ResourceLock::SetThreadRes(const ProjectPath& path) +{ + std::unique_lock lk(PathsMutex); + if (PathsInProgress.find(std::this_thread::get_id()) != PathsInProgress.cend()) + LogModule.report(logvisor::Fatal, "multiple resource locks on thread"); + + for (const auto& p : PathsInProgress) + if (p.second == path) + return false; + + PathsInProgress[std::this_thread::get_id()] = path; + return true; +} + +void ResourceLock::ClearThreadRes() +{ + std::unique_lock lk(PathsMutex); + PathsInProgress.erase(std::this_thread::get_id()); +} + bool IsPathPNG(const hecl::ProjectPath& path) { FILE* fp = hecl::Fopen(path.getAbsolutePath().c_str(), _S("rb"));