Search code examples
objective-cobjective-c-runtime

How to catch Ctrl-C signal inside an Objective-C function?


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.


Solution

  • 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.