Reputation: 2091
I'm trying to create simple signal handling - let's say something like terminating the process. I have three separate threads in my program + main thread.
The problem is that invoking the signal causes the current thread to terminate, while the others are still running.
How can I send the signal to the remaining threads? How can I differentiate these signals while sending them?
I have to use FIFO here by the way.
Here's what I've got so far:
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/stat.h>
#include <pthread.h>
#include <string.h>
#include <signal.h>
int first[2];
int second[2];
#define FIFO_FILE "tmp/myfifo"
void *input(void *ptr)
{
char str[100], fifo[100];
int length;
FILE *fp;
while(1)
{
fp = fopen(FIFO_FILE, "r");
fgets(fifo, 100, fp);
if(fifo == "s1")
{
printf("SIGNAL 1!!!");
exit(1);
}
printf("Enter the message: ");
fflush(stdout);
length = read(STDIN_FILENO, str, sizeof(str));
if(str[0] == ';')
exit(2);
if(length <= 0)
{
if(length == -1)
perror("read");
close(first[1]);
exit(2);
}
if(write(first[1], str, length) != length)
{
perror("write");
exit(2);
}
}
}
void *countChars(void *ptr)
{
char str[100], fifo[100];
int length, count = 0;
FILE *fp;
while(1)
{
fp = fopen(FIFO_FILE, "r");
fgets(fifo, 100, fp);
if(fifo == "s1")
{
printf("SIGNAL 1!!!");
exit(1);
}
length = read(first[0], str, sizeof(str));
if(length <= 0)
{
if(length == -1)
perror("read");
close(first[0]);
close(second[1]);
exit(2);
}
if(write(STDOUT_FILENO, str, length) != length)
{
perror("write");
exit(2);
}
while(str[count] != '\n') count++;
write(second[1], &count, sizeof(count));
count = 0;
}
}
void *output(void *ptr)
{
int length, count = 0;
char fifo[100];
FILE *fp;
while(1)
{
fp = fopen(FIFO_FILE, "r");
fgets(fifo, 100, fp);
if(fifo == "s1")
{
printf("SIGNAL 1!!!");
exit(1);
}
length = read(second[0], &count, sizeof(count));
if(length < sizeof(count))
{
close(second[0]);
exit(2);
}
printf("Number of characters: %d\n", count);
}
}
void s1_handler(int signo)
{
FILE *fp;
if((fp = fopen(FIFO_FILE, "wb")) == NULL)
{
perror("fopen");
exit(2);
}
fputs("s1", fp);
fclose(fp);
}
int main()
{
pthread_t t1, t2, t3;
if(pipe(first) == -1)
{
printf("First pipe error");
exit(1);
}
if(pipe(second) == -1)
{
printf("Second pipe error");
exit(1);
}
pthread_create(&t1, NULL, input, NULL);
pthread_create(&t2, NULL, countChars, NULL);
pthread_create(&t3, NULL, output, NULL);
if(signal(SIGINT, s1_handler) == SIG_ERR)
{
printf("Cant catch SIGINT\n");
}
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_join(t3, NULL);
return 0;
}
Upvotes: 0
Views: 4228
Reputation: 27542
You have quite a few (conceptual) errors in your program.
Signals: Standard signals (i.e. non-realtime signals like SIGINT) are not queued. Your process will only receive one and any further signals (of the same type) will be thrown away until the one already delivered is somehow processed. Signals (most, anyway) are delivered to the process as a whole. In the absence of your program taking actions to do otherwise, the signal will be delivered to an arbitrary thread within the process. You can resend a signal you catch to your other threads with pthread_kill
but this will require you to make each thread id available to every other thread by using, for instance, a global table of TIDs. It is unclear what you are really trying to accomplish with your program but that is almost certainly not what you want to do.
FIFOs: You seem to know that using FIFOs to communicate between threads is a dubious design but if you were told to use them then you have to use them correctly.
(1) When FIFOs are opened (w/o specifying non-blocking mode) the open
is going to block until there is both a reader and a writer on each end of the FIFO. This means all 3 of your threads will block on their respective FIFO open calls until your signal handler - see problems with that below - runs and opens the FIFO for writing.
(2) Even when you get past the opens, only one thread is going to read and consume the string that the signal handler wrote. The other threads are going to sit blocking trying to read an empty FIFO and will never process anything. Currently you are just calling exit
in the thread that reads the FIFO, which will end the program, but is that what you are really going for?
(3) There is no need to open the FIFO in each thread. You can do this that before creating the threads and either pass the FIFO file descriptor to each thread or just make it global.
(4) You are opening (and not closing) the FIFO in each thread every time through your while(1)
loops. You will run out of file descriptors very quickly that way.
Signal Handler: You should not use non-async safe calls in a signal handler. You have at least 3 - fopen, fputs, fclose. Ideally you want to do very simple things, like just set a global switch, in a signal handler and get out. So if this is anything more than a brain dead class assignment you should rethink this entirely.
I would suggest elaborating exactly what your goal is for the program and you can get some advice on how to reach it.
Upvotes: 2
Reputation: 25593
Only one thread receives the signal! Which one? See the quoted details from:
http://www.linuxprogrammingblog.com/all-about-linux-signals?page=11
Which thread receives the signal?
This is the most interesting question. There are two cases:
Process-directed signals (sent to a PID using functions like kill(2)). Threads have their separate signal mask which can be manipulated using pthread_sigmask(2) similary to sigprocmask(2), so such signal is not delivered to a thread that has this signal blocked. It's delivered to one of threads in the process with this signal unblocked. It's unspecified which thread will get it. If all threads have the signal blocked, it's queued in the per-process queue. If there is no signal handler defined for the signal and the default action is to terminate the process with or without dumping the core the whole process is terminated. Thread-directed signals. There is a special function to send a signal to a specific thread: pthread_kill(2). It can be used to send a signal from one thread to another (or itself). This way the signal will be delivered or queued for the specific thread. There are also per-thread directed signals generated by the operating system like SIGSEGV. If there is no signal handler defined for a signal that default's action is to terminate the process, a thread-directed signal terminated the whole process.
Upvotes: 0