2
votes

The program is intended to signal transaction permanently. SIGUSR1 is caught by the parent and SIGUSR2 caught by the child. They play with only the flag when they catch their own signals. I let first the parent to run, that is, at first the parent sends signal. The child waits by pause() its process until it runs its catcher on the fly. I thought I apply a simple synchronization, but seemingly not. However, if I comment in the usleep(1000), the code works. Like

initial value, flag = -99
child process, flag = 0
parent process, flag = 1
child process, flag = 0
parent process, flag = 1
child process, flag = 0
.
.
.
child process, flag = 0
parent process, flag = 1
child process, flag = 0
parent process, flag = 1
child process, flag = 0
.
.
.

but without sleep, I can't get what I want. I want to get my intend without sleep. Wrong output is,

initial value, flag = -99
parent process, flag = -99
waits forever..................

How can it be run as intended? However, what's the reason of the behaviour? By the way, I have to apply the synchronization with only signals without semaphores, mutex etc. All posix signal features, except for sleep, nanosleep or pause and busy waiting, can be used like sigaction, sigsuspend etc.

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>

volatile sig_atomic_t flag = -99; // child = 0, parent = 1;



void catcher(int sig) {
    switch (sig) {
    case SIGUSR1 : flag = 1; break;
    case SIGUSR2 : flag = 0; break;
    }
}

int safeBlockParent(int signum) {

    sigset_t maskall, maskmost, maskold;



    sigfillset(&maskall);
    sigfillset(&maskmost);
    sigdelset(&maskmost, signum);
    sigprocmask(SIG_SETMASK, &maskall, &maskold);
    if (flag == 0)
        sigsuspend(&maskmost);
    sigprocmask(SIG_SETMASK, &maskold, NULL);
}

int safeBlockChild(int signum) {
    sigset_t maskall, maskmost, maskold;

    sigfillset(&maskall);
    sigfillset(&maskmost);
    sigdelset(&maskmost, signum);
    sigprocmask(SIG_SETMASK, &maskall, &maskold);
    if (flag == 1)
        sigsuspend(&maskmost);
    sigprocmask(SIG_SETMASK, &maskold, NULL);
}

void  ChildProcess() {


    while(1) {
        safeBlockChild(SIGUSR2);
        fprintf(stderr, "child process, flag = %d\n", flag);
        kill( getppid(), SIGUSR1 );
    }
}

void  ParentProcess(pid_t childPid) {

    flag = 1;

    while(1) {
        //usleep(1000);
        fprintf(stderr, "parent process, flag = %d\n", flag);
        kill( childPid, SIGUSR2 );
        safeBlockParent(SIGUSR1);

    }
}

int main() {

    pid_t  pid;
    struct sigaction sact = { 0 };

    fprintf(stderr, "initial value, flag = %d\n", flag);

    sigemptyset( &sact.sa_mask );
    sact.sa_flags = 0;
    sact.sa_handler = catcher;

    if (sigaction (SIGUSR1, &sact, NULL) < 0) {
        perror("sigaction sigusr1 error");
        exit(1);
    }

    if (sigaction (SIGUSR2, &sact, NULL) < 0) {
        perror("sigaction sigusr2 error");
        exit(2);
    }


    pid = fork();
    if (pid < 0) { perror("fork problem"); exit(3); }

    if (pid == 0) {

        //kill(getppid(), SIGUSR1);
        ChildProcess();
    }

    else {
        ParentProcess(pid);
        //wait(NULL);
    }

    return 0;
}

The code stucks sometimes, sometimes runs.

1
Even though it can introduce timing delays that can mask race condition errors , strace is useful for debugging signal problems. It will tell you what signal interrupted pause, what signal terminated a process, etc. strace -f command. - Mark Plotnick
Even if I tried it, I most probably see the problems mentioned by a3f below. The big deal is to find solutions of which I'm lack. Could you write its solution? The question is really education-purposed nothing else @MarkPlotnick - concurrencyboy
To be sure someone can help you develop a solution, can you edit your question to include the complete assignment, including any constraints (such as only being allowed to use signal and not sigaction, not being allowed to use semaphores, etc.)? - Mark Plotnick
I've edited the question through your offer. @MarkPlotnick - concurrencyboy
OK, some hints: (1) Use sigaction instead of signal. signal isn't guaranteed to automatically re-arm the signal handler and won't give you the option of blocking signals while running a handler. (2) as mentioned in the answer below, the parent can send signals before the child has had a chance to set up a handler. Rearrange your code so that doesn't happen. (3) Make sure SIGUSR1 and SIGUSR2 are blocked/held/masked whenever the process is not prepared to deal with receiving the signal. Instead of using pause, consider using sigsuspend along with sigprocmask. - Mark Plotnick

1 Answers

2
votes

You have two race conditions:

  1. The parent process could send a signal before the child has had a chance to register a signal handler for SIGUSR2.
  2. One process could send a signal while the other is outside pause.

The latter can happen the first time round, when the child process has yet to reach pause, but the parent has sent SIGUSR2 anyway. This causes the effect you're seeing.