How to Exit a Runloop

how to stop a timer triggered runloop?

You need to send a invalidate message to the timer to remove it from the RunLoop.

See Doc

[anyTimer invalidate];

Performing selector at beginning / end of run loop

You can create a CFRunLoopObserver which will call a block on loop entry and exit. You use CFRunLoopAddObserver to add your observer to the run loop, and CFRunLoopGetMain to obtain the run loop to add to.

Here is a rather pointless example using these:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(NULL, (kCFRunLoopEntry | kCFRunLoopExit), YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity)
{
static unsigned long count = 0;
NSLog(@"activity %lu: %@", ++count, (activity & kCFRunLoopEntry ? @"Enter" : @"Exit"));
});
CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
}

This simply installs an observer which logs every entry & exit to the run loop. You can run it as a complete application in Xcode and see how many times the run loop goes around.

Note that CFRunLoopObserverCreateWithHandler returns a reference you own, if you remove the observer you are responsible for deallocation.

Stop a NSRunLoop in global queue

A couple of thoughts:

  1. If you look at the documentation for run, they show a pattern that solves your problem:

    If no input sources or timers are attached to the run loop, this method exits immediately; otherwise, it runs the receiver in the NSDefaultRunLoopMode by repeatedly invoking runMode:beforeDate:. In other words, this method effectively begins an infinite loop that processes data from the run loop’s input sources and timers.

    Manually removing all known input sources and timers from the run loop is not a guarantee that the run loop will exit. OS X can install and remove additional input sources as needed to process requests targeted at the receiver’s thread. Those sources could therefore prevent the run loop from exiting.

    If you want the run loop to terminate, you shouldn't use this method. Instead, use one of the other run methods and also check other arbitrary conditions of your own, in a loop. A simple example would be:

    BOOL shouldKeepRunning = YES;        // global
    NSRunLoop *theRL = [NSRunLoop currentRunLoop];
    while (shouldKeepRunning && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);

    where shouldKeepRunning is set to NO somewhere else in the program.

  2. Having said that, if I were going to start another thread, I wouldn't use up one of the global worker threads, but rather I'd just instantiate my own NSThread.

  3. More critically, depending upon what you're trying to do in this other thread, there are generally much better other patterns than establishing your own run loop.

    For example, if I wanted to have timer run something in another queue, I'd use a dispatch timer instead:

    @property (nonatomic, strong) dispatch_source_t timer;

    and then instantiate and start dispatch timer source to run on your designated GCD queue:

    dispatch_queue_t queue = dispatch_queue_create("com.domain.app.polltimer", 0);
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    dispatch_source_set_timer(self.timer, dispatch_walltime(NULL, 0), kPollFrequencySeconds * NSEC_PER_SEC, 1ull * NSEC_PER_SEC);

    dispatch_source_set_event_handler(self.timer, ^{
    <#code to be run upon timer event#>
    });

    dispatch_resume(self.timer);

    Or, if you want to use NSTimer, just schedule that on the main runloop, and have the method it calls dispatch the time consuming task to the background queue at that time. But, either way, I'd avoid adding the overhead of a second run loop.

  4. Having shown you better ways to use timers in background threads, now that you describe the intent (polling a server) I'd actually recommend against using timer at all. Timers are useful when you want some action to be initiated at some regular interval. But in this case, you probably want to initiate the next server request after a certain amount of time after the previous request finished. So, in the completion block of the previous request, you might do something like:

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 20.0 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    <#code to initiate next request#>
    });
  5. Also, I'd personally want to make sure there was a very compelling reason to polling your server. Polling always seems so intuitively appealing and logical, but it is an extravagant use of the user's battery, CPU and data plan. And in those cases where you need the client to respond to server changes quickly, there are often better architectures (sockets, push notifications, etc.).

How do i exit a loop running in threadA after I get a specified input in threadB? in java

The problem is that your two threads have two separate instances of StopThread. You need them to share a single instance so that they can communicate.

One way to do this is to create subclasses of Thread or Runnable so that you can pass the shared StopThread instance into each of them in their constructors.

Another way is to make a simple variable in the enclosing class (both threads will be able to 'see' this variable so they can use it to communicate).

How to remove timer from runloop immediately

The first problem here is that your timer is being added to a run loop on some arbitrary background thread. (i.e. the thread will have been created by GCD to service the background queue) You can do this, but it kind of makes no sense.

That aside, you said that what you want to have happen is for this run loop to exit, but in the documentation for the -run method, it says the following:

Manually removing all known input sources and timers from the run loop
is not a guarantee that the run loop will exit. OS X can install and
remove additional input sources as needed to process requests targeted
at the receiver’s thread. Those sources could therefore prevent the
run loop from exiting.

If you want the run loop to terminate, you shouldn't use this method.
Instead, use one of the other run methods and also check other
arbitrary conditions of your own, in a loop.

You will need to spin the event loop yourself if you want to exit it when the timer is invalid. For example:

while (_backgroundTimer.valid && [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow: 0.1]]);

This will exit the run loop a maximum of 0.1 (- epsilon) seconds after the timer is invalidated.

CFRunLoopRunInMode is exiting with code 1, as if nothing was added

You can't run a run loop in kCFRunLoopCommonModes. This is clearly stated in the documentation for CFRunLoopRunInMode().

kCFRunLoopCommonModes is a virtual mode. It's basically a set of other modes. It can only be used when adding (or removing) a source to a run loop to say "monitor this source when the run loop is run in any of the modes in the set". But when you run a run loop, you have to run it in a specific, real mode, not this virtual mode which represents a set of other modes.

I recommend that, when you're working on a private thread and only want to monitor private sources, that you add the source to a custom mode and run the run loop in that mode. A custom mode is just a string with a unique value. For example, something like "com.yourcompany.yourproject.yourmodespurpose". Using a custom mode makes sure that the run loop never does anything unexpected, like firing a source added by the frameworks.

You must not release aLoop. Functions that don't have "Create" or "Copy" in their name do not give you ownership.

You will need a loop around your call to CFRunLoopRunInMode() because it will return every time it handles an event from your source (kCFRunLoopRunHandledSource == 4) or hits the timeout (kCFRunLoopRunTimedOut == 3). You should break out of the loop if it ever returns anything else.



Related Topics



Leave a reply



Submit