Browse Source

Media files discoverer: use GCD to watch a directory for changes

Gleb Pinigin 12 years ago
parent
commit
64560819c8

+ 0 - 73
AspenProject/DirectoryWatcher.h

@@ -1,73 +0,0 @@
-/*
-     File: DirectoryWatcher.h 
- Abstract: 
- Object used to monitor the contents of a given directory by using
- "kqueue": a kernel event notification mechanism.
-  
-  Version: 1.5 
-  
- Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple 
- Inc. ("Apple") in consideration of your agreement to the following 
- terms, and your use, installation, modification or redistribution of 
- this Apple software constitutes acceptance of these terms.  If you do 
- not agree with these terms, please do not use, install, modify or 
- redistribute this Apple software. 
-  
- In consideration of your agreement to abide by the following terms, and 
- subject to these terms, Apple grants you a personal, non-exclusive 
- license, under Apple's copyrights in this original Apple software (the 
- "Apple Software"), to use, reproduce, modify and redistribute the Apple 
- Software, with or without modifications, in source and/or binary forms; 
- provided that if you redistribute the Apple Software in its entirety and 
- without modifications, you must retain this notice and the following 
- text and disclaimers in all such redistributions of the Apple Software. 
- Neither the name, trademarks, service marks or logos of Apple Inc. may 
- be used to endorse or promote products derived from the Apple Software 
- without specific prior written permission from Apple.  Except as 
- expressly stated in this notice, no other rights or licenses, express or 
- implied, are granted by Apple herein, including but not limited to any 
- patent rights that may be infringed by your derivative works or by other 
- works in which the Apple Software may be incorporated. 
-  
- The Apple Software is provided by Apple on an "AS IS" basis.  APPLE 
- MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 
- THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 
- FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 
- OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 
-  
- IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 
- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
- INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 
- MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 
- AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 
- STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 
- POSSIBILITY OF SUCH DAMAGE. 
-  
- Copyright (C) 2013 Apple Inc. All Rights Reserved. 
-  
- */
-
-#import <Foundation/Foundation.h>
-
-@class DirectoryWatcher;
-
-@protocol DirectoryWatcherDelegate <NSObject>
-@required
-- (void)directoryDidChange:(DirectoryWatcher *)folderWatcher;
-@end
-
-@interface DirectoryWatcher : NSObject 
-{
-	id <DirectoryWatcherDelegate> __weak delegate;
-    
-	int dirFD;
-    int kq;
-
-	CFFileDescriptorRef dirKQRef;
-}
-@property (nonatomic, weak) id <DirectoryWatcherDelegate> delegate;
-
-+ (DirectoryWatcher *)watchFolderWithPath:(NSString *)watchPath delegate:(id<DirectoryWatcherDelegate>)watchDelegate;
-- (void)invalidate;
-@end

+ 0 - 221
AspenProject/DirectoryWatcher.m

@@ -1,221 +0,0 @@
-/*
-     File: DirectoryWatcher.m 
- Abstract: 
- Object used to monitor the contents of a given directory by using
- "kqueue": a kernel event notification mechanism.
-  
-  Version: 1.5 
-  
- Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple 
- Inc. ("Apple") in consideration of your agreement to the following 
- terms, and your use, installation, modification or redistribution of 
- this Apple software constitutes acceptance of these terms.  If you do 
- not agree with these terms, please do not use, install, modify or 
- redistribute this Apple software. 
-  
- In consideration of your agreement to abide by the following terms, and 
- subject to these terms, Apple grants you a personal, non-exclusive 
- license, under Apple's copyrights in this original Apple software (the 
- "Apple Software"), to use, reproduce, modify and redistribute the Apple 
- Software, with or without modifications, in source and/or binary forms; 
- provided that if you redistribute the Apple Software in its entirety and 
- without modifications, you must retain this notice and the following 
- text and disclaimers in all such redistributions of the Apple Software. 
- Neither the name, trademarks, service marks or logos of Apple Inc. may 
- be used to endorse or promote products derived from the Apple Software 
- without specific prior written permission from Apple.  Except as 
- expressly stated in this notice, no other rights or licenses, express or 
- implied, are granted by Apple herein, including but not limited to any 
- patent rights that may be infringed by your derivative works or by other 
- works in which the Apple Software may be incorporated. 
-  
- The Apple Software is provided by Apple on an "AS IS" basis.  APPLE 
- MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 
- THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 
- FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 
- OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 
-  
- IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 
- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
- INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 
- MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 
- AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 
- STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 
- POSSIBILITY OF SUCH DAMAGE. 
-  
- Copyright (C) 2013 Apple Inc. All Rights Reserved. 
-  
- */ 
-
-#import "DirectoryWatcher.h"
-
-#include <sys/types.h>
-#include <sys/event.h>
-#include <sys/time.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#import <CoreFoundation/CoreFoundation.h>
-
-@interface DirectoryWatcher (DirectoryWatcherPrivate)
-- (BOOL)startMonitoringDirectory:(NSString *)dirPath;
-- (void)kqueueFired;
-@end
-
-
-#pragma mark -
-
-@implementation DirectoryWatcher
-
-@synthesize delegate;
-
-- (instancetype)init
-{
-	self = [super init];
-	delegate = NULL;
-
-	dirFD = -1;
-    kq = -1;
-	dirKQRef = NULL;
-	
-	return self;
-}
-
-- (void)dealloc
-{
-	[self invalidate];
-}
-
-+ (DirectoryWatcher *)watchFolderWithPath:(NSString *)watchPath delegate:(id)watchDelegate
-{
-	DirectoryWatcher *retVal = NULL;
-	if ((watchDelegate != NULL) && (watchPath != NULL))
-	{
-		DirectoryWatcher *tempManager = [[DirectoryWatcher alloc] init];
-		tempManager.delegate = watchDelegate;		
-		if ([tempManager startMonitoringDirectory: watchPath])
-		{
-			// Everything appears to be in order, so return the DirectoryWatcher.  
-			// Otherwise we'll fall through and return NULL.
-			retVal = tempManager;
-		}
-	}
-	return retVal;
-}
-
-- (void)invalidate
-{
-	if (dirKQRef != NULL)
-	{
-		CFFileDescriptorInvalidate(dirKQRef);
-		CFRelease(dirKQRef);
-		dirKQRef = NULL;
-		// We don't need to close the kq, CFFileDescriptorInvalidate closed it instead.
-		// Change the value so no one thinks it's still live.
-		kq = -1;
-	}
-	
-	if(dirFD != -1)
-	{
-		close(dirFD);
-		dirFD = -1;
-	}
-}
-
-@end
-
-
-#pragma mark -
-
-@implementation DirectoryWatcher (DirectoryWatcherPrivate)
-
-- (void)kqueueFired
-{
-    assert(kq >= 0);
-
-    struct kevent   event;
-    struct timespec timeout = {0, 0};
-    int             eventCount;
-	
-    eventCount = kevent(kq, NULL, 0, &event, 1, &timeout);
-    assert((eventCount >= 0) && (eventCount < 2));
-    
-	// call our delegate of the directory change
-    [delegate directoryDidChange:self];
-
-    CFFileDescriptorEnableCallBacks(dirKQRef, kCFFileDescriptorReadCallBack);
-}
-
-static void KQCallback(CFFileDescriptorRef kqRef, CFOptionFlags callBackTypes, void *info)
-{
-    DirectoryWatcher *obj;
-	
-    obj = (__bridge DirectoryWatcher *)info;
-    assert([obj isKindOfClass:[DirectoryWatcher class]]);
-    assert(kqRef == obj->dirKQRef);
-    assert(callBackTypes == kCFFileDescriptorReadCallBack);
-	
-    [obj kqueueFired];
-}
-
-- (BOOL)startMonitoringDirectory:(NSString *)dirPath
-{
-	// Double initializing is not going to work...
-	if ((dirKQRef == NULL) && (dirFD == -1) && (kq == -1))
-	{
-		// Open the directory we're going to watch
-		dirFD = open([dirPath fileSystemRepresentation], O_EVTONLY);
-		if (dirFD >= 0)
-		{
-			// Create a kqueue for our event messages...
-			kq = kqueue();
-			if (kq >= 0)
-			{
-				struct kevent eventToAdd;
-				eventToAdd.ident  = dirFD;
-				eventToAdd.filter = EVFILT_VNODE;
-				eventToAdd.flags  = EV_ADD | EV_CLEAR;
-				eventToAdd.fflags = NOTE_WRITE;
-				eventToAdd.data   = 0;
-				eventToAdd.udata  = NULL;
-				
-				int errNum = kevent(kq, &eventToAdd, 1, NULL, 0, NULL);
-				if (errNum == 0)
-				{
-					CFFileDescriptorContext context = { 0, (__bridge void *)(self), NULL, NULL, NULL };
-					CFRunLoopSourceRef      rls;
-
-					// Passing true in the third argument so CFFileDescriptorInvalidate will close kq.
-					dirKQRef = CFFileDescriptorCreate(NULL, kq, true, KQCallback, &context);
-					if (dirKQRef != NULL)
-					{
-						rls = CFFileDescriptorCreateRunLoopSource(NULL, dirKQRef, 0);
-						if (rls != NULL)
-						{
-							CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
-							CFRelease(rls);
-							CFFileDescriptorEnableCallBacks(dirKQRef, kCFFileDescriptorReadCallBack);
-							
-							// If everything worked, return early and bypass shutting things down
-							return YES;
-						}
-						// Couldn't create a runloop source, invalidate and release the CFFileDescriptorRef
-						CFFileDescriptorInvalidate(dirKQRef);
-                        CFRelease(dirKQRef);
-						dirKQRef = NULL;
-					}
-				}
-				// kq is active, but something failed, close the handle...
-				close(kq);
-				kq = -1;
-			}
-			// file handle is open, but something failed, close the handle...
-			close(dirFD);
-			dirFD = -1;
-		}
-	}
-	return NO;
-}
-
-@end

+ 26 - 9
AspenProject/VLCMediaFileDiscoverer.m

@@ -9,14 +9,13 @@
 //
 
 #import "VLCMediaFileDiscoverer.h"
-#import "DirectoryWatcher.h"
 #import "NSString+SupportedMedia.h"
 
 const float MediaTimerInterval = 2.f;
 
-@interface VLCMediaFileDiscoverer () <DirectoryWatcherDelegate> {
+@interface VLCMediaFileDiscoverer () {
     NSMutableArray *_observers;
-    DirectoryWatcher *_directoryWatcher;
+    dispatch_source_t _directorySource;
 
     NSString *_directoryPath;
     NSArray *_directoryFiles;
@@ -99,13 +98,31 @@ const float MediaTimerInterval = 2.f;
     _directoryPath = directoryPath;
      _directoryFiles = [self directoryFiles];
 
-    _directoryWatcher = [DirectoryWatcher watchFolderWithPath:directoryPath delegate:self];
+    int const folderDescriptor = open([directoryPath fileSystemRepresentation], O_EVTONLY);
+    _directorySource = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, folderDescriptor,
+                                              DISPATCH_VNODE_WRITE, DISPATCH_TARGET_QUEUE_DEFAULT);
+
+    dispatch_source_set_event_handler(_directorySource, ^(){
+        unsigned long const data = dispatch_source_get_data(_directorySource);
+        if (data & DISPATCH_VNODE_WRITE) {
+            // Do all the work on the main thread,
+            // including timer scheduling, notifications delivering
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [self directoryDidChange];
+            });
+        }
+    });
+
+    dispatch_source_set_cancel_handler(_directorySource, ^(){
+        close(folderDescriptor);
+    });
+
+    dispatch_resume(_directorySource);
 }
 
 - (void)stopDiscovering
 {
-    [_directoryWatcher invalidate];
-    _directoryWatcher.delegate = nil;
+    dispatch_source_cancel(_directorySource);
 
     [self invalidateTimer];
 }
@@ -114,8 +131,8 @@ const float MediaTimerInterval = 2.f;
 
 - (NSArray *)directoryFiles
 {
-    NSArray *foundFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:_directoryPath error:nil];
-
+    NSArray *foundFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:_directoryPath
+                                                                              error:nil];
     return foundFiles;
 }
 
@@ -126,7 +143,7 @@ const float MediaTimerInterval = 2.f;
 
 #pragma mark - directory watcher delegate
 
-- (void)directoryDidChange:(DirectoryWatcher *)folderWatcher
+- (void)directoryDidChange
 {
     NSArray *foundFiles = [self directoryFiles];
 

+ 0 - 6
VLC for iOS.xcodeproj/project.pbxproj

@@ -265,7 +265,6 @@
 		A7A0E9F9174BA66000162F25 /* papasscode_marker.png in Resources */ = {isa = PBXBuildFile; fileRef = A7A0E9F1174BA66000162F25 /* papasscode_marker.png */; };
 		A7A0E9FA174BA66000162F25 /* papasscode_marker@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A7A0E9F2174BA66000162F25 /* papasscode_marker@2x.png */; };
 		A7A0E9FB174BA66000162F25 /* PAPasscodeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A7A0E9F4174BA66000162F25 /* PAPasscodeViewController.m */; };
-		A7C30259175A3C7A00AD4388 /* DirectoryWatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = A7C30258175A3C7A00AD4388 /* DirectoryWatcher.m */; };
 		A7C3025E175A53D400AD4388 /* NSString+SupportedMedia.m in Sources */ = {isa = PBXBuildFile; fileRef = A7C3025D175A53D400AD4388 /* NSString+SupportedMedia.m */; };
 		A7CB0DB11716F72600050CF3 /* PlayingExternally@2x~iphone.png in Resources */ = {isa = PBXBuildFile; fileRef = A7CB0DAD1716F72600050CF3 /* PlayingExternally@2x~iphone.png */; };
 		A7CB0DB21716F72600050CF3 /* PlayingExternally~iphone.png in Resources */ = {isa = PBXBuildFile; fileRef = A7CB0DAE1716F72600050CF3 /* PlayingExternally~iphone.png */; };
@@ -726,8 +725,6 @@
 		A7A0E9F2174BA66000162F25 /* papasscode_marker@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "papasscode_marker@2x.png"; sourceTree = "<group>"; };
 		A7A0E9F3174BA66000162F25 /* PAPasscodeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PAPasscodeViewController.h; sourceTree = "<group>"; };
 		A7A0E9F4174BA66000162F25 /* PAPasscodeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PAPasscodeViewController.m; sourceTree = "<group>"; };
-		A7C30257175A3C7A00AD4388 /* DirectoryWatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DirectoryWatcher.h; sourceTree = "<group>"; };
-		A7C30258175A3C7A00AD4388 /* DirectoryWatcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DirectoryWatcher.m; sourceTree = "<group>"; };
 		A7C3025C175A53D400AD4388 /* NSString+SupportedMedia.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+SupportedMedia.h"; sourceTree = "<group>"; };
 		A7C3025D175A53D400AD4388 /* NSString+SupportedMedia.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+SupportedMedia.m"; sourceTree = "<group>"; };
 		A7CB0DAD1716F72600050CF3 /* PlayingExternally@2x~iphone.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "PlayingExternally@2x~iphone.png"; sourceTree = "<group>"; };
@@ -1539,8 +1536,6 @@
 		A7D03A4817A4249F0022C16F /* MediaDiscovering */ = {
 			isa = PBXGroup;
 			children = (
-				A7C30257175A3C7A00AD4388 /* DirectoryWatcher.h */,
-				A7C30258175A3C7A00AD4388 /* DirectoryWatcher.m */,
 				A7D03A4517A41AD30022C16F /* VLCMediaFileDiscoverer.h */,
 				A7D03A4617A41AD30022C16F /* VLCMediaFileDiscoverer.m */,
 			);
@@ -1878,7 +1873,6 @@
 				7D5E39CF174FCE04007DAFA1 /* VLCDropboxTableViewCell.m in Sources */,
 				7D5F7AC317529430006CCCFA /* VLCHorizontalSwipeGestureRecognizer.m in Sources */,
 				7D5F7AC61752943F006CCCFA /* VLCVerticalSwipeGestureRecognizer.m in Sources */,
-				A7C30259175A3C7A00AD4388 /* DirectoryWatcher.m in Sources */,
 				A7C3025E175A53D400AD4388 /* NSString+SupportedMedia.m in Sources */,
 				7D47D72F1761101700E86BAD /* VLCSlider.m in Sources */,
 				7D2339AF176DE72E008D223C /* VLCOpenNetworkStreamViewController.m in Sources */,