My program has a main function that will execute a series of tests, which will include some processes that needed to be terminated if the user Ctrl-C.
The following code is the correct workflow
- (void)run {
[process1 start];
[process2 start];
Running...
[process1 stop];
[process2 stop];
}
But the user may use Ctrl-C to stop my program, and I don't want to leave process1 and process2 running because they are dangerous. How can stop them properly? Kill
command cannot be used because I don't know the dynamic process names, and the only way is to call stop
on those two ObjC objects.
Technically inside your Objective-C method you can't catch a SIGINT
signal (generated by user upon pressing CTRL-C) - your program gets interrupted.
The control of flow at this point is handled by kernel. The state of cpu registers valid during execution of your objective-c run
method are inevitably lost. Hence you can't continue executing your run
method anymore.
What you can do is register a custom SIGINT
handler using C API.
#import <signal.h>
void sigHandler(int sig) {
//as noted by @bbum list of functions with guaranteed behavior in sighandler is very limited
//obj-c runtime is definitely NOT on the list
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
[process1 stop];
[process2 stop];
});
//give extra time for the async part to run
usleep(50);
}
int main(int argc, const char * argv[]) {
signal(SIGINT, sigHandler);
@autoreleasepool {
//your normal program launch goes here
}
return 0;
}
If you have a console app alternatively you could consider route here
so basically disable CTRL-C firing SIGINT
and asynchronously handle the keyboard reading for CTRL-C yourself. The main drawback is if your program would hit an endless loop it cannot be interrupted with CTRL-C. So to minimise this effect you could set this special terminal mode at the beginning of your run
method and restore it once you're done.