#pragma once #ifndef UTILITY_H #define UTILITY_H #include "ANSLicense.h" #include #include #include "boost/property_tree/ptree.hpp" #include "boost/property_tree/json_parser.hpp" #include "boost/foreach.hpp" #include #include #include #include #include #include #include #include #include #include //namespace logging = boost::log; //namespace attrs = boost::log::attributes; //namespace expr = boost::log::expressions; //namespace src = boost::log::sources; //namespace keywords = boost::log::keywords; //namespace sinks = boost::log::sinks; namespace fs = std::filesystem; #ifndef MIN # define MIN(a,b) ((a) > (b) ? (b) : (a)) #endif #ifndef MAX # define MAX(a,b) ((a) < (b) ? (b) : (a)) #endif #define DETECT_BUFFER_SIZE 0x9000 ANSLICENSE_API std::string path_to_filename(std::string path); ANSLICENSE_API std::string GetMainFolderDir(); ANSLICENSE_API std::string GetLoggerDir(); ANSLICENSE_API std::string GetLicenseDir(); // Convert file path to only the filename ANSLICENSE_API std::wstring String2WString(const std::string input); ANSLICENSE_API std::string WString2String(const std::wstring input); ANSLICENSE_API std::string Dash2Underscore(std::string text); ANSLICENSE_API std::string Underscore2Dash(std::string text); ANSLICENSE_API std::string Space2Underscore(std::string text); [[nodiscard]] ANSLICENSE_API bool FileExist(const std::string& filePath); [[nodiscard]] ANSLICENSE_API bool FolderExist(const std::string& folderPath); [[nodiscard]] ANSLICENSE_API bool CreateDirectory(const std::string& folderPath); [[nodiscard]] ANSLICENSE_API bool RenameFile(const std::string& oldFilePath, const std::string& newFilePath); [[nodiscard]] ANSLICENSE_API bool CopyFile(const std::string& sourceFilePath, const std::string& destinationFilePath); ANSLICENSE_API void CopyFiles(const std::string& sourceFolder, const std::string& destinationFolder); [[nodiscard]] ANSLICENSE_API bool DeleteFile(const std::string& filePath); [[nodiscard]] ANSLICENSE_API bool DeleteLicenseFile(const std::string& filePath); [[nodiscard]] ANSLICENSE_API std::vector ListFilesInFolder(const std::string& folderPath); ANSLICENSE_API void DeleteFilesInFolderExcept(const std::string& folderPath, const std::vector& filesToKeep); [[nodiscard]] ANSLICENSE_API bool DeleteFolder(const std::string& folderPath); ANSLICENSE_API void ToLowerCase(std::string& str); ANSLICENSE_API std::string toLower(const std::string& str); ANSLICENSE_API std::string GetFileExtension(const std::string& filePath); ANSLICENSE_API std::string GetFileNameWithoutExtension(const std::string& filePath); ANSLICENSE_API std::string FindFilePathInFileList(const std::vector& fileList, const std::string& fileName); ANSLICENSE_API std::string ReadFileContent(std::string filePath); ANSLICENSE_API std::string ReadFileAsBinary(const std::string& filename); ANSLICENSE_API int ReadAllBytes(std::string filePath, std::vector& allDataBytes); ANSLICENSE_API std::string ExtractFolderName(const std::string& filePath); // Function to extract the folder name from a file path or file name ANSLICENSE_API std::string GetParentFolder(const std::string& directoryPath);// Function to extract the folder name from a file path or file name ANSLICENSE_API std::string CreateFilePath(const std::string& folderName, const std::string& fileName);// Function to create a file path from a folder name and a file name ANSLICENSE_API std::filesystem::path GetSafeTempDirectory(); ANSLICENSE_API std::string GenerateOutputFolder(std::string modelFilePath, std::string modelName, bool edgeDeviceModel=true); ANSLICENSE_API bool ExtractPasswordProtectedZip(const std::string& zipFileName, const std::string& password, const std::string& modelName, std::string& outputFolder, bool edgeDeviceModel = true); ANSLICENSE_API bool AddFileToZip(zip* archive, const char* filePath, const char* zipPath, const char* password); ANSLICENSE_API bool AddFolderContentsToZip(zip* archive, const char* folderPath, const char* zipPath, const char* password); ANSLICENSE_API bool ZipFolderWithPassword(const char* folderPath, const char* zipFilePath, const char* password); ANSLICENSE_API std::string VectorToCommaSeparatedString(const std::vector& inputVector); ANSLICENSE_API std::string VectorToSeparatedString(const std::vector& inputVector); ANSLICENSE_API std::vector Split(const std::string str, const std::string regex_str); ANSLICENSE_API bool ReadLicenseKeyFile(std::string licenseKeyFilePath, std::string& licenseKey, std::string& activationKey, std::string& validationData); ANSLICENSE_API std::vector GetTrialLicenseKeyFiles(std::string registrationName); ANSLICENSE_API std::string GetTrialLicenseKey(std::string registrationName); ANSLICENSE_API bool UpdateTrialLicenseKey(std::string trialLicenseKey, std::string registrationName); ANSLICENSE_API int GetExpirationDate(std::string licenseData, int& productId); ANSLICENSE_API bool RemoveTrialLicenseKey(std::string registrationName); ANSLICENSE_API std::string GetStringCurrentDateTime(); // Return current datetime in string format ANSLICENSE_API std::string GetDateTimeString(const std::string& format); // For training engine //bool ExtractPasswordProtectedZipForTrainingEgnine(const std::string& zipFileName, const std::string& password, const std::string& modelName, std::string& outputFolder, bool edgeDeviceModel = true); ANSLICENSE_API bool ExtractProtectedZipFile(const std::string& zipFileName,const std::string& password,const std::string& modelName,const std::string outputFolder); // Per-path mutex for a model folder. Used to serialize extract ↔ session // creation on the same extracted folder so concurrent CreateANSODHandle calls // cannot truncate/rewrite a model file while another thread is loading it. // Keyed by folder path (not zip path) so both extractor and consumer agree. // Returns std::timed_mutex so callers can bound their wait and avoid a hang // if a peer thread deadlocks inside extraction or ORT session creation. ANSLICENSE_API std::shared_ptr GetModelFolderLock(const std::string& folderPath); // ============================================================================ // ModelFolderLockGuard // // RAII guard that serializes "open files in an extracted model folder" against // re-entries into ExtractProtectedZipFile on the SAME folder. Without this, // two threads creating handles for the same model zip can race so that thread // A opens a model file (via ORT / TRT / OpenVINO) while thread B truncates it // via std::ofstream inside the extractor — Windows surfaces this as "system // error number 13" (EACCES) on the reader. // // Backed by GetModelFolderLock() above which returns a process-wide // std::timed_mutex keyed on the folder path. The extractor takes the same // lock, so extract ↔ open is mutually exclusive. // // Acquisition is bounded by `timeout` (default 120 s) so a deadlocked peer // cannot hang the caller thread forever. On timeout, .acquired() is false and // the caller must fail the load. // // Lives in Utility.h (rather than a per-module header) so every ANSCORE // module — ANSODEngine, ANSOCR, ANSLPR, ANSFR, etc. — can use it without // pulling in ANSODEngine headers. // // Usage: // // bool MyEngine::LoadModelFromFolder(...) { // bool result = MyBase::LoadModelFromFolder(...); // if (!result) return false; // // ── serialize derived-class init against concurrent extracts ── // ANSCENTER::ModelFolderLockGuard _flg(_modelFolder, // "MyEngine::LoadModelFromFolder"); // if (!_flg.acquired()) { // _logger.LogError("MyEngine::LoadModelFromFolder", // "Timed out waiting for model-folder lock: " + _modelFolder, // __FILE__, __LINE__); // return false; // } // // ... existing body: Init(...) / buildLoadNetwork(...) / etc. ... // } // // Notes: // • Placement — insert AFTER the base's XXX() returns (extractor already // released its own lock by then) and BEFORE any file open in _modelFolder. // Wrapping the base call would deadlock — it takes the same lock itself. // • RAII — destructor auto-releases on every return path within the scope. // • Timing — entry/acquire/timeout are traced via ANS_DBG("EngineLoad"), // filter on [EngineLoad] in DebugView to diagnose stalls. // ============================================================================ namespace ANSCENTER { class ModelFolderLockGuard { public: explicit ModelFolderLockGuard(const std::string& folderPath, const char* caller, std::chrono::seconds timeout = std::chrono::seconds(120)) : _caller(caller ? caller : "(?)"), _folder(folderPath) { if (folderPath.empty()) { // Nothing to serialize — no extracted folder yet. Treat as // acquired so caller's existing init runs unchanged (e.g. // custom-path engines that never go through the zip extractor). _ok = true; ANS_DBG("EngineLoad", "%s: empty folder path, skipping lock", _caller); return; } auto lock = GetModelFolderLock(folderPath); _guard = std::unique_lock(*lock, std::defer_lock); ANS_DBG("EngineLoad", "%s: waiting on folder lock (%llds): %s", _caller, (long long)timeout.count(), folderPath.c_str()); const auto t0 = std::chrono::steady_clock::now(); if (_guard.try_lock_for(timeout)) { const auto ms = std::chrono::duration_cast( std::chrono::steady_clock::now() - t0).count(); _ok = true; ANS_DBG("EngineLoad", "%s: folder lock acquired in %lldms", _caller, (long long)ms); } else { const auto ms = std::chrono::duration_cast( std::chrono::steady_clock::now() - t0).count(); _ok = false; ANS_DBG("EngineLoad", "%s: TIMEOUT on folder lock after %lldms: %s", _caller, (long long)ms, folderPath.c_str()); } } // Non-copyable, non-movable: lock lifetime is tied to this scope. ModelFolderLockGuard(const ModelFolderLockGuard&) = delete; ModelFolderLockGuard& operator=(const ModelFolderLockGuard&) = delete; bool acquired() const noexcept { return _ok; } explicit operator bool() const noexcept { return _ok; } private: const char* _caller; std::string _folder; std::unique_lock _guard; bool _ok = false; }; } // namespace ANSCENTER #endif