From 4fcf6cdc09864286faa925535aa9177aa1f799c5 Mon Sep 17 00:00:00 2001 From: Sebastian Keller Date: Thu, 6 Feb 2014 16:51:57 +0100 Subject: [PATCH 1/9] Added ARC compatibility macros and type modifiers --- VDKQueue.h | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/VDKQueue.h b/VDKQueue.h index 9856a34..ed9bf15 100644 --- a/VDKQueue.h +++ b/VDKQueue.h @@ -67,6 +67,25 @@ #include +// ARC Helpers + +#if ! __has_feature(objc_arc) + #define ARCCompatAutorelease(__obj__) ([__obj__ autorelease]); + #define ARCCompatRetain(__obj__) ([__obj__ retain]); + #define ARCCompatRelease(__obj__) ([__obj__ release]); + + #define ARCCompatWeakPropertyModifier assign + #define ARCCompatWeakPropertyTypeModifier +#else + #define ARCCompatAutorelease(__obj__) (__obj__); + #define ARCCompatRetain(__obj__) (__obj__); + #define ARCCompatRelease(__obj__) (__obj__); + + #define ARCCompatWeakPropertyModifier weak + #define ARCCompatWeakPropertyTypeModifier __weak +#endif + + // // Logical OR these values into the u_int that you pass in the -addPath:notifyingAbout: method // to specify the types of notifications you're interested in. Pass the default value to receive all of them. @@ -117,7 +136,7 @@ extern NSString * VDKQueueAccessRevocationNotification; @interface VDKQueue : NSObject { - id _delegate; + ARCCompatWeakPropertyTypeModifier id _delegate; BOOL _alwaysPostNotifications; // By default, notifications are posted only if there is no delegate set. Set this value to YES to have notes posted even when there is a delegate. @private @@ -144,7 +163,7 @@ extern NSString * VDKQueueAccessRevocationNotification; -@property (assign) id delegate; +@property (ARCCompatWeakPropertyModifier) id delegate; @property (assign) BOOL alwaysPostNotifications; -@end \ No newline at end of file +@end From 670878913f1032373f16aa18e432a81d60615363 Mon Sep 17 00:00:00 2001 From: Sebastian Keller Date: Thu, 6 Feb 2014 17:12:26 +0100 Subject: [PATCH 2/9] Make use of ARC Macros and fix missing NSWorkspace on iOS --- VDKQueue.m | 62 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 26 deletions(-) diff --git a/VDKQueue.m b/VDKQueue.m index f1bc83b..15c8644 100644 --- a/VDKQueue.m +++ b/VDKQueue.m @@ -70,7 +70,7 @@ - (id) initWithPath:(NSString*)inPath andSubscriptionFlags:(u_int)flags; _watchedFD = open([_path fileSystemRepresentation], O_EVTONLY, 0); if (_watchedFD < 0) { - [self autorelease]; + ARCCompatAutorelease(self) return nil; } _subscriptionFlags = flags; @@ -80,13 +80,15 @@ - (id) initWithPath:(NSString*)inPath andSubscriptionFlags:(u_int)flags; -(void) dealloc { - [_path release]; + ARCCompatRelease(_path) _path = nil; if (_watchedFD >= 0) close(_watchedFD); _watchedFD = -1; +#if ! __has_feature(objc_arc) [super dealloc]; +#endif } @end @@ -128,7 +130,7 @@ - (id) init _coreQueueFD = kqueue(); if (_coreQueueFD == -1) { - [self autorelease]; + ARCCompatAutorelease(self) return nil; } @@ -147,10 +149,12 @@ - (void) dealloc // Do this to close all the open file descriptors for files we're watching [self removeAllPaths]; - [_watchedPathEntries release]; + ARCCompatRelease(_watchedPathEntries) _watchedPathEntries = nil; +#if ! __has_feature(objc_arc) [super dealloc]; +#endif } @@ -170,9 +174,9 @@ - (VDKQueuePathEntry *) addPathToQueue:(NSString *)path notifyingAbout:(u_int)fl if (pathEntry) { // All flags already set? - if(([pathEntry subscriptionFlags] & flags) == flags) + if(([pathEntry subscriptionFlags] & flags) == flags) { - return [[pathEntry retain] autorelease]; + return ARCCompatAutoreleaseInline( ARCCompatReleaseInline(pathEntry) ); } flags |= [pathEntry subscriptionFlags]; @@ -183,12 +187,12 @@ - (VDKQueuePathEntry *) addPathToQueue:(NSString *)path notifyingAbout:(u_int)fl if (!pathEntry) { - pathEntry = [[[VDKQueuePathEntry alloc] initWithPath:path andSubscriptionFlags:flags] autorelease]; + pathEntry = ARCCompatAutoreleaseInline([[VDKQueuePathEntry alloc] initWithPath:path andSubscriptionFlags:flags]); } if (pathEntry) { - EV_SET(&ev, [pathEntry watchedFD], EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR, flags, 0, pathEntry); + EV_SET(&ev, [pathEntry watchedFD], EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR, flags, 0, ARCCompatBridge(void*, pathEntry)); [pathEntry setSubscriptionFlags:flags]; @@ -203,7 +207,7 @@ - (VDKQueuePathEntry *) addPathToQueue:(NSString *)path notifyingAbout:(u_int)fl } } - return [[pathEntry retain] autorelease]; + return ARCCompatAutoreleaseInline( ARCCompatReleaseInline(pathEntry) ); } return nil; @@ -211,7 +215,7 @@ - (VDKQueuePathEntry *) addPathToQueue:(NSString *)path notifyingAbout:(u_int)fl // -// WARNING: This thread has no active autorelease pool, so if you make changes, you must manually manage +// WARNING: This thread has no active autorelease pool, so if you make changes, you must manually manage // memory without relying on autorelease. Otherwise, you will leak! // - (void) watcherThread:(id)sender @@ -229,7 +233,7 @@ - (void) watcherThread:(id)sender while(_keepWatcherThreadRunning) { - @try + @try { n = kevent(theFD, NULL, 0, &ev, 1, &timeout); if (n > 0) @@ -250,13 +254,15 @@ - (void) watcherThread:(id)sender // check here to try to eliminate this (infrequent) problem. In theory, a KEVENT that does not have a VDKQueuePathEntry // object attached as the udata parameter is not an event we registered for, so we should not be "missing" any events. In theory. // - id pe = ev.udata; + id pe = ARCCompatBridge(id, ev.udata); if (pe && [pe respondsToSelector:@selector(path)]) { - NSString *fpath = [((VDKQueuePathEntry *)pe).path retain]; // Need to retain so it does not disappear while the block at the bottom is waiting to run on the main thread. Released in that block. + NSString *fpath = ARCCompatRetainInline( ((VDKQueuePathEntry *)pe).path ); // Need to retain so it does not disappear while the block at the bottom is waiting to run on the main thread. Released in that block. if (!fpath) continue; +#if !TARGET_OS_IPHONE [[NSWorkspace sharedWorkspace] noteFileSystemChanged:fpath]; +#endif // Clear any old notifications [notesToPost removeAllObjects]; @@ -305,13 +311,17 @@ - (void) watcherThread:(id)sender if (!_delegate || _alwaysPostNotifications) { NSDictionary *userInfoDict = [[NSDictionary alloc] initWithObjectsAndKeys:fpath, @"path", nil]; +#if TARGET_OS_IPHONE + [[NSNotificationCenter defaultCenter] postNotificationName:note object:self userInfo:userInfoDict]; +#else [[[NSWorkspace sharedWorkspace] notificationCenter] postNotificationName:note object:self userInfo:userInfoDict]; - [userInfoDict release]; +#endif + ARCCompatRelease(userInfoDict) } } - [fpath release]; - [notes release]; + ARCCompatRelease(fpath) + ARCCompatRelease(notes) }); } } @@ -319,7 +329,7 @@ - (void) watcherThread:(id)sender } } - @catch (NSException *localException) + @catch (NSException *localException) { NSLog(@"Error in VDKQueue watcherThread: %@", localException); } @@ -327,15 +337,15 @@ - (void) watcherThread:(id)sender // Close our kqueue's file descriptor if(close(theFD) == -1) { - NSLog(@"VDKQueue watcherThread: Couldn't close main kqueue (%d)", errno); + NSLog(@"VDKQueue watcherThread: Couldn't close main kqueue (%d)", errno); } - [notesToPost release]; + ARCCompatRelease(notesToPost) #if DEBUG_LOG_THREAD_LIFETIME NSLog(@"watcherThread finished."); #endif - + } @@ -351,7 +361,7 @@ - (void) watcherThread:(id)sender - (void) addPath:(NSString *)aPath { if (!aPath) return; - [aPath retain]; + ARCCompatRetain(aPath) @synchronized(self) { @@ -367,14 +377,14 @@ - (void) addPath:(NSString *)aPath } } - [aPath release]; + ARCCompatRelease(aPath) } - (void) addPath:(NSString *)aPath notifyingAbout:(u_int)flags { if (!aPath) return; - [aPath retain]; + ARCCompatRetain(aPath) @synchronized(self) { @@ -390,14 +400,14 @@ - (void) addPath:(NSString *)aPath notifyingAbout:(u_int)flags } } - [aPath release]; + ARCCompatRelease(aPath) } - (void) removePath:(NSString *)aPath { if (!aPath) return; - [aPath retain]; + ARCCompatRetain(aPath) @synchronized(self) { @@ -409,7 +419,7 @@ - (void) removePath:(NSString *)aPath } } - [aPath release]; + ARCCompatRelease(aPath) } From f9a1d7da6c5552d5a32611ddb0621a2a43b18e8e Mon Sep 17 00:00:00 2001 From: Sebastian Keller Date: Thu, 6 Feb 2014 17:25:36 +0100 Subject: [PATCH 3/9] Indentation --- VDKQueue.m | 450 ++++++++++++++++++++++++++--------------------------- 1 file changed, 225 insertions(+), 225 deletions(-) diff --git a/VDKQueue.m b/VDKQueue.m index 15c8644..5f32288 100644 --- a/VDKQueue.m +++ b/VDKQueue.m @@ -2,7 +2,7 @@ // Created by Bryan D K Jones on 28 March 2012 // Copyright 2013 Bryan D K Jones // -// Based heavily on UKKQueue, which was created and copyrighted by Uli Kusterer on 21 Dec 2003. +// Based heavily on UKKQueue, which was created and copyrighted by Uli Kusterer on 21 Dec 2003. // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -10,14 +10,14 @@ // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// 3. This notice may not be removed or altered from any source -// distribution. +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source +// distribution. #import "VDKQueue.h" #import @@ -41,7 +41,7 @@ #pragma mark - #pragma ------------------------------------------------------------------------------------------------------------------------------------------------------------ -// This is a simple model class used to hold info about each path we watch. +// This is a simple model class used to hold info about each path we watch. @interface VDKQueuePathEntry : NSObject { NSString* _path; @@ -63,14 +63,14 @@ @implementation VDKQueuePathEntry - (id) initWithPath:(NSString*)inPath andSubscriptionFlags:(u_int)flags; { - self = [super init]; + self = [super init]; if (self) { _path = [inPath copy]; _watchedFD = open([_path fileSystemRepresentation], O_EVTONLY, 0); if (_watchedFD < 0) { - ARCCompatAutorelease(self) + ARCCompatAutorelease(self) return nil; } _subscriptionFlags = flags; @@ -82,10 +82,10 @@ -(void) dealloc { ARCCompatRelease(_path) _path = nil; - + if (_watchedFD >= 0) close(_watchedFD); _watchedFD = -1; - + #if ! __has_feature(objc_arc) [super dealloc]; #endif @@ -130,11 +130,11 @@ - (id) init _coreQueueFD = kqueue(); if (_coreQueueFD == -1) { - ARCCompatAutorelease(self) + ARCCompatAutorelease(self) return nil; } - - _alwaysPostNotifications = NO; + + _alwaysPostNotifications = NO; _watchedPathEntries = [[NSMutableDictionary alloc] init]; } return self; @@ -143,17 +143,17 @@ - (id) init - (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]; - - ARCCompatRelease(_watchedPathEntries) - _watchedPathEntries = nil; - + // 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]; + + ARCCompatRelease(_watchedPathEntries) + _watchedPathEntries = nil; + #if ! __has_feature(objc_arc) - [super dealloc]; + [super dealloc]; #endif } @@ -168,184 +168,184 @@ - (VDKQueuePathEntry *) addPathToQueue:(NSString *)path notifyingAbout:(u_int)fl { @synchronized(self) { - // Are we already watching this path? + // Are we already watching this path? VDKQueuePathEntry *pathEntry = [_watchedPathEntries objectForKey:path]; - - if (pathEntry) + + if (pathEntry) { - // All flags already set? + // All flags already set? if(([pathEntry subscriptionFlags] & flags) == flags) - { - return ARCCompatAutoreleaseInline( ARCCompatReleaseInline(pathEntry) ); - } - + { + return ARCCompatAutoreleaseInline( ARCCompatReleaseInline(pathEntry) ); + } + flags |= [pathEntry subscriptionFlags]; } - + struct timespec nullts = { 0, 0 }; struct kevent ev; - + if (!pathEntry) - { - pathEntry = ARCCompatAutoreleaseInline([[VDKQueuePathEntry alloc] initWithPath:path andSubscriptionFlags:flags]); - } - + { + pathEntry = ARCCompatAutoreleaseInline([[VDKQueuePathEntry alloc] initWithPath:path andSubscriptionFlags:flags]); + } + if (pathEntry) { EV_SET(&ev, [pathEntry watchedFD], EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR, flags, 0, ARCCompatBridge(void*, pathEntry)); - + [pathEntry setSubscriptionFlags:flags]; - - [_watchedPathEntries setObject:pathEntry forKey:path]; - kevent(_coreQueueFD, &ev, 1, NULL, 0, &nullts); - + + [_watchedPathEntries setObject:pathEntry forKey:path]; + kevent(_coreQueueFD, &ev, 1, NULL, 0, &nullts); + // Start the thread that fetches and processes our events if it's not already running. if(!_keepWatcherThreadRunning) { _keepWatcherThreadRunning = YES; [NSThread detachNewThreadSelector:@selector(watcherThread:) toTarget:self withObject:nil]; } - } - - return ARCCompatAutoreleaseInline( ARCCompatReleaseInline(pathEntry) ); - } - - return nil; + } + + return ARCCompatAutoreleaseInline( ARCCompatReleaseInline(pathEntry) ); + } + + return nil; } // -// WARNING: This thread has no active autorelease pool, so if you make changes, you must manually manage -// memory without relying on autorelease. Otherwise, you will leak! +// WARNING: This thread has no active autorelease pool, so if you make changes, you must manually manage +// memory without relying on autorelease. Otherwise, you will leak! // - (void) watcherThread:(id)sender { - int n; - struct kevent ev; - struct timespec timeout = { 1, 0 }; // 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. + int n; + struct kevent ev; + struct timespec timeout = { 1, 0 }; // 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. int theFD = _coreQueueFD; // So we don't have to risk accessing iVars when the thread is terminated. - - NSMutableArray *notesToPost = [[NSMutableArray alloc] initWithCapacity:5]; - + + NSMutableArray *notesToPost = [[NSMutableArray alloc] initWithCapacity:5]; + #if DEBUG_LOG_THREAD_LIFETIME NSLog(@"watcherThread started."); #endif - - while(_keepWatcherThreadRunning) - { - @try - { - n = kevent(theFD, NULL, 0, &ev, 1, &timeout); - if (n > 0) - { - //NSLog( @"KEVENT returned %d", n ); - if (ev.filter == EVFILT_VNODE) - { - //NSLog( @"KEVENT filter is EVFILT_VNODE" ); - if (ev.fflags) - { - //NSLog( @"KEVENT flags are set" ); - - // - // Note: VDKQueue gets tested by thousands of CodeKit users who each watch several thousand files at once. - // I was receiving about 3 EXC_BAD_ACCESS (SIGSEGV) crash reports a month that listed the 'path' objc_msgSend - // as the culprit. That suggests the KEVENT is being sent back to us with a udata value that is NOT what we assigned - // to the queue, though I don't know why and I don't know why it's intermittent. Regardless, I've added an extra - // check here to try to eliminate this (infrequent) problem. In theory, a KEVENT that does not have a VDKQueuePathEntry - // object attached as the udata parameter is not an event we registered for, so we should not be "missing" any events. In theory. - // - id pe = ARCCompatBridge(id, ev.udata); - if (pe && [pe respondsToSelector:@selector(path)]) - { - NSString *fpath = ARCCompatRetainInline( ((VDKQueuePathEntry *)pe).path ); // Need to retain so it does not disappear while the block at the bottom is waiting to run on the main thread. Released in that block. - if (!fpath) continue; - + + while(_keepWatcherThreadRunning) + { + @try + { + n = kevent(theFD, NULL, 0, &ev, 1, &timeout); + if (n > 0) + { + //NSLog( @"KEVENT returned %d", n ); + if (ev.filter == EVFILT_VNODE) + { + //NSLog( @"KEVENT filter is EVFILT_VNODE" ); + if (ev.fflags) + { + //NSLog( @"KEVENT flags are set" ); + + // + // Note: VDKQueue gets tested by thousands of CodeKit users who each watch several thousand files at once. + // I was receiving about 3 EXC_BAD_ACCESS (SIGSEGV) crash reports a month that listed the 'path' objc_msgSend + // as the culprit. That suggests the KEVENT is being sent back to us with a udata value that is NOT what we assigned + // to the queue, though I don't know why and I don't know why it's intermittent. Regardless, I've added an extra + // check here to try to eliminate this (infrequent) problem. In theory, a KEVENT that does not have a VDKQueuePathEntry + // object attached as the udata parameter is not an event we registered for, so we should not be "missing" any events. In theory. + // + id pe = ARCCompatBridge(id, ev.udata); + if (pe && [pe respondsToSelector:@selector(path)]) + { + NSString *fpath = ARCCompatRetainInline( ((VDKQueuePathEntry *)pe).path ); // Need to retain so it does not disappear while the block at the bottom is waiting to run on the main thread. Released in that block. + if (!fpath) continue; + #if !TARGET_OS_IPHONE - [[NSWorkspace sharedWorkspace] noteFileSystemChanged:fpath]; + [[NSWorkspace sharedWorkspace] noteFileSystemChanged:fpath]; #endif - - // Clear any old notifications - [notesToPost removeAllObjects]; - - // Figure out which notifications we need to issue - if ((ev.fflags & NOTE_RENAME) == NOTE_RENAME) - { - [notesToPost addObject:VDKQueueRenameNotification]; - } - if ((ev.fflags & NOTE_WRITE) == NOTE_WRITE) - { - [notesToPost addObject:VDKQueueWriteNotification]; - } - if ((ev.fflags & NOTE_DELETE) == NOTE_DELETE) - { - [notesToPost addObject:VDKQueueDeleteNotification]; - } - if ((ev.fflags & NOTE_ATTRIB) == NOTE_ATTRIB) - { - [notesToPost addObject:VDKQueueAttributeChangeNotification]; - } - if ((ev.fflags & NOTE_EXTEND) == NOTE_EXTEND) - { - [notesToPost addObject:VDKQueueSizeIncreaseNotification]; - } - if ((ev.fflags & NOTE_LINK) == NOTE_LINK) - { - [notesToPost addObject:VDKQueueLinkCountChangeNotification]; - } - if ((ev.fflags & NOTE_REVOKE) == NOTE_REVOKE) - { - [notesToPost addObject:VDKQueueAccessRevocationNotification]; - } - - - NSArray *notes = [[NSArray alloc] initWithArray:notesToPost]; // notesToPost will be changed in the next loop iteration, which will likely occur before the block below runs. - - - // Post the notifications (or call the delegate method) on the main thread. - dispatch_async(dispatch_get_main_queue(), - ^{ - for (NSString *note in notes) - { - [_delegate VDKQueue:self receivedNotification:note forPath:fpath]; - - if (!_delegate || _alwaysPostNotifications) - { - NSDictionary *userInfoDict = [[NSDictionary alloc] initWithObjectsAndKeys:fpath, @"path", nil]; + + // Clear any old notifications + [notesToPost removeAllObjects]; + + // Figure out which notifications we need to issue + if ((ev.fflags & NOTE_RENAME) == NOTE_RENAME) + { + [notesToPost addObject:VDKQueueRenameNotification]; + } + if ((ev.fflags & NOTE_WRITE) == NOTE_WRITE) + { + [notesToPost addObject:VDKQueueWriteNotification]; + } + if ((ev.fflags & NOTE_DELETE) == NOTE_DELETE) + { + [notesToPost addObject:VDKQueueDeleteNotification]; + } + if ((ev.fflags & NOTE_ATTRIB) == NOTE_ATTRIB) + { + [notesToPost addObject:VDKQueueAttributeChangeNotification]; + } + if ((ev.fflags & NOTE_EXTEND) == NOTE_EXTEND) + { + [notesToPost addObject:VDKQueueSizeIncreaseNotification]; + } + if ((ev.fflags & NOTE_LINK) == NOTE_LINK) + { + [notesToPost addObject:VDKQueueLinkCountChangeNotification]; + } + if ((ev.fflags & NOTE_REVOKE) == NOTE_REVOKE) + { + [notesToPost addObject:VDKQueueAccessRevocationNotification]; + } + + + NSArray *notes = [[NSArray alloc] initWithArray:notesToPost]; // notesToPost will be changed in the next loop iteration, which will likely occur before the block below runs. + + + // Post the notifications (or call the delegate method) on the main thread. + dispatch_async(dispatch_get_main_queue(), + ^{ + for (NSString *note in notes) + { + [_delegate VDKQueue:self receivedNotification:note forPath:fpath]; + + if (!_delegate || _alwaysPostNotifications) + { + NSDictionary *userInfoDict = [[NSDictionary alloc] initWithObjectsAndKeys:fpath, @"path", nil]; #if TARGET_OS_IPHONE - [[NSNotificationCenter defaultCenter] postNotificationName:note object:self userInfo:userInfoDict]; + [[NSNotificationCenter defaultCenter] postNotificationName:note object:self userInfo:userInfoDict]; #else - [[[NSWorkspace sharedWorkspace] notificationCenter] postNotificationName:note object:self userInfo:userInfoDict]; + [[[NSWorkspace sharedWorkspace] notificationCenter] postNotificationName:note object:self userInfo:userInfoDict]; #endif - ARCCompatRelease(userInfoDict) - } - } - - ARCCompatRelease(fpath) - ARCCompatRelease(notes) - }); - } - } - } - } - } - - @catch (NSException *localException) - { - NSLog(@"Error in VDKQueue watcherThread: %@", localException); - } - } - + ARCCompatRelease(userInfoDict) + } + } + + ARCCompatRelease(fpath) + ARCCompatRelease(notes) + }); + } + } + } + } + } + + @catch (NSException *localException) + { + NSLog(@"Error in VDKQueue watcherThread: %@", localException); + } + } + // Close our kqueue's file descriptor if(close(theFD) == -1) { - NSLog(@"VDKQueue watcherThread: Couldn't close main kqueue (%d)", errno); - } - - ARCCompatRelease(notesToPost) - + NSLog(@"VDKQueue watcherThread: Couldn't close main kqueue (%d)", errno); + } + + ARCCompatRelease(notesToPost) + #if DEBUG_LOG_THREAD_LIFETIME NSLog(@"watcherThread finished."); #endif - + } @@ -360,88 +360,88 @@ - (void) watcherThread:(id)sender - (void) addPath:(NSString *)aPath { - if (!aPath) return; - ARCCompatRetain(aPath) - - @synchronized(self) - { - VDKQueuePathEntry *entry = [_watchedPathEntries objectForKey:aPath]; - - // Only add this path if we don't already have it. - if (!entry) - { - entry = [self addPathToQueue:aPath notifyingAbout:VDKQueueNotifyDefault]; - if (!entry) { - NSLog(@"VDKQueue tried to add the path %@ to watchedPathEntries, but the VDKQueuePathEntry was nil. \nIt's possible that the host process has hit its max open file descriptors limit.", aPath); - } - } - } - - ARCCompatRelease(aPath) + if (!aPath) return; + ARCCompatRetain(aPath) + + @synchronized(self) + { + VDKQueuePathEntry *entry = [_watchedPathEntries objectForKey:aPath]; + + // Only add this path if we don't already have it. + if (!entry) + { + entry = [self addPathToQueue:aPath notifyingAbout:VDKQueueNotifyDefault]; + if (!entry) { + NSLog(@"VDKQueue tried to add the path %@ to watchedPathEntries, but the VDKQueuePathEntry was nil. \nIt's possible that the host process has hit its max open file descriptors limit.", aPath); + } + } + } + + ARCCompatRelease(aPath) } - (void) addPath:(NSString *)aPath notifyingAbout:(u_int)flags { - if (!aPath) return; - ARCCompatRetain(aPath) - - @synchronized(self) - { - VDKQueuePathEntry *entry = [_watchedPathEntries objectForKey:aPath]; - - // Only add this path if we don't already have it. - if (!entry) - { - entry = [self addPathToQueue:aPath notifyingAbout:flags]; - if (!entry) { - NSLog(@"VDKQueue tried to add the path %@ to watchedPathEntries, but the VDKQueuePathEntry was nil. \nIt's possible that the host process has hit its max open file descriptors limit.", aPath); - } - } - } - - ARCCompatRelease(aPath) + if (!aPath) return; + ARCCompatRetain(aPath) + + @synchronized(self) + { + VDKQueuePathEntry *entry = [_watchedPathEntries objectForKey:aPath]; + + // Only add this path if we don't already have it. + if (!entry) + { + entry = [self addPathToQueue:aPath notifyingAbout:flags]; + if (!entry) { + NSLog(@"VDKQueue tried to add the path %@ to watchedPathEntries, but the VDKQueuePathEntry was nil. \nIt's possible that the host process has hit its max open file descriptors limit.", aPath); + } + } + } + + ARCCompatRelease(aPath) } - (void) removePath:(NSString *)aPath { - if (!aPath) return; - ARCCompatRetain(aPath) - - @synchronized(self) + if (!aPath) return; + ARCCompatRetain(aPath) + + @synchronized(self) { VDKQueuePathEntry *entry = [_watchedPathEntries objectForKey:aPath]; - - // Remove it only if we're watching it. - if (entry) { - [_watchedPathEntries removeObjectForKey:aPath]; - } + + // Remove it only if we're watching it. + if (entry) { + [_watchedPathEntries removeObjectForKey:aPath]; + } } - - ARCCompatRelease(aPath) + + ARCCompatRelease(aPath) } - (void) removeAllPaths { - @synchronized(self) - { - [_watchedPathEntries removeAllObjects]; - } + @synchronized(self) + { + [_watchedPathEntries removeAllObjects]; + } } - (NSUInteger) numberOfWatchedPaths { - NSUInteger count; - - @synchronized(self) - { - count = [_watchedPathEntries count]; - } - - return count; + NSUInteger count; + + @synchronized(self) + { + count = [_watchedPathEntries count]; + } + + return count; } From c35758133fb4f74402b783e1393546b31a7811e0 Mon Sep 17 00:00:00 2001 From: Sebastian Keller Date: Thu, 6 Feb 2014 17:30:09 +0100 Subject: [PATCH 4/9] Added additional ARC macros --- VDKQueue.h | 148 +++++++++++++++++++++++++++++------------------------ 1 file changed, 80 insertions(+), 68 deletions(-) diff --git a/VDKQueue.h b/VDKQueue.h index ed9bf15..4993dd8 100644 --- a/VDKQueue.h +++ b/VDKQueue.h @@ -2,7 +2,7 @@ // Created by Bryan D K Jones on 28 March 2012 // Copyright 2013 Bryan D K Jones // -// Based heavily on UKKQueue, which was created and copyrighted by Uli Kusterer on 21 Dec 2003. +// Based heavily on UKKQueue, which was created and copyrighted by Uli Kusterer on 21 Dec 2003. // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -10,54 +10,54 @@ // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// 3. This notice may not be removed or altered from any source -// distribution. +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source +// distribution. // -// BASED ON UKKQUEUE: +// BASED ON UKKQUEUE: // -// This is an updated, modernized and streamlined version of the excellent UKKQueue class, which was authored by Uli Kusterer. -// UKKQueue was written back in 2003 and there have been many, many improvements to Objective-C since then. VDKQueue uses the -// core of Uli's original class, but makes it faster and more efficient. Method calls are reduced. Grand Central Dispatch is used in place -// of Uli's "threadProxy" objects. The memory footprint is roughly halved, as I don't create the overhead that UKKQueue does. +// This is an updated, modernized and streamlined version of the excellent UKKQueue class, which was authored by Uli Kusterer. +// UKKQueue was written back in 2003 and there have been many, many improvements to Objective-C since then. VDKQueue uses the +// core of Uli's original class, but makes it faster and more efficient. Method calls are reduced. Grand Central Dispatch is used in place +// of Uli's "threadProxy" objects. The memory footprint is roughly halved, as I don't create the overhead that UKKQueue does. // -// VDKQueue is also simplified. The option to use it as a singleton is removed. You simply alloc/init an instance and add paths you want to -// watch. Your objects can be alerted to changes either by notifications or by a delegate method (or both). See below. +// VDKQueue is also simplified. The option to use it as a singleton is removed. You simply alloc/init an instance and add paths you want to +// watch. Your objects can be alerted to changes either by notifications or by a delegate method (or both). See below. // -// It also fixes several bugs. For one, it won't crash if it can't create a file descriptor to a file you ask it to watch. (By default, an OS X process can only -// have about 3,000 file descriptors open at once. If you hit that limit, UKKQueue will crash. VDKQueue will not.) +// It also fixes several bugs. For one, it won't crash if it can't create a file descriptor to a file you ask it to watch. (By default, an OS X process can only +// have about 3,000 file descriptors open at once. If you hit that limit, UKKQueue will crash. VDKQueue will not.) // // -// DEPENDENCIES: -// -// VDKQueue requires OS 10.6+ because it relies on Grand Central Dispatch. +// DEPENDENCIES: +// +// VDKQueue requires OS 10.6+ because it relies on Grand Central Dispatch. // // -// IMPORTANT NOTE ABOUT ATOMIC OPERATIONS +// IMPORTANT NOTE ABOUT ATOMIC OPERATIONS // -// There are two ways of saving a file on OS X: Atomic and Non-Atomic. In a non-atomic operation, a file is saved by directly overwriting it with new data. -// In an Atomic save, a temporary file is first written to a different location on disk. When that completes successfully, the original file is deleted and the -// temporary one is renamed and moved into place where the original file existed. +// There are two ways of saving a file on OS X: Atomic and Non-Atomic. In a non-atomic operation, a file is saved by directly overwriting it with new data. +// In an Atomic save, a temporary file is first written to a different location on disk. When that completes successfully, the original file is deleted and the +// temporary one is renamed and moved into place where the original file existed. // -// This matters a great deal. If you tell VDKQueue to watch file X, then you save file X ATOMICALLY, you'll receive a notification about that event. HOWEVER, you will -// NOT receive any additional notifications for file X from then on. This is because the atomic operation has essentially created a new file that replaced the one you -// told VDKQueue to watch. (This is not an issue for non-atomic operations.) +// This matters a great deal. If you tell VDKQueue to watch file X, then you save file X ATOMICALLY, you'll receive a notification about that event. HOWEVER, you will +// NOT receive any additional notifications for file X from then on. This is because the atomic operation has essentially created a new file that replaced the one you +// told VDKQueue to watch. (This is not an issue for non-atomic operations.) // -// To handle this, any time you receive a change notification from VDKQueue, you should call -removePath: followed by -addPath: on the file's path, even if the path -// has not changed. This will ensure that if the event that triggered the notification was an atomic operation, VDKQueue will start watching the "new" file that took -// the place of the old one. +// To handle this, any time you receive a change notification from VDKQueue, you should call -removePath: followed by -addPath: on the file's path, even if the path +// has not changed. This will ensure that if the event that triggered the notification was an atomic operation, VDKQueue will start watching the "new" file that took +// the place of the old one. // -// Other frameworks out there try to work around this issue by immediately attempting to re-open the file descriptor to the path. This is not bulletproof and may fail; -// it all depends on the timing of disk I/O. Bottom line: you could not rely on it and might miss future changes to the file path you're supposedly watching. That's why -// VDKQueue does not take this approach, but favors the "manual" method of "stop-watching-then-rewatch". +// Other frameworks out there try to work around this issue by immediately attempting to re-open the file descriptor to the path. This is not bulletproof and may fail; +// it all depends on the timing of disk I/O. Bottom line: you could not rely on it and might miss future changes to the file path you're supposedly watching. That's why +// VDKQueue does not take this approach, but favors the "manual" method of "stop-watching-then-rewatch". // @@ -70,25 +70,37 @@ // ARC Helpers #if ! __has_feature(objc_arc) - #define ARCCompatAutorelease(__obj__) ([__obj__ autorelease]); - #define ARCCompatRetain(__obj__) ([__obj__ retain]); - #define ARCCompatRelease(__obj__) ([__obj__ release]); - - #define ARCCompatWeakPropertyModifier assign - #define ARCCompatWeakPropertyTypeModifier +#define ARCCompatAutorelease(__obj__) [__obj__ autorelease]; +#define ARCCompatRetain(__obj__) [__obj__ retain]; +#define ARCCompatRelease(__obj__) [__obj__ release]; + +#define ARCCompatAutoreleaseInline(__obj__) [__obj__ autorelease] +#define ARCCompatRetainInline(__obj__) [__obj__ retain] +#define ARCCompatReleaseInline(__obj__) [__obj__ release] + +#define ARCCompatBridge(__type__, __obj__) __obj__ + +#define ARCCompatWeakPropertyModifier assign +#define ARCCompatWeakPropertyTypeModifier #else - #define ARCCompatAutorelease(__obj__) (__obj__); - #define ARCCompatRetain(__obj__) (__obj__); - #define ARCCompatRelease(__obj__) (__obj__); - - #define ARCCompatWeakPropertyModifier weak - #define ARCCompatWeakPropertyTypeModifier __weak +#define ARCCompatAutorelease(__obj__) +#define ARCCompatRetain(__obj__) +#define ARCCompatRelease(__obj__) + +#define ARCCompatAutoreleaseInline(__obj__) __obj__ +#define ARCCompatRetainInline(__obj__) __obj__ +#define ARCCompatReleaseInline(__obj__) __obj__ + +#define ARCCompatBridge(__type__, __obj__) (__bridge __type__)__obj__ + +#define ARCCompatWeakPropertyModifier weak +#define ARCCompatWeakPropertyTypeModifier __weak #endif // -// Logical OR these values into the u_int that you pass in the -addPath:notifyingAbout: method -// to specify the types of notifications you're interested in. Pass the default value to receive all of them. +// Logical OR these values into the u_int that you pass in the -addPath:notifyingAbout: method +// to specify the types of notifications you're interested in. Pass the default value to receive all of them. // #define VDKQueueNotifyAboutRename NOTE_RENAME // Item was renamed. #define VDKQueueNotifyAboutWrite NOTE_WRITE // Item contents changed (also folder contents changed). @@ -99,14 +111,14 @@ #define VDKQueueNotifyAboutAccessRevocation NOTE_REVOKE // Access to item was revoked. #define VDKQueueNotifyDefault (VDKQueueNotifyAboutRename | VDKQueueNotifyAboutWrite \ - | VDKQueueNotifyAboutDelete | VDKQueueNotifyAboutAttributeChange \ - | VDKQueueNotifyAboutSizeIncrease | VDKQueueNotifyAboutLinkCountChanged \ - | VDKQueueNotifyAboutAccessRevocation) +| VDKQueueNotifyAboutDelete | VDKQueueNotifyAboutAttributeChange \ +| VDKQueueNotifyAboutSizeIncrease | VDKQueueNotifyAboutLinkCountChanged \ +| VDKQueueNotifyAboutAccessRevocation) // -// Notifications that this class sends to the NSWORKSPACE notification center. -// Object = the instance of VDKQueue that was watching for changes -// userInfo.path = the file path where the change was observed +// Notifications that this class sends to the NSWORKSPACE notification center. +// Object = the instance of VDKQueue that was watching for changes +// userInfo.path = the file path where the change was observed // extern NSString * VDKQueueRenameNotification; extern NSString * VDKQueueWriteNotification; @@ -119,8 +131,8 @@ extern NSString * VDKQueueAccessRevocationNotification; // -// Or, instead of subscribing to notifications, you can specify a delegate and implement this method to respond to kQueue events. -// Note the required statement! For speed, this class does not check to make sure the delegate implements this method. (When I say "required" I mean it!) +// Or, instead of subscribing to notifications, you can specify a delegate and implement this method to respond to kQueue events. +// Note the required statement! For speed, this class does not check to make sure the delegate implements this method. (When I say "required" I mean it!) // @class VDKQueue; @protocol VDKQueueDelegate @@ -136,30 +148,30 @@ extern NSString * VDKQueueAccessRevocationNotification; @interface VDKQueue : NSObject { - ARCCompatWeakPropertyTypeModifier id _delegate; - BOOL _alwaysPostNotifications; // By default, notifications are posted only if there is no delegate set. Set this value to YES to have notes posted even when there is a delegate. - + ARCCompatWeakPropertyTypeModifier id _delegate; + BOOL _alwaysPostNotifications; // By default, notifications are posted only if there is no delegate set. Set this value to YES to have notes posted even when there is a delegate. + @private - int _coreQueueFD; // The actual kqueue ID (Unix file descriptor). - NSMutableDictionary *_watchedPathEntries; // List of VDKQueuePathEntries. Keys are NSStrings of the path that each VDKQueuePathEntry is for. - BOOL _keepWatcherThreadRunning; // Set to NO to cancel the thread that watches _coreQueueFD for kQueue events + int _coreQueueFD; // The actual kqueue ID (Unix file descriptor). + NSMutableDictionary *_watchedPathEntries; // List of VDKQueuePathEntries. Keys are NSStrings of the path that each VDKQueuePathEntry is for. + BOOL _keepWatcherThreadRunning; // Set to NO to cancel the thread that watches _coreQueueFD for kQueue events } // -// Note: there is no need to ask whether a path is already being watched. 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. +// Note: there is no need to ask whether a path is already being watched. 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. // - (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" - (void) removePath:(NSString *)aPath; - (void) removeAllPaths; -- (NSUInteger) numberOfWatchedPaths; // Returns the number of paths that this VDKQueue instance is actively watching. +- (NSUInteger) numberOfWatchedPaths; // Returns the number of paths that this VDKQueue instance is actively watching. From 545e0b9a7b0ca42bae951cb1f45b9284d2894fac Mon Sep 17 00:00:00 2001 From: Sebastian Keller Date: Thu, 6 Feb 2014 17:40:34 +0100 Subject: [PATCH 5/9] Changed misplaced release to retain --- VDKQueue.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/VDKQueue.m b/VDKQueue.m index 5f32288..bec0a2e 100644 --- a/VDKQueue.m +++ b/VDKQueue.m @@ -176,7 +176,7 @@ - (VDKQueuePathEntry *) addPathToQueue:(NSString *)path notifyingAbout:(u_int)fl // All flags already set? if(([pathEntry subscriptionFlags] & flags) == flags) { - return ARCCompatAutoreleaseInline( ARCCompatReleaseInline(pathEntry) ); + return ARCCompatAutoreleaseInline( ARCCompatRetainInline(pathEntry) ); } flags |= [pathEntry subscriptionFlags]; @@ -207,7 +207,7 @@ - (VDKQueuePathEntry *) addPathToQueue:(NSString *)path notifyingAbout:(u_int)fl } } - return ARCCompatAutoreleaseInline( ARCCompatReleaseInline(pathEntry) ); + return ARCCompatAutoreleaseInline( ARCCompatRetainInline(pathEntry) ); } return nil; From 11e033192c83016ed8a39dc8da50f6ade613c01d Mon Sep 17 00:00:00 2001 From: Sebastian Keller Date: Thu, 6 Feb 2014 18:13:55 +0100 Subject: [PATCH 6/9] added iOS thread sleep possibilities --- VDKQueue.m | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/VDKQueue.m b/VDKQueue.m index bec0a2e..f53c0a1 100644 --- a/VDKQueue.m +++ b/VDKQueue.m @@ -115,7 +115,7 @@ - (void) watcherThread:(id)sender; @implementation VDKQueue -@synthesize delegate = _delegate, alwaysPostNotifications = _alwaysPostNotifications; +@synthesize delegate = _delegate, alwaysPostNotifications = _alwaysPostNotifications, sleepInterval = _sleepInterval; @@ -135,6 +135,11 @@ - (id) init } _alwaysPostNotifications = NO; +#if TARGET_OS_IPHONE + _sleepInterval = 60; +#else + _sleepInterval = 0; +#endif _watchedPathEntries = [[NSMutableDictionary alloc] init]; } return self; @@ -333,6 +338,9 @@ - (void) watcherThread:(id)sender { NSLog(@"Error in VDKQueue watcherThread: %@", localException); } +#if TARGET_OS_IPHONE + [NSThread sleepForTimeInterval:_sleepInterval]; // To save power on iOS +#endif } // Close our kqueue's file descriptor From 0e988cdfba6a120a2b4d007d8c4e178f9b17aec3 Mon Sep 17 00:00:00 2001 From: Sebastian Keller Date: Thu, 6 Feb 2014 18:14:21 +0100 Subject: [PATCH 7/9] Added iOS thread sleep possibilities --- VDKQueue.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/VDKQueue.h b/VDKQueue.h index 4993dd8..2e66dc6 100644 --- a/VDKQueue.h +++ b/VDKQueue.h @@ -150,6 +150,7 @@ extern NSString * VDKQueueAccessRevocationNotification; { ARCCompatWeakPropertyTypeModifier id _delegate; BOOL _alwaysPostNotifications; // By default, notifications are posted only if there is no delegate set. Set this value to YES to have notes posted even when there is a delegate. + int _sleepInterval; @private int _coreQueueFD; // The actual kqueue ID (Unix file descriptor). @@ -177,5 +178,6 @@ extern NSString * VDKQueueAccessRevocationNotification; @property (ARCCompatWeakPropertyModifier) id delegate; @property (assign) BOOL alwaysPostNotifications; +@property (assign) int sleepInterval; @end From faa504a1f1fd182f6a1ff7489af0c045c5053441 Mon Sep 17 00:00:00 2001 From: Sebastian Keller Date: Thu, 6 Feb 2014 18:16:27 +0100 Subject: [PATCH 8/9] changed _sleepInterval type to NSTimeInterval --- VDKQueue.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VDKQueue.h b/VDKQueue.h index 2e66dc6..06c8bd8 100644 --- a/VDKQueue.h +++ b/VDKQueue.h @@ -150,7 +150,7 @@ extern NSString * VDKQueueAccessRevocationNotification; { ARCCompatWeakPropertyTypeModifier id _delegate; BOOL _alwaysPostNotifications; // By default, notifications are posted only if there is no delegate set. Set this value to YES to have notes posted even when there is a delegate. - int _sleepInterval; + NSTimeInterval _sleepInterval; @private int _coreQueueFD; // The actual kqueue ID (Unix file descriptor). From 053ffeb23b01f7b7a2b0948b386202e291303f3b Mon Sep 17 00:00:00 2001 From: Sebastian Keller Date: Fri, 7 Feb 2014 09:49:08 +0100 Subject: [PATCH 9/9] changed sleepInterval property type to NSTimeInterval --- VDKQueue.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VDKQueue.h b/VDKQueue.h index 06c8bd8..19b9d0d 100644 --- a/VDKQueue.h +++ b/VDKQueue.h @@ -178,6 +178,6 @@ extern NSString * VDKQueueAccessRevocationNotification; @property (ARCCompatWeakPropertyModifier) id delegate; @property (assign) BOOL alwaysPostNotifications; -@property (assign) int sleepInterval; +@property (assign) NSTimeInterval sleepInterval; @end