0

I have the following piece of code in a cocoa Application for OSX:

void *callbackInfo = NULL; // could put stream-specific data here.
            FSEventStreamRef stream;
            CFAbsoluteTime latency = 1.0; /* Latency in seconds */

            /* Create the stream, passing in a callback */
            stream = FSEventStreamCreate(NULL,
                                         &mycallback,
                                         callbackInfo,
                                         pathsToWatch,
                                         kFSEventStreamEventIdSinceNow, /* Or a previous event ID */
                                         latency,
                                         kFSEventStreamCreateFlagFileEvents//kFSEventStreamCreateFlagNone /* Flags explained in reference */
                                         );

            /* Create the stream before calling this. */
            FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(),kCFRunLoopDefaultMode);
            FSEventStreamStart(stream);



            CFRunLoopRun();

        }

This code gets notified about files system changes and reacts accordingly (the callback function is not in the snippet).

My problem now is that the CFRunLoopRun() is blocking. I.e. the further execution of the code stops. However, I'm looking for a possibility that I can start the observation of file system changes but also stop it again (e.g. from another object).

One option that I thought of would be to only start the loop for a second and check for a global variable afterwards. However, I usually don't like global variables....

Does anybody here have a nice and handy idea, how this could be solved? Would it be a good idea to buy the overhead and put the execution into a separate thread?

Thanks in advance! Norbert

Norbert
  • 735
  • 8
  • 19
  • Why don't you just leave out the call to `CFRunLoopRun()` and just allow execution return to the app's main event loop? The main event loop is built on the run loop, so allowing that to run **is** allowing the run loop to run. You will get file system events as well as user events like mouse and keyboard, etc. If you eventually want to stop observing file system events, just call `FSEventStreamStop()`. – Ken Thomases Aug 01 '15 at 19:44
  • With other words: the run loop already runs and handles the events from the FS event stream? So the only thing I have to ensure is that the current thread that the object lives in does not end!? That sounds so simple that I did not even think about it ;-) – Norbert Aug 01 '15 at 21:18
  • After many tries and debugging sessions I'm almost to give up on this :-( what I did not mention in my original post is that this all happens in a command line app. And there it seems that the runloop has to be called explicitly. Whatever I do: the FSEvents are not handled if I don't call CFRunLoopRun() but when I call it, it blocks the rest of the code being executed. – Norbert Mar 17 '16 at 21:20
  • FSEvents is a notification API. How do you expect to receive those notifications? What should the thread your code is running on be doing in the meantime? How would the callback be called on it if it's busy doing other stuff? Do you want to poll (which is against the purpose of a notification API)? You could schedule a timer on the run loop and, when it fires, stop the run loop. You can use a dispatch queue instead of a run loop (`FSEventStreamSetDispatchQueue()`), in which case your callback will be called on another thread. You need to explain more clearly how you want this to work. – Ken Thomases Mar 18 '16 at 00:35
  • I obviously underestimated the complexity here :-( What I want to achieve/do in my command line tool is round about this: 1) Create a custom object. This object brings an FSEventStream that is linked to the standard runloop 2) start an observer function of this object. This will start the event stream 3) Repeat steps 1 and 2 multiple times' 4) listen to the events and handle them 5) The user shall be able to interact with the app (by pressing certain buttons that may cancel the app or influence the behavior of the callback routine for the events – Norbert Mar 18 '16 at 17:10
  • Sorry, that was imprecise in the english language. When saying button I meant a certain key on the keyboard. – Norbert Mar 19 '16 at 09:07

2 Answers2

1

CFRunLoopRun() runs the current run loop forever, until somebody registered in the runloop calls CFRunLoopStop(). This is not what you want to do unless you're doing something very fancy.

If you want to the FSEventStream callback to run, you just register it with the runloop and leave it, you don't have to do anything explicitly to the runloop after that, registering the even stream as a source is all you have to do.

If you want to stop observing the stream you call FSEventStreamStop() on it.

iluvcapra
  • 9,436
  • 2
  • 30
  • 32
0

A Cocoa application has an implicit run loop, so just delete CFRunLoopRun();

Consider to use a dispatch queue via Grand Central Dispatch (GCD) rather than schedule the stream on a run loop

vadian
  • 274,689
  • 30
  • 353
  • 361