-
-
Save Kirow/f0e25b7bd1b94bd7ac0a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "FileChangeObserver.h" | |
#undef Assert | |
#define Assert(COND) { if (!(COND)) { raise( SIGINT ) ; } } | |
@interface FileChangeObserver () | |
@property ( nonatomic, readonly ) int kqueue ; | |
@property ( nonatomic ) enum FileChangeNotificationType typeMask ; | |
@end | |
@implementation FileChangeObserver | |
@synthesize kqueue = _kqueue ; | |
+(instancetype)observerForURL:(NSURL*)url types:(enum FileChangeNotificationType)types delegate:(id<FileChangeObserverDelegate>)delegate | |
{ | |
if ( !url ) { return nil ; } | |
FileChangeObserver * result = [ [ [ self class ] alloc ] init ] ; | |
result.url = url ; | |
result.delegate = delegate ; | |
result.typeMask = types ; | |
[ result startObserving ] ; | |
return result ; | |
} | |
-(void)dealloc | |
{ | |
printf("%s\n", __PRETTY_FUNCTION__); | |
[ self stopObserving ] ; | |
} | |
// | |
// kqueue_main | |
// | |
static void (^kqueue_main)(FileChangeObserver *) = ^(__unsafe_unretained FileChangeObserver * self) { | |
int fd = open( [ [ self.url path ] fileSystemRepresentation ], O_EVTONLY ) ; | |
Assert( fd >= 0 ); | |
int q = self.kqueue ; | |
{ | |
struct kevent event = { | |
.ident = fd, | |
.filter = EVFILT_VNODE, | |
.flags = EV_ADD | EV_CLEAR, | |
.fflags = self.typeMask, | |
} ; | |
int error = kevent( q, &event, 1, NULL, 0, NULL); | |
Assert(error == 0); | |
} | |
struct kevent event = {0} ; | |
for(;;) | |
{ | |
int nEvents = kevent( q, NULL, 0, &event, 1, NULL ) ; | |
if ( nEvents != 1 ) { break ; } | |
[[ NSThread mainThread ] perform:^{ | |
[ self.delegate fileChanged:self typeMask:(enum FileChangeNotificationType)event.fflags ] ; | |
}]; | |
} | |
} ; | |
-(void)stopObserving | |
{ | |
@synchronized( self ) | |
{ | |
close( self.kqueue ) ; | |
} | |
} | |
-(void)invalidate | |
{ | |
[ self stopObserving ] ; | |
} | |
-(void)startObserving | |
{ | |
@synchronized( self ) | |
{ | |
printf("%s\n", __PRETTY_FUNCTION__); | |
static dispatch_queue_t __q ; | |
{ | |
static dispatch_once_t onceToken; | |
dispatch_once(&onceToken, ^{ | |
__q = dispatch_queue_create( "file observer queue", DISPATCH_QUEUE_CONCURRENT ) ; | |
}); | |
} | |
// this is __unsafe_unretained to avoid retaining 'self'. | |
// using __weak isn't a strong enough to prevent ARC from retaining 'self'. | |
// This allows the observer to be torn down simply be releasing it, | |
// instead of requiring the client to invoke -invalidate. | |
__unsafe_unretained id unsafe_self = self ; | |
dispatch_async( __q, ^{ | |
kqueue_main( unsafe_self ) ; | |
}) ; | |
} | |
} | |
-(int)kqueue | |
{ | |
if ( !_kqueue ) { _kqueue = kqueue() ; } | |
return _kqueue ; | |
} | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <Foundation/Foundation.h> | |
enum FileChangeNotificationType | |
{ | |
kFileChangeType_Delete = NOTE_DELETE | |
, kFileChangeType_Write = NOTE_WRITE | |
, kFileChangeType_DirectoryContentsChanged = kFileChangeType_Write | |
//#define NOTE_WRITE 0x00000002 /* data contents changed */ | |
//#define NOTE_EXTEND 0x00000004 /* size increased */ | |
//#define NOTE_ATTRIB 0x00000008 /* attributes changed */ | |
//#define NOTE_LINK 0x00000010 /* link count changed */ | |
//#define NOTE_RENAME 0x00000020 /* vnode was renamed */ | |
//#define NOTE_REVOKE 0x00000040 /* vnode access was revoked */ | |
//#define NOTE_NONE 0x00000080 /* No specific vnode event: to test for EVFILT_READ activation*/ | |
} ; | |
@class FileChangeObserver ; | |
@protocol FileChangeObserverDelegate<NSObject> | |
@required | |
-(void)fileChanged:(FileChangeObserver*)observer typeMask:(enum FileChangeNotificationType)type ; | |
@end | |
@interface FileChangeObserver : NSObject | |
@property ( nonatomic, copy ) NSURL * url ; | |
@property ( nonatomic, weak ) id<FileChangeObserverDelegate> delegate ; | |
+(instancetype)observerForURL:(NSURL*)url types:(enum FileChangeNotificationType)types delegate:(id<FileChangeObserverDelegate>)delegate ; | |
-(void)invalidate ; | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// category for NSString to print kqueue event flags | |
@implementation NSString (FileChangeObserver) | |
+(NSString*)stringWithKEventFFlags:(int)flags | |
{ | |
NSMutableArray * array = [ NSMutableArray array ] ; | |
struct | |
{ | |
__unsafe_unretained NSString * name; | |
} bitNames[] = { | |
{ @"NOTE_DELETE" } | |
, { @"NOTE_WRITE" } | |
, { @"NOTE_EXTEND" } | |
, { @"NOTE_ATTRIB" } | |
, { @"NOTE_LINK" } | |
, { @"NOTE_RENAME" } | |
, { @"NOTE_REVOKE" } | |
, { @"NOTE_NONE" } | |
} ; | |
for( int index = 0, count = COUNTOF( bitNames ); index < count; ++index ) | |
{ | |
if ( ( flags & ( 1 << index )) != 0 ) | |
{ | |
[ array addObject:bitNames[ index ].name ] ; | |
} | |
} | |
NSString * result = [ array componentsJoinedByString:@" " ] ; | |
return result ; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment