8

I'm using the code example by David Hamrick to monitor a file using GCD.

int fildes = open("/path/to/config.plist", O_RDONLY);

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
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, ^
{
    //Reload the config file
});
dispatch_source_set_cancel_handler(source, ^
{
    //Handle the cancel
});
dispatch_resume(source);

I want to use to monitor a change of a plist. I get a notification after the first change but not for the following changes. Why?

Monolo
  • 18,205
  • 17
  • 69
  • 103
Matthieu Riegler
  • 31,918
  • 20
  • 95
  • 134

2 Answers2

11

You can indeed just re-open the file and re-register a source (deleting the previous one) when DISPATCH_VNODE_DELETE is received. Or you can use a call which was devised for just this kind of scenario, namely dispatch_io_create_with_path() - that will not only watch by path, it will open the file for you and let you read the contents asynchronously.

Since you asked (not sure which technique you asked for, but here's the simplest) here's a stand-alone code sample:

#include <dispatch/dispatch.h>
#include <stdio.h>

int main(int ac, char *av[])
{
  int fdes = open("/tmp/pleasewatchthis", O_RDONLY);
  dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
  void (^eventHandler)(void), (^cancelHandler)(void);
  unsigned long mask = DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_EXTEND | DISPATCH_VNODE_ATTRIB | DISPATCH_VNODE_LINK | DISPATCH_VNODE_RENAME | DISPATCH_VNODE_REVOKE;
  __block dispatch_source_t source;

  eventHandler = ^{
    unsigned long l = dispatch_source_get_data(source);
    if (l & DISPATCH_VNODE_DELETE) {
      printf("watched file deleted!  cancelling source\n");
      dispatch_source_cancel(source);
    }
    else {
      // handle the file has data case
      printf("watched file has data\n");
    }
  };
  cancelHandler = ^{
    int fdes = dispatch_source_get_handle(source);
    close(fdes);
    // Wait for new file to exist.
    while ((fdes = open("/tmp/pleasewatchthis", O_RDONLY)) == -1)
      sleep(1);
    printf("re-opened target file in cancel handler\n");
    source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fdes, mask, queue);
    dispatch_source_set_event_handler(source, eventHandler);
    dispatch_source_set_cancel_handler(source, cancelHandler);
    dispatch_resume(source);
  };

  source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE,fdes, mask, queue);
  dispatch_source_set_event_handler(source, eventHandler);
  dispatch_source_set_cancel_handler(source, cancelHandler);
  dispatch_resume(source);
  dispatch_main();
}
jkh
  • 3,246
  • 16
  • 13
  • I'm pretty new to GCD, do you have some example using that code ? – Matthieu Riegler Jul 08 '12 at 11:50
  • As I said below I already got this solution. I was talking about one with dispatch_io_create_with_path. – Matthieu Riegler Jul 09 '12 at 13:50
  • See http://developer.apple.com/library/ios/#documentation/FileManagement/Conceptual/FileSystemProgrammingGUide/TechniquesforReadingandWritingCustomFiles/TechniquesforReadingandWritingCustomFiles.html - using channels somewhat more verbose, too verbose for a trivial code snippet. Upon reflection, for your application (which is very simple), I think I'd stick with the nested source registration technique. Channels are intended for more complicated scenarios. – jkh Jul 09 '12 at 16:14
2

After a little bit a research I found out :

=> I was getting the flag DISPATCH_VNODE_DELETE

I was monitoring ~/Library/Preferences/com.apple.dock.plist As I found out, changing the dock orientation, deletes the original file and replaces it with a new one.

Therefore, the monitoring stopped.

The same author proposes a solution where in case of deletion, the GCD block call itself to continue the monitoring.

Matthieu Riegler
  • 31,918
  • 20
  • 95
  • 134