Skip to content
This repository was archived by the owner on Jul 4, 2025. It is now read-only.

Commit 8c4b046

Browse files
committed
fix linux
Signed-off-by: James <namnh0122@gmail.com>
1 parent a1a7ce2 commit 8c4b046

File tree

1 file changed

+96
-26
lines changed

1 file changed

+96
-26
lines changed

engine/services/file_watcher_service.h

Lines changed: 96 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <windows.h>
1414

1515
#else // Linux
16+
#include <poll.h>
1617
#include <limits.h>
1718
#include <sys/inotify.h>
1819
#include <unistd.h>
@@ -21,7 +22,7 @@
2122
class FileWatcherService {
2223
private:
2324
#if defined(_WIN32)
24-
HANDLE dir_handle;
25+
HANDLE dir_handle = INVALID_HANDLE_VALUE
2526
HANDLE stop_event;
2627
#elif defined(__APPLE__)
2728
FSEventStreamRef event_stream;
@@ -34,7 +35,7 @@ class FileWatcherService {
3435
public:
3536
FileWatcherService(const std::string& path,
3637
std::shared_ptr<ModelService> model_service)
37-
: watch_path_{path}, running_{false} {
38+
: watch_path_{path}, running_{false}, model_service_{model_service} {
3839
if (!std::filesystem::exists(path)) {
3940
throw std::runtime_error("Path does not exist: " + path);
4041
}
@@ -75,11 +76,12 @@ class FileWatcherService {
7576
}
7677
#else // Linux
7778
// For Linux, closing the fd will interrupt the read() call
79+
CTL_INF("before close fd!");
7880
if (fd >= 0) {
7981
close(fd);
8082
}
8183
#endif
82-
84+
CTL_INF("before join!");
8385
// Add timeout to avoid infinite waiting
8486
if (watch_thread_.joinable()) {
8587
watch_thread_.join();
@@ -216,23 +218,36 @@ class FileWatcherService {
216218
#else // Linux
217219

218220
void AddWatch(const std::string& dirPath) {
219-
wd = inotify_add_watch(fd, dirPath.c_str(),
220-
IN_DELETE | IN_CREATE | IN_DELETE_SELF);
221+
const int watch_flags = IN_DELETE | IN_DELETE_SELF | IN_CREATE;
222+
wd = inotify_add_watch(fd, dirPath.c_str(), watch_flags);
221223
if (wd < 0) {
222-
throw std::runtime_error("Failed to add watch on: " + dirPath);
224+
throw std::runtime_error("Failed to add watch on " + dirPath +
225+
": " + std::string(strerror(errno)));
223226
}
224227
watch_descriptors[wd] = dirPath;
225228

226-
// Recursively add watches to subdirectories
227-
for (const auto& entry :
228-
std::filesystem::recursive_directory_iterator(dirPath)) {
229-
if (std::filesystem::is_directory(entry)) {
230-
AddWatch(entry.path().string());
229+
// Add watches for subdirectories
230+
try {
231+
for (const auto& entry :
232+
std::filesystem::recursive_directory_iterator(dirPath)) {
233+
if (std::filesystem::is_directory(entry)) {
234+
int subwd = inotify_add_watch(fd, entry.path().c_str(), watch_flags);
235+
if (subwd >= 0) {
236+
watch_descriptors[subwd] = entry.path().string();
237+
} else {
238+
CTL_ERR("Failed to add watch for subdirectory " +
239+
entry.path().string() + ": " +
240+
std::string(strerror(errno)));
241+
}
242+
}
231243
}
244+
} catch (const std::filesystem::filesystem_error& e) {
245+
CTL_ERR("Error walking directory tree: " + std::string(e.what()));
232246
}
233247
}
234248

235249
void CleanupWatches() {
250+
CTL_INF("Cleanup Watches");
236251
for (const auto& [wd, path] : watch_descriptors) {
237252
inotify_rm_watch(fd, wd);
238253
}
@@ -245,33 +260,88 @@ class FileWatcherService {
245260
}
246261

247262
void WatcherThread() {
248-
fd = inotify_init();
263+
fd = inotify_init1(IN_NONBLOCK);
249264
if (fd < 0) {
250-
throw std::runtime_error("Failed to initialize inotify");
265+
CTL_ERR("Failed to initialize inotify: " + std::string(strerror(errno)));
266+
return;
251267
}
252268

253-
// Add initial watch on the main directory
254-
AddWatch(watch_path_);
269+
try {
270+
AddWatch(watch_path_);
271+
} catch (const std::exception& e) {
272+
CTL_ERR("Failed to add watch: " + std::string(e.what()));
273+
close(fd);
274+
return;
275+
}
255276

277+
const int POLL_TIMEOUT_MS = 1000; // 1 second timeout
256278
char buffer[4096];
279+
struct pollfd pfd = {
280+
.fd = fd,
281+
.events = POLLIN,
282+
.revents = 0
283+
};
284+
257285
while (running_) {
258-
int length = read(fd, buffer, sizeof(buffer));
259-
if (length < 0) {
286+
// Poll will sleep until either:
287+
// 1. Events are available (POLLIN)
288+
// 2. POLL_TIMEOUT_MS milliseconds have elapsed
289+
// 3. An error occurs
290+
int poll_result = poll(&pfd, 1, POLL_TIMEOUT_MS);
291+
292+
if (poll_result < 0) {
293+
if (errno == EINTR) {
294+
// System call was interrupted, just retry
295+
continue;
296+
}
297+
CTL_ERR("Poll failed: " + std::string(strerror(errno)));
298+
break;
299+
}
300+
301+
if (poll_result == 0) { // Timeout - no events
302+
// No need to sleep - poll() already waited
260303
continue;
261304
}
262305

263-
int i = 0;
264-
while (i < length) {
265-
struct inotify_event* event = (struct inotify_event*)&buffer[i];
266-
if (event->mask & IN_DELETE) {
267-
auto deletedPath = watch_descriptors[event->wd] + "/" + event->name;
268-
model_service_->ForceIndexingModelList();
306+
if (pfd.revents & POLLERR || pfd.revents & POLLNVAL) {
307+
CTL_ERR("Poll error on fd");
308+
break;
309+
}
310+
311+
// Read all pending events
312+
while (running_) {
313+
int length = read(fd, buffer, sizeof(buffer));
314+
if (length < 0) {
315+
if (errno == EAGAIN || errno == EWOULDBLOCK) {
316+
// No more events to read
317+
break;
318+
}
319+
CTL_ERR("Read error: " + std::string(strerror(errno)));
320+
break;
321+
}
322+
323+
if (length == 0) {
324+
break;
325+
}
326+
327+
// Process events
328+
size_t i = 0;
329+
while (i < static_cast<size_t>(length)) {
330+
struct inotify_event* event =
331+
reinterpret_cast<struct inotify_event*>(&buffer[i]);
332+
333+
if (event->mask & (IN_DELETE | IN_DELETE_SELF)) {
334+
try {
335+
model_service_->ForceIndexingModelList();
336+
} catch (const std::exception& e) {
337+
CTL_ERR("Error processing delete event: " + std::string(e.what()));
338+
}
339+
}
340+
341+
i += sizeof(struct inotify_event) + event->len;
269342
}
270-
i += sizeof(struct inotify_event) + event->len;
271343
}
272344
}
273-
274-
close(fd);
275345
}
276346
#endif
277347
};

0 commit comments

Comments
 (0)