0

I have a signal where I set a callback handler and then in my function I call alarm(1) so that my program will time out after 1 second. Upon timing out I'd like it to retry to same blocking call recvfrom() until a set MAX_ATTEMPTS which is 5.

Problem:

It only retries 2 times before exiting the program. Any idea what might be going wrong?

/* declate signal for setting alarm */
signal(SIGALRM, timeout_hdler);
int attempts = 0;

while(1) {
    if(attempts > MAX_ATTEMPTS) {
        printf("Connection is not working, EXITING program");
        s.curr_state = CLOSED;
        /* we assume connection is failing so we shut down */
        exit(-1);
    }

    if (s.curr_state == CLOSED) {

        printf("Sending SYN_packet with seqnum: %d...\n", SYN_packet->seqnum);

        if (sendto(sockfd, SYN_packet, sizeof(SYN_packet), 0, server, socklen) == -1) {
            perror("Sendto error");
            exit(-1);
        }

        s.curr_state = SYN_SENT;

        printf("Current state SYN_SENT: %d\n", s.curr_state);
    }

    if (s.curr_state == SYN_SENT) {



        alarm(1);
        attempts++;
        printf("\nAttempt number: %d\n", attempts);
        printf("Waiting for SYNACK_packet...\n");

        if (recvfrom(
                sockfd, SYNACK_packet, sizeof(*SYNACK_packet), 0, (struct sockaddr *) &server, &socklen) == -1)
        {
            if (errno != EINTR) {
                perror("Recvfrom SYNACK_packet error\n");
                s.curr_state = CLOSED;
                exit(-1);
            }
        }

        if ((SYNACK_packet->type == SYNACK) && (validate_packet(SYNACK_packet) == 1)) {

            printf("SYNACK_packet received\n");

            s.address = *(struct sockaddr *) &server;
            s.sock_len = socklen;
            s.curr_state = ESTABLISHED;
            s.seq_num = SYNACK_packet->seqnum;
            printf("Current state ESTABLISHED: %d\n", s.curr_state);
            return sockfd;
        }
    }
}

The handler (which does nothing apart from printing):

void timeout_hdler(int signum) {
    printf("TIMEOUT has occured with signum: %d", signum);
}

Here is the output in my console (from the printf statement):

In Connect() with socket: 4, server: 2, socklen: 16
Sending SYN_packet with seqnum: 67...
Current state SYN_SENT: 1

Attempt number: 1
Waiting for SYNACK_packet...
TIMEOUT has occured with signum: 14
Attempt number: 2
Waiting for SYNACK_packet...
Alarm clock

Why is it exiting the program after only 2 attempts? Ideally I'd like it to retry 5 times before closing the connection (this is a Go Back N implementation using UDP)

UPDATE

I resolved the issue by reinstalling the signal: signal(SIGALRM, timeout_hdler); in my handler. But why is that? Am I doing this wrong?

void timeout_hdler(int signum) {
    printf("TIMEOUT has occured with signum: %d", signum);
    signal(SIGALRM, timeout_hdler);
}
Cyzanfar
  • 6,997
  • 9
  • 43
  • 81
  • 1
    See [How to avoid using `printf()` in signal handlers](https://stackoverflow.com/questions/16891019/) for information about why your signal handler is not reliable. See also [What is the difference between `sigaction()` and `signal()`?](https://stackoverflow.com/questions/231912/) – Jonathan Leffler Oct 02 '18 at 05:36
  • See [How to create a Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve) and post a complete example, please. – Luis Colorado Oct 05 '18 at 17:14
  • @LuisColorado what do you mean this is a complete, minimal and verifiable example – Cyzanfar Oct 05 '18 at 17:16
  • @JonathanLeffler, signals are not reliable in linux kernel, they are in BSD, but not in linux, even using `sigaction(2)`. You only get the first one, you won't get more than one if you have been sent several signals with that signal blocked. – Luis Colorado Oct 05 '18 at 17:17
  • @LuisColorado no it worked very well in my case. Thanks to Jonathan – Cyzanfar Oct 05 '18 at 17:18
  • @Czanfar, well, you begin with an statement (a system call), no block delimiter around it, no `#include` files.... I'm afraid (have not tested, but...) your first snippet is not going to compile as such. I have to repair severely your code to be able to test it locally. – Luis Colorado Oct 05 '18 at 17:19
  • I understand... and I have no problem... but I'm not going to dedicate a line of code to a person that answers unqualifying, of course... Good luck! – Luis Colorado Oct 05 '18 at 17:22

1 Answers1

1

The semantics of signal depends on the OS and libc. But according to your output in your specific case the signal handler handlers gets reset to the default after the first invocation of the handler function. The default then results in the program exit if the signal is triggered again, i.e. the Alarm clock and exit you see in the output.

From the documentation of signal in Linux:

The only portable use of signal() is to set a signal's disposition to SIG_DFL or SIG_IGN. ....
In the original UNIX systems, when a handler that was established using signal() was invoked by the delivery of a signal, the disposition of the signal would be reset to SIG_DFL, and the system did not block delivery of further instances of the signal. ...

In other words: don't use signal. Instead you should use sigaction:

POSIX.1 solved the portability mess by specifying sigaction(2), which provides explicit control of the semantics when a signal handler is invoked; use that interface instead of signal().

Steffen Ullrich
  • 114,247
  • 10
  • 131
  • 172
  • Good call with that `sigaction`. It is more complex than just using `signal` wondering if I can get away with just instantiating signal inside the handler... – Cyzanfar Oct 02 '18 at 04:44
  • 1
    @Cyzanfar: There is a race in that the handler is reset to default before calling the signal handler which means another ALARM before the handler is reestablished will cause program exit. This most likely affect your specific case though. Note that you also could do w/o signal handlers at all by using SO_SNDTIMEO and SO_RCVTIMEO. – Steffen Ullrich Oct 02 '18 at 04:54
  • Makes sense I'll try that. Appreciate the help @Steffen. – Cyzanfar Oct 02 '18 at 05:00