Permalink
Browse files

Merge branch 'fix-fsevents'

  • Loading branch information...
danielpunkass committed Apr 28, 2012
2 parents 43dd018 + d9df722 commit 494499195e35b0c4aff80a975880f43a72a19633
Showing with 95 additions and 79 deletions.
  1. +15 −3 UKFSEventsWatcher.h
  2. +80 −76 UKFSEventsWatcher.m
View
@@ -31,7 +31,7 @@
CFTimeInterval latency; // Time that must pass before events are being sent.
FSEventStreamCreateFlags flags; // See FSEvents.h
NSMutableDictionary* eventStreams; // List of FSEventStreamRef pointers in NSValues, with the pathnames as their keys.
- NSMutableDictionary* pathReferenceCounts;// To support a client adding the same path multiple times, we
+ NSCountedSet* eventStreamPaths; // To support a client adding the same path multiple times, we
// count the number of times it's been added, and only remove when
// the number goes to zero...
}
@@ -44,8 +44,20 @@
- (void) setFSEventStreamCreateFlags:(FSEventStreamCreateFlags)flags;
- (FSEventStreamCreateFlags) fsEventStreamCreateFlags;
-// UKFileWatcher defines the methods: addPath: removePath: and delegate accessors.
-- (void) removeAllPaths;
+// UKFileWatcher defines the methods: addPath: removePath: removeAllPaths: and delegate accessors.
+//
+// Our implementation differs from the basic UKFileWatcher protocol in that calls to
+// addPath and removePath are expected to be balanced so that if for example addPath:
+// is called twice with the same path, it should be called twice with removePath: to
+// effect the actual ending of the FSEvent observation.
+//
+// removeAllPaths ensures that every watched path is no longer watched, regardless
+// of the number of times addPath: has been called on a given path.
+//
+
+- (void) addPath: (NSString*)path;
+- (void) removePath: (NSString*)path;
+- (void) removeAllPaths;
@end
View
@@ -63,7 +63,7 @@ @implementation UKFSEventsWatcher
// Singleton accessor.
// -----------------------------------------------------------------------------
-+(id) sharedFileWatcher
++ (id) sharedFileWatcher
{
static UKFSEventsWatcher* sSharedFileWatcher = nil;
static NSString* sSharedFileWatcherMutex = @"UKFSEventsWatcher";
@@ -83,14 +83,14 @@ +(id) sharedFileWatcher
// * CONSTRUCTOR:
// -----------------------------------------------------------------------------
--(id) init
+- (id) init
{
if (self = [super init])
{
latency = 1.0;
flags = kFSEventStreamCreateFlagUseCFTypes | kFSEventStreamCreateFlagWatchRoot;
eventStreams = [[NSMutableDictionary alloc] init];
- pathReferenceCounts = [[NSMutableDictionary alloc] init];
+ eventStreamPaths = [[NSCountedSet alloc] init];
}
return self;
@@ -100,15 +100,15 @@ -(id) init
// * DESTRUCTOR:
// -----------------------------------------------------------------------------
--(void) dealloc
+- (void) dealloc
{
[self removeAllPaths];
[eventStreams release];
- [pathReferenceCounts release];
+ [eventStreamPaths release];
[super dealloc];
}
--(void) finalize
+- (void) finalize
{
[self removeAllPaths];
[super finalize];
@@ -159,7 +159,7 @@ - (FSEventStreamCreateFlags) fsEventStreamCreateFlags
// Mutator for file watcher delegate.
// -----------------------------------------------------------------------------
--(void) setDelegate: (id)newDelegate
+- (void) setDelegate:(id)newDelegate
{
delegate = newDelegate;
}
@@ -169,7 +169,7 @@ -(void) setDelegate: (id)newDelegate
// Accessor for file watcher delegate.
// -----------------------------------------------------------------------------
--(id) delegate
+- (id) delegate
{
return delegate;
}
@@ -194,70 +194,79 @@ - (NSString*) pathToParentFolderOfFile:(NSString*)inPath
return inPath;
}
+- (BOOL) _registerFSEventsObserverForPath:(NSString*)path
+{
+ BOOL succeeded = YES;
+ FSEventStreamContext context;
+ context.version = 0;
+ context.info = (void*) self;
+ context.retain = NULL;
+ context.release = NULL;
+ context.copyDescription = NULL;
+
+ NSArray* pathArray = [NSArray arrayWithObject:path];
+ FSEventStreamRef stream = FSEventStreamCreate(NULL,&FSEventCallback,&context,(CFArrayRef)pathArray,kFSEventStreamEventIdSinceNow,latency,flags);
+
+ if (stream)
+ {
+ FSEventStreamScheduleWithRunLoop(stream,CFRunLoopGetMain(),kCFRunLoopCommonModes);
+ FSEventStreamStart(stream);
+
+ [eventStreams setObject:[NSValue valueWithPointer:stream] forKey:path];
+ }
+ else
+ {
+ NSLog( @"UKFSEventsWatcher _registerFSEventObserverForPath:%@ failed",path);
+ succeeded = NO;
+ }
+
+ return succeeded;
+}
+
// -----------------------------------------------------------------------------
// addPath:
-// Start watching the folder at the specified path.
+// Start watching the folder at the specified path, or if we are already watching
+// the path, increase its count in eventStreamPaths
// -----------------------------------------------------------------------------
--(void) addPath: (NSString*)path
+- (void) addPath:(NSString*)path
{
- BOOL succeeded = YES;
path = [self pathToParentFolderOfFile:path];
- NSArray* paths = [NSArray arrayWithObject:path];
-
- NSUInteger currentRegistrationCount = 0;
// Do we already have a stream scheduled for this path?
// NOTE: Synchronize the whole thing so we don't run the risk of the current count changing while
// we're busy updating it with our new addition.
@synchronized (self)
{
- NSNumber* currentRegistrationCountNumber = [pathReferenceCounts objectForKey:path];
- if (currentRegistrationCountNumber != nil)
- {
- currentRegistrationCount = [currentRegistrationCountNumber unsignedIntValue];
- }
-
+ BOOL succeeded = YES;
+
+ NSUInteger currentRegistrationCount = [eventStreamPaths countForObject:path];
if (currentRegistrationCount == 0)
{
- FSEventStreamContext context;
- context.version = 0;
- context.info = (void*) self;
- context.retain = NULL;
- context.release = NULL;
- context.copyDescription = NULL;
-
- FSEventStreamRef stream = FSEventStreamCreate(NULL,&FSEventCallback,&context,(CFArrayRef)paths,kFSEventStreamEventIdSinceNow,latency,flags);
-
- if (stream)
- {
- FSEventStreamScheduleWithRunLoop(stream,CFRunLoopGetMain(),kCFRunLoopCommonModes);
- FSEventStreamStart(stream);
-
- [eventStreams setObject:[NSValue valueWithPointer:stream] forKey:path];
- }
- else
- {
- NSLog( @"UKFSEventsWatcher addPath:%@ failed",path);
- succeeded = NO;
- }
+ succeeded = [self _registerFSEventsObserverForPath:path];
}
if (succeeded)
{
- currentRegistrationCount = currentRegistrationCount + 1;
- NSNumber* newCountNumber = [NSNumber numberWithUnsignedInt:currentRegistrationCount];
- [pathReferenceCounts setObject:newCountNumber forKey:path];
+ [eventStreamPaths addObject:path];
}
}
}
+- (void) _unregisterFSEventStream:(FSEventStreamRef)stream
+{
+ FSEventStreamStop(stream);
+ FSEventStreamInvalidate(stream);
+ FSEventStreamRelease(stream);
+}
+
// -----------------------------------------------------------------------------
// removePath:
-// Stop watching the folder at the specified path.
+// Decrease the watch count for the given path, and if the count has gone
+// to zero, stop watching the given path.
// -----------------------------------------------------------------------------
--(void) removePath: (NSString*)path
+- (void) removePath:(NSString*)path
{
// Ensure we are removing a folder, not a file inside the desired folder. This matches
// the normalization done in addPath to make sure removePath for the same path will succeed.
@@ -267,30 +276,21 @@ -(void) removePath: (NSString*)path
@synchronized (self)
{
- NSUInteger currentRegistrationCount = 0;
- NSNumber* currentRegistrationCountNumber = [pathReferenceCounts objectForKey:path];
- if (currentRegistrationCountNumber != nil)
- {
- currentRegistrationCount = [currentRegistrationCountNumber unsignedIntValue];
- }
-
// We are sometimes asked to removePath on a path that we were never asked to add. That's
// OK - it just means they are being extra-certain before (probably) adding it for the
// first time...
+ NSUInteger currentRegistrationCount = [eventStreamPaths countForObject:path];
if (currentRegistrationCount > 0)
{
- NSUInteger newRegistrationCount = currentRegistrationCount - 1;
+ [eventStreamPaths removeObject:path];
+
+ NSUInteger newRegistrationCount = [eventStreamPaths countForObject:path];
- // Clear everything out if we've gone to zero, otherwise just update with te new count
+ // Clear everything out if we've gone to zero
if (newRegistrationCount == 0)
{
valueToRemove = [[[eventStreams objectForKey:path] retain] autorelease];
[eventStreams removeObjectForKey:path];
- [pathReferenceCounts removeObjectForKey:path];
- }
- else
- {
- [pathReferenceCounts setObject:[NSNumber numberWithUnsignedInt:newRegistrationCount] forKey:path];
}
}
}
@@ -301,35 +301,39 @@ -(void) removePath: (NSString*)path
if (stream)
{
- FSEventStreamStop(stream);
- FSEventStreamInvalidate(stream);
- FSEventStreamRelease(stream);
+ [self _unregisterFSEventStream:stream];
}
}
}
// -----------------------------------------------------------------------------
// removeAllPaths:
-// Stop watching all known folders.
+// Stop watching all known paths.
// -----------------------------------------------------------------------------
--(void) removeAllPaths
+- (void) removeAllPaths
{
- NSEnumerator* paths = [[eventStreams allKeys] objectEnumerator];
- NSString* path;
-
- while (path = [paths nextObject])
+ @synchronized (self)
{
- // Kind of a hack, but to get the remove to work as expected, we need
- // to make sure the reference count shows up as 1. The client in this
- // case is asking us to disregard all reference counts and just remove
- // everything ...
- @synchronized (self)
+ // We don't really need the paths, we just need the open FSEventStreamRefs.
+ // Unregister them all indiscriminately, then remove all objects from
+ // our tracking collections.
+
+ NSEnumerator* eventStreamEnum = [[eventStreams allValues] objectEnumerator];
+ NSValue* thisEventStreamPointer = nil;
+
+ while (thisEventStreamPointer = [eventStreamEnum nextObject])
{
- [pathReferenceCounts setObject:[NSNumber numberWithUnsignedInt:1] forKey:path];
+ FSEventStreamRef stream = [thisEventStreamPointer pointerValue];
+
+ if (stream)
+ {
+ [self _unregisterFSEventStream:stream];
+ }
}
- [self removePath:path];
+ [eventStreams removeAllObjects];
+ [eventStreamPaths removeAllObjects];
}
}

0 comments on commit 4944991

Please sign in to comment.