Why there is still a race condition even though I call sigsuspend()? - c

This is the coursework of my class. I'm not sure whether my answer is correct or not. Can someone give me any instruction. In the first question, I can't figure out why there is a race condition, is my explanation wrong?
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
void sigHandler(int signo) {
/* Line C */
}
int main() {
pid_t pid;
sigset_t newmask, waitmask, oldmask;
if ((pid = fork()) == 0) { // Child
struct sigaction action;
sigemptyset(&newmask);
sigemptyset(&waitmask);
sigaddset(&newmask, SIGUSR1);
sigaddset(&waitmask, SIGUSR2);
sigprocmask(SIG_BLOCK, &newmask, &oldmask);
action.sa_flags = 0;
sigemptyset(&action.sa_mask);
action.sa_handler = sigHandler;
sigaction(SIGUSR2, &action, NULL);
sigaction(SIGINT, &action, NULL);
sigaddset(&action.sa_mask, SIGINT);
sigaction(SIGUSR1, &action, NULL);
/* Line A: critical section */
sigsuspend(&waitmask); /* Line B */
} else { // Parent
int stat;
kill(pid, SIGUSR1);
kill(pid, SIGUSR2);
kill(pid, SIGINT);
pid = wait(&stat);
}
return 0;
}
Although sigsuspend() is used here, there is still a race condition. Why?
Since we don't know when the parent process executes first or the child process. If the parent executes first, the parent sends SIGUSR1, SIGUSR2 and SIGINT to the child, since SIGUSR1 is now blocked, the SIGUSR2 and SIGINT will be correctly catched, but we then sigaddset(&action.sa_mask, SIGINT), which means the following line sigaction(SIGUSR1, &action, NULL) will blocked both SIGUSR1 and SIGINT. Finally, the Line B calls sigsuspend(&waitmask), which will wait signals excepts SIGUSR2. But here is my question: since sigsuspend(&waitmask) didn't block SIGUSR1, why there is still a race condition? Were the SIGUSR2 and SIGINT catched already?
Suppose the child process receives (a)SIGUSR2 at line A, (b)SIGINT when blocked at line B, and (c)SIGUSR1 when SIGINT is caught at line C, how the child process' signal mask changes? Explane your answer.
(a) The mask contains SIGUSR1 (by sigprocmask()) and SIGINT(by sigaddset()).
(b) The mask contains only SIGUSR2 (by sigsuspend()).
After sigsuspend(), the mask restore to SIGUSR1 and SIGINT.
(c) The mask contains SIGINT and SIGUSR1, so it'll block SIGUSR1.
Suppose the child process receives (a)SIGUSR1 at line A, (b)SIGUSR2 when blocked at line B, and (c)SIGINT when SIGUSR1 is caught at line C, how the child process' signal mask changes? Explane your answer.
(a) The mask contains both SIGUSR1 (by sigprocmask()) and SIGINT (by sigaddset()) and the SIGUSR1 is blocked.
(b) The mask contains only SIGUSR2(by sigsuspend()).
After sigsuspend(), the mask restore to SIGUSR1 and SIGINT.
(c) The mask contains SIGINT and SIGUSR1, so it'll block SIGINT.

Related

Should child processes also be unblocking blocked SIGCHLD signals?

I'm trying to understand how blocking and unblocking signals work and I'm trying to understand the following piece of code. Specifically I am looking at line 28 (commented in the code): int a = sigprocmask(SIG_UNBLOCK, &mask, NULL);, aka where the signal is unblocked in the child.
The textbook I got the code from says that the code uses signal blocking in order to ensure that the program performs its add function (simplified to printf("adding %d\n", pid);) before its delete function (simplified to printf("deleting %d\n", pid);). This makes sense to me; by blocking the SIGCHLD signal, then unblocking it after we perform the add function, we ensure that handler isn't called until we perform the add function. However, why would we unblock the signal in the child? Doesn't that just eliminate the whole point of blocking by immediately unblocking it, allowing the child to delete before the parent adds?
However, the output (described after the code) is identical whether or not I have the line commented out or not, meaning that is clearly not what happens. The textbook states:
"Notice that children inherit the blocked set of their parents, so we must be careful to unblock the SIGCHLD signal in the child before calling execve."
But that still seems to me like the unblocking would result in the handler being called. What exactly does this line do?
void handler(int sig) {
pid_t pid;
printf("here\n");
while ((pid = waitpid(-1, NULL, 0)) > 0); /* Reap a zombie child */
printf("deleting %d\n", pid); /* Delete the child from the job list */
}
int main(int argc, char **argv) {
int pid;
sigset_t mask;
signal(SIGCHLD, handler);
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &mask, NULL); /* Block SIGCHLD */
pid = fork();
if (pid == 0) {
printf("in child\n");
int a = sigprocmask(SIG_UNBLOCK, &mask, NULL); // LINE 28
printf("a is %d\n",a);
execve("/bin/date", argv, NULL);
exit(0);
}
printf("adding %d\n", pid);/* Add the child to the job list */
sleep(5);
printf("awake\n");
int b = sigprocmask(SIG_UNBLOCK, &mask, NULL);
printf("b is %d\n", b);
sleep(3);
exit(0);
}
Outputs:
adding 652
in child
a is 0
Wed Apr 24 20:18:04 UTC 2019
awake
here
deleting -1
b is 0
However, why would we unblock the signal in the child? Doesn't that
just eliminate the whole point of blocking by immediately unblocking
it, allowing the child to delete before the parent adds?
No. Each process has its own signal mask. A new process inherits its parent's signal mask, but only in the same sense that it inherits the contents of the parent's memory -- the child gets what amounts to an independent copy. Its modifications to that copy are not reflected in the parent's copy, nor vise versa after the child starts. If this were not the case, then all processes in the system would share a single signal mask.
It is only the parent that must not receive SIGCLD too soon, so only the parent needs to have that signal blocked.
[...] The textbook states:
"Notice that children inherit the blocked set of their parents, so we must be careful to unblock the SIGCHLD signal in the child before
calling execve."
But that still seems to me like the unblocking would result in the
handler being called.
Again, "inherit" in the sense of inheriting a copy, not in the sense of sharing the same mask.
What exactly does this line do?
It unblocks SIGCLD in the child -- again, having no effect on the parent -- in case it being blocked would interfere with the behavior of /bin/date, which the child is about to exec.

About unix signals: sigsuspend() and race condition

This is the coursework of my class. I'm not sure whether my answer is correct or not. Can someone give me any instruction. In the first question, I can't figure out why there is a race condition, is my explanation wrong?
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
void sigHandler(int signo) {
/* Line C */
}
int main() {
pid_t pid;
sigset_t newmask, waitmask, oldmask;
if ((pid = fork()) == 0) { // Child
struct sigaction action;
sigemptyset(&newmask);
sigemptyset(&waitmask);
sigaddset(&newmask, SIGUSR1);
sigaddset(&waitmask, SIGUSR2);
sigprocmask(SIG_BLOCK, &newmask, &oldmask);
action.sa_flags = 0;
sigemptyset(&action.sa_mask);
action.sa_handler = sigHandler;
sigaction(SIGUSR2, &action, NULL);
sigaction(SIGINT, &action, NULL);
sigaddset(&action.sa_mask, SIGINT);
sigaction(SIGUSR1, &action, NULL);
/* Line A: critical section */
sigsuspend(&waitmask); /* Line B */
} else { // Parent
int stat;
kill(pid, SIGUSR1);
kill(pid, SIGUSR2);
kill(pid, SIGINT);
pid = wait(&stat);
}
return 0;
}
Although sigsuspend() is used here, there is still a race condition. Why?
Since we don't know when the parent process executes first or the child process. If the parent executes first, the parent sends SIGUSR1, SIGUSR2 and SIGINT to the child, since SIGUSR1 is now blocked, the SIGUSR2 and SIGINT will be correctly catched, but we then sigaddset(&action.sa_mask, SIGINT), which means the following line sigaction(SIGUSR1, &action, NULL) will blocked both SIGUSR1 and SIGINT. Finally, the Line B calls sigsuspend(&waitmask), which will wait signals excepts SIGUSR2. But here is my question: since sigsuspend(&waitmask) didn't block SIGUSR1, why there is still a race condition? Were the SIGUSR2 and SIGINT catched already?
Suppose the child process receives (a)SIGUSR2 at line A, (b)SIGINT when blocked at line B, and (c)SIGUSR1 when SIGINT is caught at line C, how the child process' signal mask changes? Explain your answer.
(a) The mask contains SIGUSR1 (by sigprocmask()) and SIGINT(by sigaddset()).
(b) The mask contains only SIGUSR2 (by sigsuspend()).
After sigsuspend(), the mask restore to SIGUSR1 and SIGINT.
(c) The mask contains SIGINT and SIGUSR1, so it'll block SIGUSR1.
Suppose the child process receives (a)SIGUSR1 at line A, (b)SIGUSR2 when blocked at line B, and (c)SIGINT when SIGUSR1 is caught at line C, how the child process' signal mask changes? Explain your answer.
(a) The mask contains both SIGUSR1 (by sigprocmask()) and SIGINT (by sigaddset()) and the SIGUSR1 is blocked.
(b) The mask contains only SIGUSR2(by sigsuspend()).
After sigsuspend(), the mask restore to SIGUSR1 and SIGINT.
(c) The mask contains SIGINT and SIGUSR1, so it'll block SIGINT.

Using two pause() to catch two signals but failed

I'm writing a program that forks two children to do something. These two children send two different signals to its parent when their job is done. In the meanwhile, the parent waits for its children using two pause().
However, the program stopped after the first pause() and waits for another signal at the second pause(). Using gdb, I find that two signals from children are received, but only one pause() is finished.
What is the cause of this problem?
In main:
struct sigaction parent_act;
struct sigaction child_act[2];
// set the signal handlers
parent_act.sa_handler = &p0_handler;
child_act[0].sa_handler = &p1_handler;
child_act[1].sa_handler = &p2_handler;
// set the behavior when child get signal SIGUSR1 and SIGUSR2
sigaction(SIGUSR1, &child_act[0], NULL);
sigaction(SIGUSR2, &child_act[1], NULL);
// fork two child
for(i = 0; i < 2; i++){
pid[i] = fork();
// child process
else if(pid[i] == 0){
pause(); // wait for signal
return 0;
}
}
// set the behavior when parent get signal SIGUSR1 and SIGUSR2
sigaction(SIGUSR1, &parent_act, NULL);
sigaction(SIGUSR2, &parent_act, NULL);
kill(pid[0], SIGUSR1); // signal the child to do its job
kill(pid[1], SIGUSR2); // signal the other child to do its job
pause(); // wait for child
pause(); // wait for child
In handlers:
void p0_handler(int dummy)
{
return;
}
void p1_handler(int dummy)
{
// do something
kill(getppid(), SIGUSR1); // tell parent it's done
return;
}
void p2_handler(int dummy)
{
// do something
kill(getppid(), SIGUSR2); // tell parent it's done
return;
}
First child sends SIGUSR1 to parent and the second one sends SIGUSR2. It seems the first pause() received two signals. Is that possible?
Most likely both signals arrive at the same time (or the second one arrives during execution of the signal handler) at which point the second pause will wait indefinitely.
The problem is that there is no guarantee that even if you like set a flag "One child finished" the other signal does not arrive before pause is called.
The only way to make this waterproof with signals is to use select on a timeout.
int num_signals_received = 0;
void p0_handler(int dummy)
{
// signal safe action
__sync_fetch_and_add(&num_signals_received, 1);
return;
}
instead of pause:
struct timeval timeout = {1,0}; // 1 second
while (num_signals_received < 2)
select(0, NULL, NULL, NULL, &timeout);
If you are not set on signals for synchronisation then I would advise you move to either shared memory (mmap/MAP_SHARED) or file descriptor based (pipe(2)) synchrosisation.

posix threads block signal and unblock

Is there a way to block certain signals and unblock other signals in the same set?
I just dont seem to get my head around it!
An example
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGUSR1);
// Block signal SIGUSR1 in this thread
pthread_sigmask(SIG_BLOCK, &set, NULL);
sigaddset(&set, SIGALRM);
// Listen to signal SIGUSR2
pthread_sigmask(SIG_UNBLOCK, &set, NULL);
pthread_t printer_thread1, printer_thread2;
pthread_create(&printer_thread1, NULL, print, (void *)&f1);
pthread_create(&printer_thread2, NULL, print, (void *)&f2);
bool tl = true;
while(1)
{
if(tl)
{
// thread1 does something
kill(pid, SIGUSR1);
// main thread waits for SIGALRM
sigwait(&set, &sig);
tl = !tl;
}
else
{
// thread2 does something
kill(pid, SIGUSR2);
// main thread waits for SIGALRM
sigwait(&set, &sig);
tl = !tl;
}
}
I am not allowed to use Mutexs, semaphores etc only signals.
Can someone help? :)
Is there a way to block certain signals and unblock other signals in
the same set?
With pthread_sigmask, you can choose to either:
add a set of signals to the set of blocked signals, using the constant SIG_BLOCK
remove a set of signals to the set of blocked signals, using the constant SIG_UNBLOCK
define the set of signals to be blocked, using the constant SIG_SET
In other words, there's a current set of blocked signals for the thread, and you can modify it as specified above, one operation at a time.
The important point is that newly created threads inherit the signal mask of the creating thread, so you can set the mask of the new thread right before creating it, or in the function that the new thread will run, at your convenience.
Regarding your example, I suppose that you are trying to have printer_thread1 block SIGUSR2 and SIGALRM, and have printer_thread2 block SIGUSR1 and SIGALRM, and have the main thread block SIGUSR1 and SIGUSR2, so that each thread can send a signal that will be caught by a single thread (without the code of print, f1 and f2, it's impossible to know for sure what is your intent in your example).
You should be able to achieve that by the following code:
sigset_t set;
pthread_t printer_thread1, printer_thread2;
// Block signal SIGUSR1 & SIGALRM in printer_thread1
sigemptyset(&set);
sigaddset(&set, SIGUSR1);
sigaddset(&set, SIGALRM);
pthread_sigmask(SIG_SET, &set, NULL);
pthread_create(&printer_thread1, NULL, print, (void *)&f1);
// Block signal SIGUSR2 & SIGALRM in printer_thread2
sigaddset(&set, SIGUSR2);
sigaddset(&set, SIGALRM);
pthread_sigmask(SIG_SET, &set, NULL);
pthread_create(&printer_thread2, NULL, print, (void *)&f2);
// Block signal SIGUSR1 & SIGUSR2 in the main thread
sigemptyset(&set);
sigaddset(&set, SIGUSR1);
sigaddset(&set, SIGUSR2);
// Listen to signal SIGALRM
pthread_sigmask(SIG_SET, &set, NULL);
bool tl = true;
while(1)
{
if(tl)
{
// thread1 does something
kill(pid, SIGUSR1);
// main thread waits for SIGALRM
sigwait(&set, &sig);
tl = !tl;
}
else
{
// thread2 does something
kill(pid, SIGUSR2);
// main thread waits for SIGALRM
sigwait(&set, &sig);
tl = !tl;
}
}
see these man pages :
pthread_sigmask
sigprocmask
for further details.
I think what you want to do here is
// Block signal SIGUSR1 in this thread
sigemptyset(&set);
sigaddset(&set, SIGUSR1);
pthread_sigmask(SIG_BLOCK, &set, NULL);
// Listen to signal SIGALRM
sigemptyset(&set);
sigaddset(&set, SIGALRM);
pthread_sigmask(SIG_UNBLOCK, &set, NULL);
The set is only used to tell it what to block or unblock. Once passed to the command, you are free to reset it and build up another signal mask. If you skip the sigemptyset, the set will still contain SIGUSR1, which will subsequently be unblocked again. Well, I think that is how it works, at least - it has been a long time since I used signals.

Non-blocking check for signals in a loop

I have a thread in an application that has a loop like this:
...
while (1)
{
checkDatabase();
checkChildren();
sleep(3);
}
...
checkDatabase() is self-explanatory; checkChildren() simply calls waitpid(-1, &status, WNOHANG) to deal with child processes that have either exited or received a signal.
The application works fairly well, but it has default signal handling. The problem is that this parent process has a number of threads (don't worry about child processes for now) and I don't have any experience with synchronous signals, let alone in a POSIX threads application. I have used signal() before but apparently it's non-portable and it doesn't do what I need anyway. I have no experience at all with sigaction methods, and I can't find good documentation on how to fill in the structs and so on.
What I need to do is to synchronously catch terminating signals like SIGINT, SIGTERM and SIGQUIT in the above loop (and I need to ignore SIGPIPE altogether so that I can catch the EPIPE error from IO methods), so it would look like this:
...
while (1)
{
checkDatabase();
checkChildren();
checkForSignals();
sleep(3);
}
...
All other threads should not have anything to do with the signal; only the thread that executes this loop should be aware of it. And, obviously, it needs to be a non-blocking check so the loop doesn't block during its first iteration. The method called if a signal is found will sort out the other threads and destroy mutexes, and all that.
Could anyone please give me a heads-up? Many thanks.
(Following the question's comments, and for completeness, this solution tries to avoid signal handlers.)
It is possible to block signals from being raised through sigprocmask() (or, rather, pthread_sigmask() since you're using threads). From there on, the signals that were raised but blocked are available through sigpending().
Therefore, you could do something like (error checking omitted for brevity):
sigset_t blocked;
sigemptyset(&blocked);
sigaddset(&blocked, SIGINT);
sigaddset(&blocked, SIGTERM);
sigaddset(&blocked, SIGQUIT);
pthread_sigmask(SIG_BLOCK, &blocked, NULL); // Block SIGINT, SIGTERM and SIGQUIT.
signal(SIGPIPE, SIG_IGN); // Ignore SIGPIPE.
Then, later:
void checkForSignals(void)
{
sigset_t pending;
sigpending(&pending);
if (sigismember(&pending, SIGINT)) {
// Handle SIGINT...
}
if (sigismember(&pending, SIGTERM)) {
// Handle SIGTERM...
}
if (sigismember(&pending, SIGQUIT)) {
// Handle SIGQUIT...
}
}
Since sigpending() does not block, this seems to match your requirements.
Create a signal handler for SIGINT, SIGTERM and SIGQUIT, using the same function. In that signal function just set a flag that can be polled in your loop.
Something like this:
/* Global variable, will be set to non-zero if SIGINT, SIGTERM or SIGQUIT is caught */
int term_signal_set = 0;
void my_signal_handler(int)
{
term_signal_set = 1;
}
/* ... */
signal(SIGINT, my_signal_handler);
signal(SIGTERM, my_signal_handler);
signal(SIGQUIT, my_signal_handler);
signal(SIGPIPE, SIG_IGN); /* So functions return EPIPE */
while (1)
{
/* ... */
if (term_signal_set > 0)
break; /* Or do something else */
sleep(3);
}
In a multithreaded application receiving a signal, there is no predetermination, which thread receives the signal. Typical workaraounds include setting a global variable in the signal handler and checking it from a dedicated thread.
So in your case the signal handler (called from whatever thread) would just set something like a global variable for the signal received, and in CheckForSignals() you would test it.
sigaction is the way to go. man sigaction should help you. Here is an example from the web
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
struct sigaction act;
void sighandler(int signum, siginfo_t *info, void *ptr)
{
printf("Received signal %d\n", signum);
printf("Signal originates from process %lu\n",
(unsigned long)info->si_pid);
}
int main()
{
printf("I am %lu\n", (unsigned long)getpid());
memset(&act, 0, sizeof(act));
act.sa_sigaction = sighandler;
act.sa_flags = SA_SIGINFO;
sigaction(SIGTERM, &act, NULL);
// Waiting for CTRL+C...
sleep(100);
return 0;
}

Resources