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

Commit d23dfbc

Browse files
committed
fix: event listener not work on macos
1 parent a4093b5 commit d23dfbc

File tree

2 files changed

+54
-35
lines changed

2 files changed

+54
-35
lines changed

engine/config/yaml_config.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ void YamlHandler::Reset() {
1818
void YamlHandler::ReadYamlFile(const std::string& file_path) {
1919
namespace fs = std::filesystem;
2020
namespace fmu = file_manager_utils;
21+
2122
try {
2223
yaml_node_ = YAML::LoadFile(file_path);
2324
// incase of model.yml file, we don't have files yet, create them
@@ -41,7 +42,6 @@ void YamlHandler::ReadYamlFile(const std::string& file_path) {
4142
yaml_node_["files"] = v;
4243
}
4344
} catch (const YAML::BadFile& e) {
44-
std::cerr << "Failed to read file: " << e.what() << std::endl;
4545
throw;
4646
}
4747
}

engine/services/file_watcher_service.h

Lines changed: 53 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
#include <windows.h>
1414

1515
#else // Linux
16-
#include <poll.h>
1716
#include <limits.h>
17+
#include <poll.h>
1818
#include <sys/inotify.h>
1919
#include <unistd.h>
2020
#endif
@@ -121,38 +121,60 @@ class FileWatcherService {
121121
auto* watcher = static_cast<FileWatcherService*>(clientCallBackInfo);
122122

123123
for (size_t i = 0; i < numEvents; i++) {
124-
if (eventFlags[i] & kFSEventStreamEventFlagItemRemoved) {
124+
if (eventFlags[i] & (kFSEventStreamEventFlagItemRemoved |
125+
kFSEventStreamEventFlagItemRenamed |
126+
kFSEventStreamEventFlagItemModified)) {
127+
CTL_INF("File removed: " + std::string(paths[i]));
128+
CTL_INF("File event detected: " + std::string(paths[i]) +
129+
" flags: " + std::to_string(eventFlags[i]));
125130
watcher->model_service_->ForceIndexingModelList();
126131
}
127132
}
128133
}
129134

130135
void WatcherThread() {
131-
// macOS implementation
132-
auto mypath = CFStringCreateWithCString(NULL, watch_path_.c_str(),
136+
// Create a dedicated run loop for file system events
137+
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
138+
139+
auto mypath = CFStringCreateWithCString(nullptr, watch_path_.c_str(),
133140
kCFStringEncodingUTF8);
134-
auto path_to_watch = CFArrayCreate(NULL, (const void**)&mypath, 1, NULL);
141+
auto pathsToWatch = CFArrayCreate(nullptr, (const void**)&mypath, 1,
142+
&kCFTypeArrayCallBacks);
135143

136-
FSEventStreamContext context = {0, this, NULL, NULL, NULL};
144+
FSEventStreamContext context = {0, this, nullptr, nullptr, nullptr};
137145

138-
event_stream =
139-
FSEventStreamCreate(NULL, &FileWatcherService::callback, &context,
140-
path_to_watch, kFSEventStreamEventIdSinceNow,
141-
0.5, // 500ms latency
142-
kFSEventStreamCreateFlagFileEvents);
146+
event_stream = FSEventStreamCreate(
147+
nullptr, &FileWatcherService::callback, &context, pathsToWatch,
148+
kFSEventStreamEventIdSinceNow, 1, // each second
149+
kFSEventStreamCreateFlagFileEvents | kFSEventStreamCreateFlagNoDefer |
150+
kFSEventStreamCreateFlagWatchRoot);
143151

144-
dispatch_queue_t queue = dispatch_get_main_queue();
145-
FSEventStreamSetDispatchQueue(event_stream, queue);
146-
FSEventStreamStart(event_stream);
152+
if (!event_stream) {
153+
CFRelease(pathsToWatch);
154+
CFRelease(mypath);
155+
throw std::runtime_error("Failed to create FSEvent stream");
156+
}
157+
158+
FSEventStreamScheduleWithRunLoop(event_stream, runLoop,
159+
kCFRunLoopDefaultMode);
160+
161+
if (!FSEventStreamStart(event_stream)) {
162+
FSEventStreamInvalidate(event_stream);
163+
FSEventStreamRelease(event_stream);
164+
CFRelease(pathsToWatch);
165+
CFRelease(mypath);
166+
throw std::runtime_error("Failed to start FSEvent stream");
167+
}
147168

148169
while (running_) {
149-
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0, false);
170+
// Process events for a short time before checking running_ flag
171+
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.25, true);
150172
}
151173

152174
FSEventStreamStop(event_stream);
153175
FSEventStreamInvalidate(event_stream);
154176
FSEventStreamRelease(event_stream);
155-
CFRelease(path_to_watch);
177+
CFRelease(pathsToWatch);
156178
CFRelease(mypath);
157179
}
158180

@@ -221,22 +243,22 @@ class FileWatcherService {
221243
const int watch_flags = IN_DELETE | IN_DELETE_SELF | IN_CREATE;
222244
wd = inotify_add_watch(fd, dirPath.c_str(), watch_flags);
223245
if (wd < 0) {
224-
throw std::runtime_error("Failed to add watch on " + dirPath +
225-
": " + std::string(strerror(errno)));
246+
throw std::runtime_error("Failed to add watch on " + dirPath + ": " +
247+
std::string(strerror(errno)));
226248
}
227249
watch_descriptors[wd] = dirPath;
228250

229251
// Add watches for subdirectories
230252
try {
231-
for (const auto& entry :
253+
for (const auto& entry :
232254
std::filesystem::recursive_directory_iterator(dirPath)) {
233255
if (std::filesystem::is_directory(entry)) {
234256
int subwd = inotify_add_watch(fd, entry.path().c_str(), watch_flags);
235257
if (subwd >= 0) {
236258
watch_descriptors[subwd] = entry.path().string();
237259
} else {
238-
CTL_ERR("Failed to add watch for subdirectory " +
239-
entry.path().string() + ": " +
260+
CTL_ERR("Failed to add watch for subdirectory " +
261+
entry.path().string() + ": " +
240262
std::string(strerror(errno)));
241263
}
242264
}
@@ -274,21 +296,17 @@ class FileWatcherService {
274296
return;
275297
}
276298

277-
const int POLL_TIMEOUT_MS = 1000; // 1 second timeout
299+
const int POLL_TIMEOUT_MS = 1000; // 1 second timeout
278300
char buffer[4096];
279-
struct pollfd pfd = {
280-
.fd = fd,
281-
.events = POLLIN,
282-
.revents = 0
283-
};
284-
301+
struct pollfd pfd = {.fd = fd, .events = POLLIN, .revents = 0};
302+
285303
while (running_) {
286304
// Poll will sleep until either:
287305
// 1. Events are available (POLLIN)
288306
// 2. POLL_TIMEOUT_MS milliseconds have elapsed
289307
// 3. An error occurs
290308
int poll_result = poll(&pfd, 1, POLL_TIMEOUT_MS);
291-
309+
292310
if (poll_result < 0) {
293311
if (errno == EINTR) {
294312
// System call was interrupted, just retry
@@ -297,7 +315,7 @@ class FileWatcherService {
297315
CTL_ERR("Poll failed: " + std::string(strerror(errno)));
298316
break;
299317
}
300-
318+
301319
if (poll_result == 0) { // Timeout - no events
302320
// No need to sleep - poll() already waited
303321
continue;
@@ -327,17 +345,18 @@ class FileWatcherService {
327345
// Process events
328346
size_t i = 0;
329347
while (i < static_cast<size_t>(length)) {
330-
struct inotify_event* event =
348+
struct inotify_event* event =
331349
reinterpret_cast<struct inotify_event*>(&buffer[i]);
332-
350+
333351
if (event->mask & (IN_DELETE | IN_DELETE_SELF)) {
334352
try {
335353
model_service_->ForceIndexingModelList();
336354
} catch (const std::exception& e) {
337-
CTL_ERR("Error processing delete event: " + std::string(e.what()));
355+
CTL_ERR("Error processing delete event: " +
356+
std::string(e.what()));
338357
}
339358
}
340-
359+
341360
i += sizeof(struct inotify_event) + event->len;
342361
}
343362
}

0 commit comments

Comments
 (0)