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