Its probably better to do this with threads in general.
However, there is a way that will work with limitations for simple applications.
Please forgive the use of a C++11 lambda, I think it makes things a little clearer here.
namespace
{
sigjmp_buf context;
} // namespace
void nonBlockingCall(int timeoutInSeconds)
{
struct sigaction* oldAction = nullptr;
struct sigaction newAction;
if (sigsetjmp(::context,0) == 0)
{
// install a simple lambda as signal handler for the alarm that
// effectively makes the call time out.
// (e.g. if the call gets stuck inside something like poll() )
newAction.sa_handler = [] (int) {
siglongjmp(::context,1);
};
sigaction(SIGALRM,&newAction,oldAction);
alarm(timeoutInSeconds); //timeout by raising SIGALM
BLOCKING_LIBRARY_CALL
alarm(0); //cancel alarm
//call did not time out
}
else
{
// timer expired during your call (SIGALM was raised)
}
sigaction(SIGALRM,oldAction,nullptr);
}
Limitations:
This is unsafe for multi-threaded code.
If you have multi-threaded code it is better to have the timer in a
monitoring thread and then kill the blocked thread.
timers and signals from elsewhere could interfere.
Unless BLOCKING_LIBRARY_CALL documents its behaviour very well you may be in undefined behaviour land.
- It will not free resources properly if interrupted.
- It might install signal handlers or masks or raise signals itself.
If using this idiom in C++ rather than C you must not allow any objects to be constructed or destroyed between setjmp and longjmp.
Others may find additional issues with this idiom.
I thought I'd seen this in Steven's somewhere, and indeed it is discussed in the signals chapter where it discusses using alarm() to implement sleep().