Go4Expert

Go4Expert (http://www.go4expert.com/)
-   C (http://www.go4expert.com/articles/c-tutorials/)
-   -   Evolution of sleep() C Function (http://www.go4expert.com/articles/evolution-sleep-c-function-t26891/)

poornaMoksha 9Oct2011 17:52

Evolution of sleep() C Function
 
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 :
SYNOPSIS
#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.
pause() function

From Linux man page :
SYNOPSIS
#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.
Now, lets come to the main topic 'sleep()'.

From Linux Man page :
SYNOPSIS
#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.
Can anyone think of simulating a 'sleep()' function with the help of alarm() and pause() functions?

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();
    ...
    ...
}

Well, we all think that this will work, so lets convert it into code. Here is the actual piece(the first implementation):

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 */
}

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:

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 */
}

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 :

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 */

}

Well, nothing is perfect. An assignment for you guys is to find out the latest implementation of sleep() and find any loophole in it. :-)

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 :-) .


All times are GMT +5.5. The time now is 05:51.