diff --git a/macosx/VDKQueue/VDKQueue.h b/macosx/VDKQueue/VDKQueue.h index c3db40aa5..1ee87b3db 100644 --- a/macosx/VDKQueue/VDKQueue.h +++ b/macosx/VDKQueue/VDKQueue.h @@ -136,10 +136,12 @@ extern NSString const* VDKQueueAccessRevocationNotification; // Just add it or remove it and this class will take action only if appropriate. // (Add only if we're not already watching it, remove only if we are.) // -// Warning: You must pass full, root-relative paths. Do not pass tilde-abbreviated paths or file URLs. +// Warning: Only pass file paths ("/path"), not string representations of URLs ("file://path"). - (void)addPath:(NSString*)aPath; - (void)addPath:(NSString*)aPath notifyingAbout:(u_int)flags; // See note above for values to pass in "flags" +// Either `removePath:` or `removeAllPaths` must be called if we want this object to ever be dealloc'd. +// This is because adding a path detaches a thread which retains self. - (void)removePath:(NSString*)aPath; - (void)removeAllPaths; diff --git a/macosx/VDKQueue/VDKQueue.mm b/macosx/VDKQueue/VDKQueue.mm index e60f6592e..ef6b0f81e 100644 --- a/macosx/VDKQueue/VDKQueue.mm +++ b/macosx/VDKQueue/VDKQueue.mm @@ -111,13 +111,11 @@ NSString const* VDKQueueAccessRevocationNotification = @"VDKQueueAccessWasRevoke - (void)dealloc { - // Shut down the thread that's scanning for kQueue events - _keepWatcherThreadRunning = NO; - - // Do this to close all the open file descriptors for files we're watching - [self removeAllPaths]; - - _watchedPathEntries = nil; + // Close our kqueue's file descriptor + if (close(_coreQueueFD) == -1) + { + NSLog(@"VDKQueue watcherThread: Couldn't close main kqueue (%d)", errno); + } } #pragma mark - @@ -171,22 +169,23 @@ NSString const* VDKQueueAccessRevocationNotification = @"VDKQueueAccessWasRevoke - (void)watcherThread:(id)sender { - int n; - struct kevent ev; - // 1 second timeout. Should be longer, but we need this thread to exit when a kqueue is dealloced, so 1 second timeout is quite a while to wait. - struct timespec timeout = { 1, 0 }; - // So we don't have to risk accessing iVars when the thread is terminated. - int theFD = _coreQueueFD; - - NSMutableArray* notesToPost = [[NSMutableArray alloc] initWithCapacity:5]; - #if DEBUG_LOG_THREAD_LIFETIME NSLog(@"watcherThread started."); #endif + NSThread.currentThread.name = @"VDKQueue"; + + struct kevent ev; + // 1 second timeout. Should be longer, but we need this thread to exit when a kqueue is dealloced, so 1 second timeout is quite a while to wait. + const struct timespec timeout = { 1, 0 }; + // So we don't have to risk accessing iVars when the thread is terminated. + int const theFD = _coreQueueFD; + + NSMutableArray* notesToPost = [[NSMutableArray alloc] initWithCapacity:5]; + while (_keepWatcherThreadRunning) { - n = kevent(theFD, NULL, 0, &ev, 1, &timeout); + int n = kevent(theFD, NULL, 0, &ev, 1, &timeout); if (n <= 0 || ev.filter != EVFILT_VNODE || !ev.fflags) { continue; @@ -254,12 +253,6 @@ NSString const* VDKQueueAccessRevocationNotification = @"VDKQueueAccessWasRevoke }); } - // Close our kqueue's file descriptor - if (close(theFD) == -1) - { - NSLog(@"VDKQueue watcherThread: Couldn't close main kqueue (%d)", errno); - } - #if DEBUG_LOG_THREAD_LIFETIME NSLog(@"watcherThread finished."); #endif @@ -280,6 +273,8 @@ NSString const* VDKQueueAccessRevocationNotification = @"VDKQueueAccessWasRevoke return; } + aPath = aPath.stringByStandardizingPath; + @synchronized(self) { if (_watchedPathEntries[aPath]) @@ -306,14 +301,17 @@ NSString const* VDKQueueAccessRevocationNotification = @"VDKQueueAccessWasRevoke return; } + aPath = aPath.stringByStandardizingPath; + @synchronized(self) { - VDKQueuePathEntry* entry = _watchedPathEntries[aPath]; + // Close the open file descriptor if we're watching it. + [_watchedPathEntries removeObjectForKey:aPath]; - // Remove it only if we're watching it. - if (entry) + if (_watchedPathEntries.count == 0) { - [_watchedPathEntries removeObjectForKey:aPath]; + // Shut down the thread that's scanning for kQueue events. + _keepWatcherThreadRunning = NO; } } } @@ -322,7 +320,11 @@ NSString const* VDKQueueAccessRevocationNotification = @"VDKQueueAccessWasRevoke { @synchronized(self) { + // Close all the open file descriptors for files we're watching. [_watchedPathEntries removeAllObjects]; + + // Shut down the thread that's scanning for kQueue events. + _keepWatcherThreadRunning = NO; } }