In this article, we will discuss different implementations of sleep functions that had flaws to understand how sleep function evolved. Before discussing the sleep function implementations, lets first understand briefly the following two functions :
alarm() function
From Linux Man page :
From Linux man page :
From Linux Man page :
Ok guyz, I have an Idea.
What if we do something like :
Well, we all think that this will work, so lets convert it into code. Here is the actual piece(the first implementation):
Huff, was not that difficult or was it? :-)
Now, the intelligent viewers would have already started searching for the loopholes as I have already told that this is flawed.
So, whats the flaw ?
Well, besides others, the major loop hole lies in the time window between the call to alarm() and the call to pause(). What if this time gap is more than the 'seconds' after which the timer for alarm expires?? The pause() function will be called after the signal handler has been executed and the process will be in suspended state forever(assuming no signal occurs there after). Holy cow!!! That was a good catch by those of you who got it bang on.
Well, these geeks who actually write these logics do make mistakes and if you believe in yourself, you can also be like them one day. But the truth prevails, you'll still make mistakes as these geeks do. :-)
OK, enough of motivation. When the above limitation was reported, another implementation came along. Here is the piece of code:
Well, this seemed to be a promising one as it cleared off the earlier problem where a process may hang. Here in this implementation, even if the alarm goes off before the function pause() is called then longjmp ensured that process did not hang forever. Well, that smart coding. But unfortunately, a bug exposed in this implementation too as suppose we call Mysleep2() for 3 seconds and in between these 3 seconds, another signal (say generated through CTRL+C by user) is occurs and is being handled by its handler. Then as soon as the 3 second alarm expires, the call to longjmp() from handler of alarm signal will cause the handler of SIGINT(generated by user) to abort abruptly.
The code foe above explanation is something like this :
Well, nothing is perfect. An assignment for you guys is to find out the latest implementation of sleep() and find any loophole in it. :-)
In this article we discussed the evolution of sleep() function and how it is being constantly updated with every new loophole being surfaced out. This also shows that programming with signals is not a cake walk :-) .
alarm() function
From Linux Man page :
SYNOPSISpause() function
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
DESCRIPTION
alarm() arranges for a SIGALRM signal to be delivered to the calling process in seconds seconds.
If seconds is zero, no new alarm() is scheduled.
In any event any previously set alarm() is canceled.
RETURN VALUE
alarm() returns the number of seconds remaining until any previously scheduled alarm was due to be delivered, or zero if there was no previously
scheduled alarm.
From Linux man page :
SYNOPSISNow, lets come to the main topic 'sleep()'.
#include <unistd.h>
int pause(void);
DESCRIPTION
pause() causes the calling process (or thread) to sleep until a signal is delivered that either terminates the process or causes the invocation of
a signal-catching function.
RETURN VALUE
pause() only returns when a signal was caught and the signal-catching function returned. In this case pause() returns -1, and errno is set to
EINTR.
From Linux Man page :
SYNOPSISCan anyone think of simulating a 'sleep()' function with the help of alarm() and pause() functions?
#include <unistd.h>
unsigned int sleep(unsigned int seconds);
DESCRIPTION
sleep() makes the current process sleep until seconds seconds have elapsed or a signal arrives which is not ignored.
RETURN VALUE
Zero if the requested time has elapsed, or the number of seconds left to sleep.
Different earlier Implementations
Ok guyz, I have an Idea.
What if we do something like :
Code:
/* pseudo code for my implementation of sleep() */
//Signal handler for SIGALRM
void sig_alrm(int sig)
{
/* do some stuff */
}
// My_sleep()
unsigned int my_sleep(unsigned int seconds)
{
// Set signal handler for SIGALRM
...
...
alarm(seconds);
...
pause();
...
...
}
Code:
#include <signal.h>
#include <unistd.h>
static void
sig_alarm_handler(int sig)
{
/* dummy definition, nothing much to do */
}
unsigned int
Mysleep1(unsigned int seconds)
{
if (signal(SIGALRM, sig_alarm_handler) == SIG_ERR)
return(seconds);
alarm(seconds); /* start the timer */
pause(); /* Suspend the process until a signal occurs */
return(alarm(0)); /* turn off timer, return un-slept time */
}
Now, the intelligent viewers would have already started searching for the loopholes as I have already told that this is flawed.
So, whats the flaw ?
Well, besides others, the major loop hole lies in the time window between the call to alarm() and the call to pause(). What if this time gap is more than the 'seconds' after which the timer for alarm expires?? The pause() function will be called after the signal handler has been executed and the process will be in suspended state forever(assuming no signal occurs there after). Holy cow!!! That was a good catch by those of you who got it bang on.
Well, these geeks who actually write these logics do make mistakes and if you believe in yourself, you can also be like them one day. But the truth prevails, you'll still make mistakes as these geeks do. :-)
OK, enough of motivation. When the above limitation was reported, another implementation came along. Here is the piece of code:
Code:
#include <setjmp.h>
#include <signal.h>
#include <unistd.h>
static jmp_buf env;
static void sig_alarm_handler(int sig)
{
longjmp(env, 1);
}
unsigned int Mysleep2(unsigned int seconds)
{
if (signal(SIGALRM, sig_alarm) == SIG_ERR)
return(seconds);
if (setjmp(env) == 0) {
alarm(seconds); /* start the timer */
pause(); /* suspend the process until a signal is caught */
}
return(alarm(0)); /* turn off timer, return un-slept time */
}
The code foe above explanation is something like this :
Code:
static jmp_buf env;
static void sig_alarm_handler(int sig)
{
longjmp(env, 1);
}
unsigned int Mysleep2(unsigned int seconds)
{
if (signal(SIGALRM, sig_alarm) == SIG_ERR)
return(seconds);
if (setjmp(env) == 0) {
alarm(seconds); /* start the timer */
pause(); /* suspend the process until a signal is caught */
}
return(alarm(0)); /* turn off timer, return unslept time */
}
int
main(void)
{
unsigned int unslept;
if (signal(SIGINT, sig_int) == SIG_ERR)
err_sys("signal(SIGINT) error");
unslept = Mysleep2(5);
printf("sleep2 returned: %u\n", unslept);
exit(0);
}
static void
sig_int(int signo)
{
/* do some stuff for more than 5 seconds */
}
Conclusion
In this article we discussed the evolution of sleep() function and how it is being constantly updated with every new loophole being surfaced out. This also shows that programming with signals is not a cake walk :-) .
