Cogs.Core
FileSystemWatcher.Linux.cpp
1#include "FileSystemWatcher.h"
2
3#include "IO.h"
4
5#include <stdio.h>
6#include <sys/inotify.h>
7#include <unistd.h>
8
9#include <chrono>
10#include <mutex>
11#include <string>
12#include <thread>
13#include <unordered_map>
14
15using namespace std::chrono;
16
17namespace Cogs {
18 struct Item {
19 Item(const FileSystemWatcher::CallbackFn& callback, int descriptor, const std::string& filename) : descriptor(descriptor) {
20 callbacks.push_back(callback);
21 fileTime = IO::getFileTime(filename);
22 }
23
24 FileSystemWatcher::CallbackList callbacks;
25 fs::file_time_type fileTime;
26 int descriptor;
27 };
28
29 struct Storage {
30 typedef std::unordered_map<std::string, Item> ItemMap;
31
32 int notifyHandle = -1;
33 std::mutex mutex;
34 ItemMap items;
35 std::thread watcherThread;
36 bool volatile watcherDone = false;
37
38 ItemMap::const_iterator FindItem(int wd) const {
39 auto e = items.end();
40
41 for (auto i = items.begin(); i != e; ++i) {
42 if (i->second.descriptor == wd) {
43 return i;
44 }
45 }
46 return e;
47 }
48 };
49}
50
51Cogs::FileSystemWatcher::FileSystemWatcher() {
52 storage = new Storage();
53 storage->notifyHandle = inotify_init1(IN_NONBLOCK);
54
55 if (storage->notifyHandle == -1) {
56 // ...
57 return;
58 }
59
60 storage->watcherThread = std::thread([this]{
61 uint8_t buffer[sizeof(inotify_event)];
62 inotify_event* e = reinterpret_cast<inotify_event*>(buffer);
63 size_t bytesread = 0;
64
65 while (!storage->watcherDone) {
66 int justread = read(storage->notifyHandle, buffer + bytesread, sizeof(inotify_event) - bytesread);
67
68 if (justread == -1) {
69 switch (errno) {
70 case EWOULDBLOCK: {
71 std::this_thread::sleep_for(500ms);
72 break;
73 }
74 default: {
75 bytesread = 0;
76 break;
77 }
78 }
79 }
80 else {
81 bytesread += justread;
82
83 if (bytesread == sizeof(inotify_event)) {
84 switch (e->mask) {
85 case IN_MODIFY: {
86 std::lock_guard<std::mutex> lock(storage->mutex);
87 auto i = storage->FindItem(e->wd);
88
89 if (i != storage->items.end()) {
90 Event e = { i->first, EventType::FileUpdated };
91
92 for (auto& callback : i->second.callbacks) {
93 callback(e);
94 }
95 }
96 break;
97 }
98 //case IN_MOVE_SELF:
99 case IN_DELETE_SELF:
100 {
101 std::lock_guard<std::mutex> lock(storage->mutex);
102 auto i = storage->FindItem(e->wd);
103
104 if (i != storage->items.end()) {
105 Event e = { i->first, EventType::FileDeleted };
106
107 for (auto& callback : i->second.callbacks) {
108 callback(e);
109 }
110 inotify_rm_watch(storage->notifyHandle, i->second.descriptor);
111 storage->items.erase(i);
112 }
113 break;
114 }
115 }
116 bytesread = 0;
117 }
118 }
119 }
120 });
121}
122
123Cogs::FileSystemWatcher::~FileSystemWatcher() {
124 if (storage->watcherThread.joinable()) {
125 storage->watcherDone = true;
126 storage->watcherThread.join();
127 }
128 close(storage->notifyHandle);
129 delete storage;
130}
131
132bool Cogs::FileSystemWatcher::watchFile(const StringView& path, const CallbackFn& callback) {
133 const auto absoluteFilePath = IO::absolute(path.to_string());
134
135 if (IO::exists(absoluteFilePath)) {
136 std::lock_guard<std::mutex> lock(storage->mutex);
137 auto i = storage->items.find(absoluteFilePath);
138
139 if (i == storage->items.end()) {
140 int descriptor = inotify_add_watch(storage->notifyHandle, absoluteFilePath.c_str(), IN_MODIFY | IN_MOVE_SELF | IN_DELETE_SELF);
141
142 if (descriptor >= 0) {
143 storage->items.emplace(absoluteFilePath, Item(callback, descriptor, absoluteFilePath));
144 return true;
145 }
146 }
147 else {
148 i->second.callbacks.push_back(callback);
149 return true;
150 }
151 }
152 return false;
153}
154
155bool Cogs::FileSystemWatcher::unwatchFile(const StringView& path) {
156 const auto absoluteFilePath = IO::absolute(path.to_string());
157 std::lock_guard<std::mutex> lock(storage->mutex);
158 auto i = storage->items.find(absoluteFilePath);
159
160 if (i != storage->items.end()) {
161 inotify_rm_watch(storage->notifyHandle, i->second.descriptor);
162 storage->items.erase(i);
163 return true;
164 }
165 return false;
166}
Contains all Cogs related functionality.
Definition: FieldSetter.h:23