refactor: support deallocating VDKQueue object (#6290)

This commit is contained in:
Cœur 2023-11-30 01:17:43 +01:00 committed by GitHub
parent f85c3b6f8d
commit e10689beea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 32 additions and 28 deletions

View File

@ -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;

View File

@ -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;
}
}