11

I recently switched from using kqueue to GCD dispatch sources to monitor file changes. This has worked out well and resulted in a much simpler API. I documented my switch here. The only issue I have is that now I cannot access the flags on the event that I was able to in kqueue. For example with kqueue I was able to check if the file was deleted, renamed, or it's attributes were changed with the following:

struct kevent event;

...

if(event.flag & EV_DELETE)
{
    printf("File was deleted\n");
}

Is this API not available with GCD or do I need to set up dispatch sources up for each flag I would like to listen to. Or is it best to use kqueue since it provides greater visibility to the event that has occurred.

Cœur
  • 37,241
  • 25
  • 195
  • 267
DHamrick
  • 8,338
  • 9
  • 45
  • 62
  • 3
    I didn't actually read your question, but I thumbed it up so your reputation can be 1337. Ok I'll read it now. – morningstar Oct 13 '11 at 06:47

2 Answers2

9

I found the answer in the Concurrency Programming Guide. I had first looked in the GCD Reference but without luck. The relevant line from the guide was

For a descriptor dispatch source that monitors file system activity, this function returns a constant indicating the type of event that occurred. For a list of constants, see the dispatch_source_vnode_flags_t enumerated type.

Here is an example of how you can use it.

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
int fildes = open("path/to/some/file", O_EVTONLY);
__block dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE,fildes, 
                                                  DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_EXTEND | DISPATCH_VNODE_ATTRIB | DISPATCH_VNODE_LINK | DISPATCH_VNODE_RENAME | DISPATCH_VNODE_REVOKE,
                                                  queue);
dispatch_source_set_event_handler(source, ^
{
    unsigned long flags = dispatch_source_get_mask(source);
    if(flags & DISPATCH_VNODE_DELETE)
        printf("DISPATCH_VNODE_DELETE\n");
    if(flags & DISPATCH_VNODE_WRITE)
        printf("DISPATCH_VNODE_WRITE\n");
    if(flags & DISPATCH_VNODE_EXTEND)
        printf("DISPATCH_VNODE_EXTEND\n");
    if(flags & DISPATCH_VNODE_ATTRIB)
        printf("DISPATCH_VNODE_ATTRIB\n");
    if(flags & DISPATCH_VNODE_LINK)
        printf("DISPATCH_VNODE_LINK\n");
    if(flags & DISPATCH_VNODE_RENAME)
        printf("DISPATCH_VNODE_RENAME\n");
    if(flags & DISPATCH_VNODE_REVOKE)
        printf("DISPATCH_VNODE_REVOKE\n");
});
dispatch_source_set_cancel_handler(source, ^(void) 
{
    close(fildes);
});
dispatch_resume(source);
Cœur
  • 37,241
  • 25
  • 195
  • 267
DHamrick
  • 8,338
  • 9
  • 45
  • 62
  • 1
    Thank you, that's amazing. I used the one on your [blog post](http://www.davidhamrick.com/2011/10/13/Monitoring-Files-With-GCD-Being-Edited-With-A-Text-Editor.html). However, you should correct the line 17 from `[blockSelf watchStyleSheet:path];` to `[blockSelf watchConfigFile:path];`. – Stéphane Bruckert Oct 10 '14 at 16:27
  • there is no point for `source` to be `__block` as it is never assigned to – user102008 Oct 28 '15 at 22:36
  • also it should be `dispatch_source_get_data` instead of `dispatch_source_get_mask`. The glory of using someone else's code without actually understanding it... – Mojo66 Apr 23 '20 at 18:16
5

You may change *dispatch_source_get_mask(source)* to *dispatch_source_get_data(source)*, as dispatch_source_get_mask(source) returns all the flags you passed in the creation of the handler instead of the event generated.