209 lines
11 KiB
C++
209 lines
11 KiB
C++
#pragma once
|
|
#ifndef UTILITY_H
|
|
#define UTILITY_H
|
|
#include "ANSLicense.h"
|
|
#include <boost/date_time.hpp>
|
|
#include <boost/date_time/gregorian/gregorian.hpp>
|
|
#include "boost/property_tree/ptree.hpp"
|
|
#include "boost/property_tree/json_parser.hpp"
|
|
#include "boost/foreach.hpp"
|
|
|
|
#include <zlib.h>
|
|
#include <zip.h>
|
|
#include <filesystem>
|
|
#include <cstring>
|
|
#include <iostream>
|
|
#include <vector>
|
|
#include <regex>
|
|
#include <stdio.h>
|
|
#include <memory>
|
|
#include <mutex>
|
|
|
|
//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<std::string> ListFilesInFolder(const std::string& folderPath);
|
|
ANSLICENSE_API void DeleteFilesInFolderExcept(const std::string& folderPath, const std::vector<std::string>& 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<std::string>& 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<char>& 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<std::string>& inputVector);
|
|
ANSLICENSE_API std::string VectorToSeparatedString(const std::vector<std::string>& inputVector);
|
|
ANSLICENSE_API std::vector<std::string> 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<std::string> 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::recursive_timed_mutex so callers can bound their wait and
|
|
// recursion — layered load paths (e.g. ANSALPR_OD::LoadEngine ->
|
|
// ANSONNXYOLO::LoadModelFromFolder) legitimately re-enter on the same
|
|
// thread; a non-recursive timed_mutex self-deadlocks that nesting. Cross-
|
|
// thread serialization is unchanged.
|
|
ANSLICENSE_API std::shared_ptr<std::recursive_timed_mutex> 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::recursive_timed_mutex keyed on the folder path. The extractor takes
|
|
// the same lock, so extract ↔ open is mutually exclusive across threads,
|
|
// while same-thread re-entry (layered loaders) is permitted without
|
|
// deadlocking.
|
|
//
|
|
// 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<std::recursive_timed_mutex>(*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::milliseconds>(
|
|
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::milliseconds>(
|
|
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<std::recursive_timed_mutex> _guard;
|
|
bool _ok = false;
|
|
};
|
|
} // namespace ANSCENTER
|
|
|
|
#endif |