Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CFRunLoopRun() vs [NSRunLoop run]

I have an NSRunLoop object, to which I attach timers and streams. It works great. Stopping it is another story alltogether.

I run the loop using [runLoop run].

If I try to stop the loop using CRunLoopStop([[NSRunLoop currentRunLoop] getCFRunLoop]), the loop won't stop. If I start the loop using CRunLoopRun() instead, it works. I have also made sure that the call is made on the correct thread (the one running my custom run loop). I have debugged this with pthread_self().

I found a mailing list archive, where a developer said "don't bother using CRunLoopStop() if you started the loop using the run method of NSRunLoop". I can understand why it is the way it is - you normally pair up initializers and finalizers from the same set of functions.

How do you stop an NSRunLoop without "resorting to CF"? I don't see a stop method on NSRunLoop. The docs says that you can stop a run loop in three ways:

  1. Configure the run loop to run with a timeout value
  2. Tell the run loop to stop using CFRunLoopStop()
  3. Remove all input sources, but this is an unreliable way to stop the run loop because you can never know what stuck what into the run loop behind your back

Well, I already tried 2. and there's an "ugly" feel to it, because you have to dig into CF. 3. is out of the question - I don't fancy non deterministic code.

This leaves us with 1. If I understand the docs correctly, you cannot "add" a timeout to an already existing run loop. You can only run new run loops with a timeout. If I run a new run loop, it will not solve my problem, as it will only create a nested run loop. I'll still pop right back into the old one, the same I wanted to stop... right? I might have misunderstood this one. Also, I don't want to run the loop with a timeout value. If I do, I'll have to make a trade off between burning CPU cycles (low timeout value) and responsiveness (high timeout value).

This is the setup I have right now (pseudo code-ish):

Communicator.h

@interface Communicator : NSObject {
    NSThread* commThread;
}

-(void) start;
-(void) stop;
@end

Communicator.m

@interface Communicator (private)
-(void) threadLoop:(id) argument;
-(void) stopThread;
@end

@implementation Communicator
-(void) start {
    thread = [[NSThread alloc] initWithTarget:self 
                                     selector:@selector(threadLoop:)
                                       object:nil];
    [thread start];
}

-(void) stop {
    [self performSelector:@selector(stopThread)
                 onThread:thread
               withObject:self
            waitUntilDone:NO];
    // Code ommitted for waiting for the thread to exit...
    [thread release];
    thread = nil;
}
@end

@implementation Communicator (private)
-(void) stopThread {
    CRunLoopStop([[NSRunLoop currentRunLoop] getCFRunLoop]);
}

-(void) threadLoop:(id) argument {
    // Code ommitted for setting up auto release pool

    NSRunLoop* runLoop = [NSRunLoop currentRunLoop];

    // Code omitted for adding input sources to the run loop

    CFRunLoopRun();
    // [runLoop run]; <- not stoppable with 

    // Code omitted for draining auto release pools

    // Code omitted for signalling that the thread has exited
}
@endif

What am I to do? Is it common/a good pattern to mess around with CF? I don't know Foundation well enough. Is interfering in the CF layer possibly dangerous (with respect to memory corruption, inconsistencies, memory leaks)? Is there a better pattern to achieve what I am trying to achieve?

like image 416
Jörgen Sigvardsson Avatar asked Dec 21 '11 13:12

Jörgen Sigvardsson


People also ask

What is CFRunLoopRun?

Call CFRunLoopRun() to run the current thread's run loop in the default mode until the run loop is stopped with CFRunLoopStop(_:) . You can also call CFRunLoopRunInMode(_:_:_:) to run the current thread's run loop in a specified mode for a set period of time (or until the run loop is stopped).

What is run loop mode?

A run loop is an event processing loop that you use to schedule work and coordinate the receipt of incoming events. The purpose of a run loop is to keep your thread busy when there is work to do and put your thread to sleep when there is none. Run loop management is not entirely automatic.

What is run loop IOS Swift?

A RunLoop is a programmatic interface to objects that manage input sources, such as touches for an application. A RunLoop is created and managed by the system, who's also responsible for creating a RunLoop object for each thread object.


1 Answers

You're doing well. There's not problem to use CoreFoundation when you cannot achieve your goal with Foundation. As CoreFoundation is C, it's easier to mess up with memory management but there is no intrinsic danger in using CFRunLoop rather than NSRunLoop (sometimes it may even be safer: CFRunLoop API are thread-safe whereas NSRunLoop isn't).

If you want to stop your NSRunLoop, you can run it using runMode:beforeDate:. runMode:beforeDate: returns as soon as an input source is processed so you don't need to wait until the timeout date is reached.

 NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
 NSDate *date = [NSDate distantFuture];
 while ( !runLoopIsStopped && [runLoop runMode:NSDefaultRunLoopMode beforeDate:date] );

Then, to stop the run loop, you just need to set runLoopIsStopped to YES.

like image 87
Nicolas Bachschmidt Avatar answered Oct 21 '22 06:10

Nicolas Bachschmidt