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:
- Configure the run loop to run with a timeout value
- Tell the run loop to stop using
CFRunLoopStop()
- 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?