Question

It seems as though I can create a dispatch_source with a type of DISPATCH_SOURCE_TYPE_VNODE and provide the file descriptor for the directory I'm interested in but I'm not sure what flags I ought to set to detect a file creation.

What I ultimately want to do is to attach a start using dispatch_io_read to read data from the file as it is written but to do this I need the file descriptor for the file which is not available until it's created.

For clarity, I do not directly create the file and so can't easily determine (without polling) when it exists.

Was it helpful?

Solution

Here's an example of how you would use GCD for that:

@implementation AppDelegate
{
    dispatch_source_t _source;
    NSArray* mFiles;
    NSString* mPath;
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    // Add an observer
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(directoryDidChange:) name:@"FilesChanged" object:self];

    mPath = @"/your/dir";

    int fildes = open(mPath.UTF8String, O_RDONLY);

    dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);

    dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fildes, DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_EXTEND | DISPATCH_VNODE_RENAME | DISPATCH_VNODE_REVOKE, queue);

    dispatch_source_set_event_handler(source, ^{
        [self updateListWithNotification: YES];
    });

    dispatch_source_set_cancel_handler(source, ^{
        close((int)dispatch_source_get_handle(source));
    });

    _source = source;

    dispatch_resume(source); // Start monitoring

    dispatch_async(queue, ^{
        [self updateListWithNotification: NO];
    });
}

- (void)applicationWillTerminate:(NSNotification *)notification
{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"FilesChanged" object:self];
    dispatch_source_cancel(_source); // Stop monitoring
}

- (void)updateListWithNotification: (BOOL)withNotification
{
    // Our manipulation of state here is OK because we know this only ever gets called on a serial queue
    mFiles = mFiles ?: [NSArray array];

    NSArray* contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath: mPath error: nil] ?: [NSArray array];

    if (withNotification)
    {
        NSSet* oldFiles = [NSSet setWithArray: mFiles];
        NSSet* newFiles = [NSSet setWithArray: contents];

        NSMutableSet* addedFiles = [newFiles mutableCopy]; [addedFiles minusSet: oldFiles];
        NSMutableSet* removedFiles = [oldFiles mutableCopy]; [removedFiles minusSet: newFiles];
        NSDictionary* ui = @{ @"FilesRemoved" : removedFiles, @"FilesAdded" : addedFiles };
        dispatch_async(dispatch_get_main_queue(), ^{
            [[NSNotificationCenter defaultCenter] postNotificationName: @"FilesChanged" object: self userInfo: ui];
        });
    }

    mFiles = contents;
}


- (void)directoryDidChange: (NSNotification*)n
{
    NSLog(@"Directory %@ changed.\nFiles removed: %@\nFiles added: %@", mPath, n.userInfo[@"FilesRemoved"], n.userInfo[@"FilesAdded"]);
}

@end

This uses GCD to monitor the directory and then posts an NSNotification to the main thread with a set of files added and removed since the last notification.

OTHER TIPS

Answered from this link.

In short, no but...

You cannot use dispatch_io_create to handle this as it does not work with directories, you will get a posix error 21 when trying to set up the channel.

You can however use dispatch_source_create with type DISPATCH_SOURCE_TYPE_VNODE and mask DISPATCH_VNODE_WRITE as follows:

// Open FD for events only
int dirFD = open(filename, O_EVTONLY);
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_source_t src = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE,
                              dirFD,
                              DISPATCH_VNODE_WRITE,
                              queue);

// Set the block to be submitted in response to an event
dispatch_source_set_event_handler(src, ^{
    fprintf(stderr, "Directory changed!\n");

    // TODO: Identify new file here

    dispatch_source_cancel(src);
});

// Set the block to be submitted in response to source cancellation
dispatch_source_set_cancel_handler(src, ^{
    close(dirFD);
    fprintf(stderr, "Cancel!");
});

dispatch_resume(src);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top