Sandeep
Sandeep

Reputation: 19452

How to cancel a pending signal?

How can I cancel a sent signal to a process which is still not delivered?

Consider the scenario where I have sent a signal to a process, but the process was in un-interruptible state.

I am doing a conditional wait for the signal to be handled. But since it did not I want to continue executing further. In that case, is there a way that I can cancel the sent signal(it is not yet delivered)

Upvotes: 4

Views: 3827

Answers (1)

Filipe Gonçalves
Filipe Gonçalves

Reputation: 21213

A pending signal is canceled if that signal is ignored before the signal is delivered. You just have to ignore the signal. You can do this with sigaction() by setting the sa_handler field in struct sigaction to SIG_IGN.

Here's some example code that illustrates this and shows that it works. The code does the following:

  1. Blocks SIGINT so that we have a window of time to send it SIGINT when it is blocked - this will generate a pending SIGINT that will be delivered when the process signal mask is changed to a mask that does not include SIGINT (and will be canceled if the signal is ignored).
  2. Waits for you to send SIGINT
  3. Ignores SIGINT after knowing that a SIGINT is pending. This has the effect of canceling the pending signal.
  4. Restores the original default action for SIGINT, which is to terminate the process
  5. Restores the original process signal mask, that does not block SIGINT.
  6. Waits for you to enter any character to terminate.

You can see that the process does not terminate after step 5 and waits for your input, which means that the pending signal was canceled.

Here's the code that illustrates this:

#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(void) {

    sigset_t block_sigint, prev_mask;
    sigemptyset(&block_sigint);
    sigaddset(&block_sigint, SIGINT);

    if (sigprocmask(SIG_SETMASK, &block_sigint, &prev_mask) < 0) {
        perror("Couldn't block SIGINT");
        return 0;
    }

    printf("SIGINT blocked: kill -SIGINT %ld to generate a pending SIGINT. Press return when done.\n", (long) getpid());

    /* Now, open a new terminal and send SIGINT to this process.
     *
     * After doing that, the signal will be pending delivery because it is currently blocked.
     *
     * Now, if we ignore SIGINT, the pending signal will be cancelled
     */

    getchar();

    struct sigaction ign_sigint, prev;
    ign_sigint.sa_handler = SIG_IGN;
    ign_sigint.sa_flags = 0;
    sigemptyset(&ign_sigint.sa_mask);

    if (sigaction(SIGINT, &ign_sigint, &prev) < 0) {
        perror("Couldn't ignore SIGINT");
        return 0;
    }

    printf("SIGINT ignored - pending SIGINT was canceled.\n");

    /* Now, restore the default action for SIGINT */
    if (sigaction(SIGINT, &prev, NULL) < 0) {
        perror("Couldn't restore default SIGINT behavior");
        return 0;
    }

    /* And also restore the process's original sigmask, which does not block SIGINT */
    if (sigprocmask(SIG_SETMASK, &prev_mask, NULL) < 0) {
        perror("Couldn't restore original process sigmask");
        return 0;
    }

    printf("Process sigmask and action for SIGINT are now restored\n");

    /* We will not receive SIGINT at this point because it was canceled
     * So the process will block on getchar() here instead of terminating
     */
    getchar();

    return 0;   
}

Upvotes: 15

Related Questions