0

I have just written the below routine to handle the EINTR error. The routine is given below,

while((s = sem_wait(&w4compl)) == -1)
{
        if (errno == EINTR)
        {
                perror("call interrupted by sig. handler\n");
                continue;
        }
        else
                printf("Other Error Generated\n");
}

SO, here i am not able to see the print "call interrupted by sig. handler\n" statement. How can test this so that it will print the same(How can i execute the part of if (errno == EINTR)).

  • HI P.P. can you give me the link if this question is already answered. – Nirav Patel Aug 07 '18 at 11:43
  • It's linked in your question. – P.P Aug 07 '18 at 11:44
  • @P.P. If you look closely, that's actually not a good duplicate. Sure, the title is nice and generic, but the top answers all say "Don't re-call `fclose()`", which doesn't really help here. – Jonathon Reinhart Aug 07 '18 at 11:49
  • Agree with Jonathan .It's doesn't help me much . – Nirav Patel Aug 07 '18 at 11:52
  • @JonathonReinhart The linked question is for unit testing EINTR which is what OP asked here too. There are decent answers there. Sure, fclose was used as an example (and it's sem_post here). I am not sure we need a different question for every sytem call that could return EINTR and how to unit test it. – P.P Aug 07 '18 at 11:54
  • @P.P. I agree that we don't need a different question for every system call, but `fclose()` is a library function with additional semantics (disassociating with the underlying file descriptor), so that question was really just a bad example IMO. I didn't see any answers that suggested getting the syscall in a blocked state (which is easy for semaphores) and sending a signal to the thread. – Jonathon Reinhart Aug 07 '18 at 11:58

2 Answers2

1

Install a signal handler, and cause a signal to be delivered (using alarm(), setitimer(), or timer_create()+timer_settime()), so that the delivery of the signal will interrupt the sem_wait() call.


Consider this example program:

#define  _POSIX_C_SOURCE  200809L
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <semaphore.h>
#include <stdio.h>
#include <errno.h>

static void dummy_handler(int signum)
{
}

static int install_dummy_handler(int signum)
{
    struct sigaction  act;
    memset(&act, 0, sizeof act);
    sigemptyset(&act.sa_mask);
    act.sa_handler = dummy_handler;
    act.sa_flags = 0;
    return sigaction(signum, &act, NULL);
}

static const char *errname(const int errnum)
{
    switch (errnum) {
    case EINTR:  return "EINTR";
    case EINVAL: return "EINVAL";
    default:     return "(other)";
    }
}

int main(void)
{
    sem_t  s;

    if (install_dummy_handler(SIGALRM) == -1) {
        fprintf(stderr, "Cannot install ARLM signal handler: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    }

    sem_init(&s, 0, 0);

    alarm(1);

    if (sem_wait(&s) == -1) {
        const int  errnum = errno;
        printf("sem_wait() failed with errno == %s (%d; %s).\n",
               errname(errnum), errnum, strerror(errnum));
    } else
        printf("sem_wait() succeeded.\n");

    return EXIT_SUCCESS;
}

In main(), we install a signal handler for the SIGALRM signal. It does not matter if the signal handler function does anything at all, because it is the delivery of the signal that causes "slow" syscalls to return with EINTR error. (As long as the SA_RESTART flag was not used when that handler was installed. If you look at act.sa_mask in install_dummy_handler(), you'll see we used no flags at all. All the flags and sigaction() usage are described in the man 2 sigaction man page.)

In main(), we first initialize our semaphore, then set an alarm for one second. When the real, wall-clock time has elapsed, the SIGALRM signal is raised. Do note that although SIGALRM is just fine for this example and similar purposes, you'll probably want to use POSIX per-process interval timers instead.

Next, we simply call sem_wait() on the semaphore, and examine the result. In practice, if you compile and run the above example.c using e.g.

gcc -Wall -O2 example.c -lpthread -o example
./example

the program will output

sem_wait() failed with errno == EINTR (4; Interrupted system call).

after one second.

Nominal Animal
  • 38,216
  • 5
  • 59
  • 86
  • @NiravPatel: Do note that if you have multiple threads, the signal must be directed at the thread doing the `sem_wait()` call, and that it is the delivery of the signal to the userspace handler function, not the signal itself, that causes the interruption of the blocking call. In some cases you may need a signal pair, with the primary signal blocked in all but one thread, so that its signal handler can "forward" the signal (using the secondary signal that is not blocked) to each existing other thread, to interrupt several threads. Let me know if you need further details or an example. – Nominal Animal Aug 08 '18 at 14:11
0

Just about any system call on Linux can return EINTR if the system call is interrupted.

From the man page (emphasis mine):

sem_wait() decrements (locks) the semaphore pointed to by sem. If the semaphore's value is greater than zero, then the decrement proceeds, and the function returns, immediately. If the semaphore currently has the value zero, then the call blocks until either it becomes possible to perform the decrement (i.e., the semaphore value rises above zero), or a signal handler interrupts the call.

To trigger this case, you should make sure that the sem_wait system call is blocked (waiting), and then send a signal (which has a handler) to the thread.

Some psuedo-code:

sigint_handler:
    return

thread2:
    <Your while loop from the question>

main:
    signal(SIGINT, sigint_handler)  // Setup signal handler

    sem_wait(&w4compl)
    t2 = start_thread(thread2)
    sleep(5)                        // Hack to make sure thread2 is blocked

    pthread_kill(t2, SIGINT)
Jonathon Reinhart
  • 132,704
  • 33
  • 254
  • 328
  • Thanks @Jonathan Reinhart . Here the routine called in one of the thread. So how can i give SIGINT signal to thread? – Nirav Patel Aug 07 '18 at 11:45
  • Use [`pthread_kill`](http://man7.org/linux/man-pages/man3/pthread_kill.3.html) to send a signal to a thread. – Jonathon Reinhart Aug 07 '18 at 11:47
  • Just to understand, *which* system call that OP should block here? And why SIGINT? – P.P Aug 07 '18 at 11:50
  • @Jonathan I will try the pseudo code in my existing code and check. – Nirav Patel Aug 07 '18 at 12:08
  • 1. Using `signal` is not a good idea (sigaction should be preferred). 2. Besides `sleep` being a bad choice as a sync mechanism, `sleep` itself can be interrupted by a signal (I still don't understand how `sleep` in main ensures it's blocked in the thread). – P.P Aug 07 '18 at 12:21
  • 3. OP's question, as I understand it, is about *unit testing* the scenario where a system call returns EINTR. So I'd expect a transparent mechanism (such as the one answers that mentioned using function pointers in the linked-dup). 4. I wouldn't use signal handlers, signal blocking/sleeping, etc just for "unit testing" i.e., "unit testing" is supposed to *test*, not influence/change/introduce additional behaviours, etc. – P.P Aug 07 '18 at 12:21