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. // 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.) // (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;
- (void)addPath:(NSString*)aPath notifyingAbout:(u_int)flags; // See note above for values to pass in "flags" - (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)removePath:(NSString*)aPath;
- (void)removeAllPaths; - (void)removeAllPaths;

View File

@ -111,13 +111,11 @@ NSString const* VDKQueueAccessRevocationNotification = @"VDKQueueAccessWasRevoke
- (void)dealloc - (void)dealloc
{ {
// Shut down the thread that's scanning for kQueue events // Close our kqueue's file descriptor
_keepWatcherThreadRunning = NO; if (close(_coreQueueFD) == -1)
{
// Do this to close all the open file descriptors for files we're watching NSLog(@"VDKQueue watcherThread: Couldn't close main kqueue (%d)", errno);
[self removeAllPaths]; }
_watchedPathEntries = nil;
} }
#pragma mark - #pragma mark -
@ -171,22 +169,23 @@ NSString const* VDKQueueAccessRevocationNotification = @"VDKQueueAccessWasRevoke
- (void)watcherThread:(id)sender - (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 #if DEBUG_LOG_THREAD_LIFETIME
NSLog(@"watcherThread started."); NSLog(@"watcherThread started.");
#endif #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) 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) if (n <= 0 || ev.filter != EVFILT_VNODE || !ev.fflags)
{ {
continue; 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 #if DEBUG_LOG_THREAD_LIFETIME
NSLog(@"watcherThread finished."); NSLog(@"watcherThread finished.");
#endif #endif
@ -280,6 +273,8 @@ NSString const* VDKQueueAccessRevocationNotification = @"VDKQueueAccessWasRevoke
return; return;
} }
aPath = aPath.stringByStandardizingPath;
@synchronized(self) @synchronized(self)
{ {
if (_watchedPathEntries[aPath]) if (_watchedPathEntries[aPath])
@ -306,14 +301,17 @@ NSString const* VDKQueueAccessRevocationNotification = @"VDKQueueAccessWasRevoke
return; return;
} }
aPath = aPath.stringByStandardizingPath;
@synchronized(self) @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 (_watchedPathEntries.count == 0)
if (entry)
{ {
[_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) @synchronized(self)
{ {
// Close all the open file descriptors for files we're watching.
[_watchedPathEntries removeAllObjects]; [_watchedPathEntries removeAllObjects];
// Shut down the thread that's scanning for kQueue events.
_keepWatcherThreadRunning = NO;
} }
} }