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,39 +121,59 @@ 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 (),
133- kCFStringEncodingUTF8 );
134- auto path_to_watch = CFArrayCreate (NULL , (const void **)&mypath, 1 , NULL );
136+ CFRunLoopRef runLoop = CFRunLoopGetCurrent ();
135137
136- FSEventStreamContext context = {0 , this , NULL , NULL , NULL };
138+ auto path = CFStringCreateWithCString (nullptr , watch_path_.c_str (),
139+ kCFStringEncodingUTF8 );
140+ auto path_to_watch =
141+ CFArrayCreate (nullptr , (const void **)&path, 1 , &kCFTypeArrayCallBacks );
137142
138- event_stream =
139- FSEventStreamCreate (NULL , &FileWatcherService::callback, &context,
140- path_to_watch, kFSEventStreamEventIdSinceNow ,
141- 0.5 , // 500ms latency
142- kFSEventStreamCreateFlagFileEvents );
143+ FSEventStreamContext context = {0 , this , nullptr , nullptr , nullptr };
143144
144- dispatch_queue_t queue = dispatch_get_main_queue ();
145- FSEventStreamSetDispatchQueue (event_stream, queue);
146- FSEventStreamStart (event_stream);
145+ event_stream = FSEventStreamCreate (
146+ nullptr , &FileWatcherService::callback, &context, path_to_watch,
147+ kFSEventStreamEventIdSinceNow , 1 , // each second
148+ kFSEventStreamCreateFlagFileEvents | kFSEventStreamCreateFlagNoDefer |
149+ kFSEventStreamCreateFlagWatchRoot );
150+
151+ if (!event_stream) {
152+ CFRelease (path_to_watch);
153+ CFRelease (path);
154+ throw std::runtime_error (" Failed to create FSEvent stream" );
155+ }
156+
157+ FSEventStreamScheduleWithRunLoop (event_stream, runLoop,
158+ kCFRunLoopDefaultMode );
159+
160+ if (!FSEventStreamStart (event_stream)) {
161+ FSEventStreamInvalidate (event_stream);
162+ FSEventStreamRelease (event_stream);
163+ CFRelease (path_to_watch);
164+ CFRelease (path);
165+ throw std::runtime_error (" Failed to start FSEvent stream" );
166+ }
147167
148168 while (running_) {
149- CFRunLoopRunInMode (kCFRunLoopDefaultMode , 1.0 , false );
169+ CFRunLoopRunInMode (kCFRunLoopDefaultMode , 0.25 , true );
150170 }
151171
152172 FSEventStreamStop (event_stream);
153173 FSEventStreamInvalidate (event_stream);
154174 FSEventStreamRelease (event_stream);
155175 CFRelease (path_to_watch);
156- CFRelease (mypath );
176+ CFRelease (path );
157177 }
158178
159179#elif defined(_WIN32)
@@ -221,22 +241,22 @@ class FileWatcherService {
221241 const int watch_flags = IN_DELETE | IN_DELETE_SELF | IN_CREATE;
222242 wd = inotify_add_watch (fd, dirPath.c_str (), watch_flags);
223243 if (wd < 0 ) {
224- throw std::runtime_error (" Failed to add watch on " + dirPath +
225- " : " + std::string (strerror (errno)));
244+ throw std::runtime_error (" Failed to add watch on " + dirPath + " : " +
245+ std::string (strerror (errno)));
226246 }
227247 watch_descriptors[wd] = dirPath;
228248
229249 // Add watches for subdirectories
230250 try {
231- for (const auto & entry :
251+ for (const auto & entry :
232252 std::filesystem::recursive_directory_iterator (dirPath)) {
233253 if (std::filesystem::is_directory (entry)) {
234254 int subwd = inotify_add_watch (fd, entry.path ().c_str (), watch_flags);
235255 if (subwd >= 0 ) {
236256 watch_descriptors[subwd] = entry.path ().string ();
237257 } else {
238- CTL_ERR (" Failed to add watch for subdirectory " +
239- entry.path ().string () + " : " +
258+ CTL_ERR (" Failed to add watch for subdirectory " +
259+ entry.path ().string () + " : " +
240260 std::string (strerror (errno)));
241261 }
242262 }
@@ -274,21 +294,17 @@ class FileWatcherService {
274294 return ;
275295 }
276296
277- const int POLL_TIMEOUT_MS = 1000 ; // 1 second timeout
297+ const int POLL_TIMEOUT_MS = 1000 ; // 1 second timeout
278298 char buffer[4096 ];
279- struct pollfd pfd = {
280- .fd = fd,
281- .events = POLLIN,
282- .revents = 0
283- };
284-
299+ struct pollfd pfd = {.fd = fd, .events = POLLIN, .revents = 0 };
300+
285301 while (running_) {
286302 // Poll will sleep until either:
287303 // 1. Events are available (POLLIN)
288304 // 2. POLL_TIMEOUT_MS milliseconds have elapsed
289305 // 3. An error occurs
290306 int poll_result = poll (&pfd, 1 , POLL_TIMEOUT_MS);
291-
307+
292308 if (poll_result < 0 ) {
293309 if (errno == EINTR) {
294310 // System call was interrupted, just retry
@@ -297,7 +313,7 @@ class FileWatcherService {
297313 CTL_ERR (" Poll failed: " + std::string (strerror (errno)));
298314 break ;
299315 }
300-
316+
301317 if (poll_result == 0 ) { // Timeout - no events
302318 // No need to sleep - poll() already waited
303319 continue ;
@@ -327,17 +343,18 @@ class FileWatcherService {
327343 // Process events
328344 size_t i = 0 ;
329345 while (i < static_cast <size_t >(length)) {
330- struct inotify_event * event =
346+ struct inotify_event * event =
331347 reinterpret_cast <struct inotify_event *>(&buffer[i]);
332-
348+
333349 if (event->mask & (IN_DELETE | IN_DELETE_SELF)) {
334350 try {
335351 model_service_->ForceIndexingModelList ();
336352 } catch (const std::exception& e) {
337- CTL_ERR (" Error processing delete event: " + std::string (e.what ()));
353+ CTL_ERR (" Error processing delete event: " +
354+ std::string (e.what ()));
338355 }
339356 }
340-
357+
341358 i += sizeof (struct inotify_event ) + event->len ;
342359 }
343360 }
0 commit comments