How to Use Signal Inside a C++ Class

Is it possible to use signal inside a C++ class?

The second parameter of signal should be a pointer to a function accepting an int and returning void. What you're passing to signal is a pointer to a member function accepting an int and returning void (its type being void (myClass::*)(int)). I can see three possibilities to overcome this issue:

1 - Your method myHandler can be static: this is great, make it static

class myClass 
{
public:
void myFunction ()
{
signal(SIGIO, myClass::myHandler);
}

static void myHandler (int signum)
{
// handling code
}
};

2 - Your method shouldn't be static: if you're planning to use signal with only one instance, you can create a private static object, and write a static method that simply call the method on this object. Something along the lines of

class myClass 
{
public:
void myFunction ()
{
signal(SIGIO, myClass::static_myHandler);
}

void myHandler (int signum)
{
// handling code
}

static void static_myHandler(int signum)
{
instance.myHandler(signum);
}

private:
static myClass instance;
};

3 - However, if you're planning on using the signal with multiple instances, things will get more complicated. Perhaps a solution would be to store each instance you want to manipulate in a static vector, and invoking the method on each of these :

class myClass
{
public:
void myFunction () // registers a handler
{
instances.push_back(this);
}

void myHandler (int signum)
{
// handling code
}

static void callHandlers (int signum) // calls the handlers
{
std::for_each(instances.begin(),
instances.end(),
std::bind2nd(std::mem_fun(&myClass::myHandler), signum));
}
private:
static std::vector<myClass *> instances;
};

and somewhere, do a single call to

signal(SIGIO, myClass::callHandlers);

But I think that if you end up using the last solution, you should probably think about changing your handling design :-)!

How to register a signal handler as a class method?

Being a C function, signal can only take a plain function pointer, not arbitrary callable types. You'll need a non-member wrapper function, and a global variable to store this, in order to call a member function from a signal handler.

static A * signal_object;
extern "C" void signal_handler(int signum) {signal_object->f(signum);}

// later...
signal_object = this;
signal(SIGSEGV, signal_handler);

How to use std::bind function as a signal handler in C++?

The result of std::bind is an unspecified function object whose type cannot be converted into void (*)(int). Try encapsulating it:

void handler_foo(int signal)
{
return terminate_or_interrupt_handler(signal_flag, signal);
}

Or, if C++11 is available, a lambda might be better:

signal(SIGTERM, [](int signal) { return terminate_or_interrupt_handler(signal_flag, signal); });

Note that since signal_flag is a global variable (namespace-scope variable), no capture is required. A non-capturing lambda can be implicitly converted into the corresponding function pointer type.

Waiting for a signal inside the signal handler itself

Since you're using signal rather than sigaction, it's important to note the following from the POSIX standard:

When a signal occurs, and func points to a function, it is implementation-defined whether the equivalent of a:

signal(sig, SIG_DFL);

is executed or the implementation prevents some implementation-defined set of signals (at least including sig) from occurring until the current signal handling has completed.

This reflects the two historic signal implementations, SVID and BSD.
Since you're using Ubuntu 18.04, you're likely using glibc, which implements the latter (BSD) semantics. SIGUSR1 is masked while its handler is executing.

Since you want the SVID semantics, in which no signals are masked and you need to reestablish the signal handler each time the signal handler is called, you should replace your signal(SIGUSR1, sig_handler); calls with the following:

struct sigaction sa = { .sa_handler = sig_handler, .sa_flags = SA_NODEFER|SA_RESETHAND };
sigemptyset(&sa.mask);
sigaction(SIGUSR1, &sa, NULL);

The SA_NODEFER flag together with the empty mask means no signals will be masked; SA_RESETHAND means the signal action will be reset to SIG_DFL.

In addition, as the other answers said, you shouldn't be calling printf from within the signal handler. The Linux signal safety man page says which functions can be called. sigaction, signal, and pause are OK. You can use write to write strings instead of printf.

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

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.



Related Topics



Leave a reply



Submit