13 const double VuoFileWatcher::checkIntervalSeconds = 2;
14 const double VuoFileWatcher::changeNotificationDelaySeconds = 0.25;
32 queue = dispatch_queue_create(
"org.vuo.filewatcher", NULL);
33 dispatch_sync(queue, ^{
34 initialize(delegate, fileOrFolderToWatch, persistent, NULL);
43 queue = parent->queue;
44 initialize(delegate, fileOrFolderToWatch, persistent, parent);
54 this->parent = parent;
55 this->fileToWatch = fileOrFolderToWatch;
56 this->delegate = delegate;
57 this->persistent = persistent;
58 this->fileToWatchSource = NULL;
59 this->fileToWatchSourceRunning = dispatch_group_create();
61 this->periodicCheckForExistence = NULL;
62 this->periodicCheckForExistenceRunning = dispatch_group_create();
63 this->childWatchersQueue = dispatch_queue_create(
"org.vuo.filewatcher.child", NULL);
64 this->changeNotificationDelay = NULL;
65 this->changeNotificationDelayRunning = dispatch_group_create();
67 DIR *d = opendir(fileOrFolderToWatch.c_str());
73 else if (errno == ENOTDIR)
77 else if (errno == ENOENT)
80 periodicallyCheckForExistence();
84 VUserLog(
"Error: Couldn't access '%s': %s", fileOrFolderToWatch.c_str(), strerror(errno));
93 void VuoFileWatcher::startWatching()
95 fileToWatchFD = open(fileToWatch.c_str(), O_EVTONLY);
96 if (fileToWatchFD < 0)
98 VUserLog(
"Error: %s", strerror(errno));
102 dispatch_group_enter(fileToWatchSourceRunning);
104 unsigned long mask = DISPATCH_VNODE_DELETE | DISPATCH_VNODE_RENAME | DISPATCH_VNODE_WRITE;
105 fileToWatchSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fileToWatchFD, mask, queue);
106 dispatch_source_set_event_handler(fileToWatchSource, ^{
107 unsigned long l = dispatch_source_get_data(fileToWatchSource);
109 if (l & DISPATCH_VNODE_DELETE
110 || l & DISPATCH_VNODE_RENAME)
112 DIR *d = opendir(fileToWatch.c_str());
118 else if (errno == ENOTDIR)
122 dispatch_source_cancel(fileToWatchSource);
124 else if (errno == ENOENT)
127 dispatch_source_cancel(fileToWatchSource);
129 periodicallyCheckForExistence();
132 else if (l & DISPATCH_VNODE_WRITE)
135 updateChildWatchers();
138 dispatch_source_set_cancel_handler(fileToWatchSource, ^{
139 close(fileToWatchFD);
141 dispatch_release(fileToWatchSource);
142 fileToWatchSource = NULL;
143 dispatch_group_leave(fileToWatchSourceRunning);
151 dispatch_resume(fileToWatchSource);
153 updateChildWatchers();
162 void VuoFileWatcher::periodicallyCheckForExistence()
164 dispatch_group_enter(periodicCheckForExistenceRunning);
166 periodicCheckForExistence = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
167 dispatch_source_set_timer(periodicCheckForExistence, dispatch_time(DISPATCH_TIME_NOW, checkIntervalSeconds*NSEC_PER_SEC), checkIntervalSeconds*NSEC_PER_SEC, checkIntervalSeconds*NSEC_PER_SEC/10);
168 dispatch_source_set_event_handler(periodicCheckForExistence, ^{
169 DIR *d = opendir(fileToWatch.c_str());
175 dispatch_source_cancel(periodicCheckForExistence);
178 dispatch_source_set_cancel_handler(periodicCheckForExistence, ^{
179 dispatch_release(periodicCheckForExistence);
180 periodicCheckForExistence = NULL;
181 dispatch_group_leave(periodicCheckForExistenceRunning);
183 dispatch_resume(periodicCheckForExistence);
193 void VuoFileWatcher::updateChildWatchers()
195 DIR *d = opendir(fileToWatch.c_str());
199 __block vector<VuoFileWatcher *> childWatchersToDestroy;
200 dispatch_sync(childWatchersQueue, ^{
204 while ((de = readdir(d)) != NULL)
206 if (strcmp(de->d_name,
".") == 0
207 || strcmp(de->d_name,
"..") == 0)
210 string compositePath = fileToWatch +
'/' + de->d_name;
212 bool haveExistingWatcher =
false;
213 for (vector<VuoFileWatcher *>::iterator i = childWatchers.begin(); i != childWatchers.end(); ++i)
214 if ((*i)->fileToWatch == compositePath)
216 haveExistingWatcher = true;
220 if (haveExistingWatcher)
223 childWatchers.push_back(
new VuoFileWatcher(delegate, compositePath,
false,
this));
227 for (vector<VuoFileWatcher *>::iterator i = childWatchers.begin(); i != childWatchers.end(); )
229 if (!(*i)->fileToWatchSource
230 && !(*i)->periodicCheckForExistence)
232 childWatchersToDestroy.push_back(*i);
233 i = childWatchers.erase(i);
252 void VuoFileWatcher::changeObserved()
256 parent->changeObserved();
260 if (changeNotificationDelay)
262 dispatch_source_set_timer(changeNotificationDelay, dispatch_time(DISPATCH_TIME_NOW, changeNotificationDelaySeconds*NSEC_PER_SEC), DISPATCH_TIME_FOREVER, changeNotificationDelaySeconds*NSEC_PER_SEC/10);
265 dispatch_group_enter(changeNotificationDelayRunning);
267 changeNotificationDelay = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
268 dispatch_source_set_timer(changeNotificationDelay, dispatch_time(DISPATCH_TIME_NOW, changeNotificationDelaySeconds*NSEC_PER_SEC), DISPATCH_TIME_FOREVER, changeNotificationDelaySeconds*NSEC_PER_SEC/10);
269 dispatch_source_set_event_handler(changeNotificationDelay, ^{
271 dispatch_source_cancel(changeNotificationDelay);
273 dispatch_source_set_cancel_handler(changeNotificationDelay, ^{
274 dispatch_release(changeNotificationDelay);
275 changeNotificationDelay = NULL;
276 dispatch_group_leave(changeNotificationDelayRunning);
278 dispatch_resume(changeNotificationDelay);
287 __block vector<VuoFileWatcher *> childWatchersCopy;
288 dispatch_sync(childWatchersQueue, ^{
289 childWatchersCopy = childWatchers;
290 childWatchers.clear();
296 if (fileToWatchSource)
298 dispatch_source_cancel(fileToWatchSource);
299 dispatch_group_wait(fileToWatchSourceRunning, DISPATCH_TIME_FOREVER);
301 dispatch_release(fileToWatchSourceRunning);
303 if (periodicCheckForExistence)
305 dispatch_source_cancel(periodicCheckForExistence);
306 dispatch_group_wait(periodicCheckForExistenceRunning, DISPATCH_TIME_FOREVER);
308 dispatch_release(periodicCheckForExistenceRunning);
310 if (changeNotificationDelay)
312 dispatch_source_cancel(changeNotificationDelay);
313 dispatch_group_wait(changeNotificationDelayRunning, DISPATCH_TIME_FOREVER);
315 dispatch_release(changeNotificationDelayRunning);
318 dispatch_release(queue);
320 dispatch_release(childWatchersQueue);