1#include "Logging/Logger.h"
2#include "FileSystemWatcher.h"
6#define DR_FSW_IMPLEMENTATION
7#include <dr_libs/dr_fsw.h>
12#include <unordered_map>
18 std::vector<FileSystemWatcher::CallbackFn> callbacks;
19 fs::file_time_type fileTime;
23 std::atomic<int> usage = {};
28 drfsw_context * fswContext =
nullptr;
30 std::mutex directoriesMutex;
31 std::unordered_map<std::string, DirectoryEntry> directories;
33 std::mutex filesMutex;
34 std::unordered_map<std::string, FileEntry> files;
36 std::thread watcherThread;
37 std::atomic<bool> watcherDone =
false;
41Cogs::FileSystemWatcher::FileSystemWatcher()
43 storage =
new Storage();
44 storage->fswContext = drfsw_create_context();
46 if (!storage->fswContext) {
51 storage->watcherThread = std::thread([
this]{
53 while (!storage->watcherDone && drfsw_next_event(storage->fswContext, &e)) {
55 case drfsw_event_type_updated:
57 const std::string canonicalFilePath = IO::canonical(e.absolutePath);
59 if (std::strcmp(e.absoluteBasePath, e.absolutePath) == 0) {
64 std::unique_lock dirsLock(storage->directoriesMutex);
65 if (auto dirIt = storage->directories.find(canonicalFilePath); dirIt != storage->directories.end()) {
66 DirectoryEntry& dirEntry = dirIt->second;
69 if (!dirEntry.initial) {
73 dirEntry.initial = false;
77 const size_t N = canonicalFilePath.length();
79 std::lock_guard filesLock(storage->filesMutex);
80 for (auto& fileIt : storage->files) {
81 const std::string& filePath = fileIt.first;
84 if (N <= canonicalFilePath.size() && (std::strncmp(canonicalFilePath.c_str(), filePath.c_str(), N) == 0)) {
87 FileEntry& fileEntry = fileIt.second;
88 IO::FileTimeType fileTime = IO::getFileTime(filePath);
89 if (fileEntry.fileTime != fileTime) {
90 fileEntry.fileTime = fileTime;
93 LOG_DEBUG(logger,
"Detected modification between watcher issued and operational: %s", filePath.c_str());
94 FileSystemWatcher::Event args = { filePath, FileSystemWatcher::EventType::FileUpdated };
95 for (auto& callback : fileEntry.callbacks) {
108 std::lock_guard filesLock(storage->filesMutex);
109 if (auto cbIt = storage->files.find(canonicalFilePath); cbIt != storage->files.end()) {
110 FileEntry& fileEntry = cbIt->second;
112 IO::FileTimeType fileTime = IO::getFileTime(canonicalFilePath);
113 if (fileEntry.fileTime != fileTime) {
114 fileEntry.fileTime = fileTime;
116 LOG_DEBUG(logger,
"Detected modification: %s", canonicalFilePath.c_str());
118 FileSystemWatcher::Event args = { cbIt->first, FileSystemWatcher::EventType::FileUpdated };
119 for (auto& callback : fileEntry.callbacks) {
135Cogs::FileSystemWatcher::~FileSystemWatcher()
137 if (storage->fswContext) {
139 auto fswContext = storage->fswContext;
140 storage->fswContext =
nullptr;
141 drfsw_delete_context(fswContext);
144 if (storage->watcherThread.joinable()) {
145 storage->watcherDone =
true;
146 storage->watcherThread.join();
152bool Cogs::FileSystemWatcher::watchFile(
const StringView& path,
const CallbackFn& callback)
154 const std::string absoluteFilePath = IO::absolute(path);
156 if (!IO::exists(absoluteFilePath))
return false;
158 const std::string canonicalPath = IO::canonical(absoluteFilePath);
159 const std::string directoryPath = IO::parentPath(canonicalPath);
162 std::lock_guard lock(storage->filesMutex);
166 auto it = storage->files.insert({ canonicalPath, {{}, IO::getFileTime(canonicalPath) } });
167 it.first->second.callbacks.emplace_back(callback);
172 std::lock_guard lock(storage->directoriesMutex);
173 if (
auto found = storage->directories.find(directoryPath); found == storage->directories.end()) {
174 storage->directories[directoryPath].usage++;
175 drfsw_add_directory(storage->fswContext, directoryPath.c_str());
183bool Cogs::FileSystemWatcher::unwatchFile(
const StringView & path)
185 const std::string absoluteFilePath = IO::absolute(path);
187 if (!IO::exists(absoluteFilePath))
return false;
189 const std::string canonicalPath = IO::canonical(path);
190 const std::string directoryPath = IO::parentPath(canonicalPath);
193 std::lock_guard lock(storage->directoriesMutex);
195 auto found = storage->directories.find(directoryPath);
197 if (found == storage->directories.end()) {
201 if (found->second.usage.fetch_sub(1) == 1) {
202 drfsw_remove_directory(storage->fswContext, directoryPath.c_str());
204 storage->directories.erase(found);
208 std::lock_guard lock(storage->filesMutex);
209 if (
auto fileIt = storage->files.find(absoluteFilePath); fileIt != storage->files.end()) {
210 storage->files.erase(fileIt);
Log implementation class.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Contains all Cogs related functionality.