Sending signals between three threads - c

I had to write a program that uses 3 threads - one to read letters, second to count characters, and the third to output them. This is the code:
#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];
void *input(void *ptr)
{
char str[100];
int length;
while(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];
int length, count = 0;
while(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;
while(1)
{
length = read(second[0], &count, sizeof(count));
if(length < sizeof(count))
{
close(second[0]);
exit(2);
}
printf("Number of characters: %d\n", count);
}
}
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);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_join(t3, NULL);
return 0;
}
It works, but right now I have to implement signals here. Sending SIGUSR1 signal should stop program execution until sending SIGUSR2 signal.
The problem is that when I send the signal, only one thread gets it. And thus I have to use FIFO to inform other threads which signal was executed and execute it in the rest of them.
How could I do this?

Signals are delivered to the process, not to the threads. Thus any thread that is able to handle a signal may be used to call a signal handler. What you need to do is figure out how to handle the signal and then decide how to communicate that to all the threads. You have not really described what you mean by "stop program execution", so I'll have to guess.
I would suggest using a combination of pthread_sigmask and sigwait. You can use pthread_sigmask to disable automatic handling of SIGUSR1 and SIGUSR2 in your worker threads. Then call sigwait in a fourth signal handler thread to explicitly handle those signals. When the signal handler thread receives a SIGUSR1 it sets a global flag. The worker threads check that flag periodically and go to sleep (on a condition variable maybe?) when it is set. The signal handler thread then loops around and calls sigwait again. When it receives a SIGUSR2, it wakes up the worker threads, then loops around and calls sigwait, once again.

Related

How to supply input to a thread which is polling for stdin, form another thread in the same process?

Referring to following code example, I want the main thread to supply the number num that the child thread is expecting using scanf.
I tried this way to write the wordcount (9) to stdin which is to be read by child thread, but it is not working.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
void* child_thread_func(void* terminalflag)
{
int num=0;
printf("Trying to read num from stdin\n");
scanf("%d",&num);
/*expecting 9 to be printed here*/
printf("Entered number is %d\n", num);
}
int main () {
pthread_t tid;
if (pthread_create(&tid, NULL, &child_thread_func, NULL) != 0) {
printf("Failed to initialize thread\n");
exit(1);
}
sleep(2);
char buffer[50];
FILE *wfp = popen("wc -c", "w");
if (wfp != NULL) {
sprintf(buffer, "dummyword");
int save_stdin = dup(fileno(stdin));
dup2(fileno(wfp), fileno(stdin));
fwrite(buffer, sizeof(char), strlen(buffer), wfp);
dup2(save_stdin, fileno(stdin));
pclose(wfp);
}
pthread_join(tid, NULL);
}
Can someone suggest a correct way or any other alternative way to do this?
Thanks.
I don't think there is any good way for a process to write text to its own stdin; stdin is meant to be a way for the parent process (or the user, if the parent process is a Terminal window) to send data to your process, not for your process to send data to itself.
However, you could achieve a similar result by having your child thread use select() or similar to read input from both stdin and from the output end of a pipe; then your parent process can send data to the child process by writing to the input end of that same pipe.
Below is a modified version of your program demonstrating the technique. Note that the child thread will print out any text that you type into stdin; and also the main thread will send a line of text to the child thread once every 5 seconds, and the child thread will also print out that text. After the main thread has sent 5 messages to the child thread, the main thread will close its end of the pipe, causing the child thread to exit and then the process can exit cleanly as well.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/select.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
static int pipeReadFD = -1;
static int ReadTextFrom(const char * descriptionOfSender, int fd)
{
char buf[256];
const int numBytesRead = read(fd, buf, sizeof(buf)-1); // -1 so we always have room to place NUL terminator byte
if (numBytesRead > 0)
{
buf[numBytesRead] = '\0'; // make sure the string is NUL-terminated
printf("ReadTextFrom(): Read %i bytes from [%s]: [%s]\n", numBytesRead, descriptionOfSender, buf);
}
return numBytesRead;
}
void* init_on_sys_ready(void* terminalflag)
{
int num=0;
printf("Child thread: trying to read text from stdin\n");
while(1)
{
const int stdinFD = fileno(stdin);
const int maxFD = (pipeReadFD > stdinFD) ? pipeReadFD : stdinFD;
fd_set readFDSet;
FD_ZERO(&readFDSet);
FD_SET(stdinFD, &readFDSet);
FD_SET(pipeReadFD, &readFDSet);
const int selRet = select(maxFD+1, &readFDSet, NULL, NULL, NULL);
if (selRet >= 0)
{
if ((FD_ISSET(stdinFD, &readFDSet))&&(ReadTextFrom("stdin", stdinFD) <= 0)) break;
if ((FD_ISSET(pipeReadFD, &readFDSet))&&(ReadTextFrom("pipe", pipeReadFD) <= 0)) break;
}
else
{
perror("select");
break;
}
}
printf("Child thread exiting!\n");
return NULL;
}
int main(int argc, char ** argv)
{
int pipeFDs[2];
if (pipe(pipeFDs) < 0)
{
perror("pipe");
return -1;
}
pipeReadFD = pipeFDs[0];
int pipeWriteFD = pipeFDs[1];
pthread_t tid;
if (pthread_create(&tid, NULL, &init_on_sys_ready, NULL) != 0) {
printf("Failed to initialize CLI\n");
exit(1);
}
int count = 0;
for (int count=0; count < 5; count++)
{
char buf[512];
snprintf(buf, sizeof(buf), "Hello #%i from main thread", ++count);
const size_t slen = strlen(buf);
if (write(pipeWriteFD, buf, slen) == slen)
{
printf("main() sent [%s] to the child thread via the pipe.\n", buf);
}
else
{
perror("write");
break;
}
sleep(5);
}
close(pipeWriteFD); // this will cause the child thread to exit ASAP
pthread_join(tid, NULL);
return 0;
}
popen's man states:
[...] the command's standard output is the same as that of the process that called popen()
So you just need a way to redirect stdout to stdin.
Which is exactly what pipe is for. It links an output fd with an input fd.
As pipe creates new fds, we need to use dup2 to replace stdin and stdout, as you've already did in your example code. Threads share the same memory, so you don't have to worry about any child/parent differences in fds.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
void* child_thread_func(void* terminalflag)
{
int num=0;
printf("Trying to read num from stdin\n");
scanf("%d",&num);
/*expecting 9 to be printed here*/
printf("Entered number is %d\n", num);
}
int main () {
setbuf(stdin, NULL);
pthread_t tid;
if (pthread_create(&tid, NULL, &child_thread_func, NULL) != 0) {
printf("Failed to initialize thread\n");
exit(1);
}
int save_stdin = dup(STDIN_FILENO);
int save_stdout = dup(STDOUT_FILENO);
int tube[2];
pipe(tube);
dup2(tube[0], STDIN_FILENO);
dup2(tube[1], STDOUT_FILENO);
char buffer[50] = {0};
FILE *wfp = popen("wc -c", "w");
if (wfp != NULL) {
sprintf(buffer, "dummyword");
fwrite(buffer, sizeof(char), strlen(buffer), wfp);
pclose(wfp);
}
dup2(save_stdin, STDIN_FILENO);
dup2(save_stdout, STDOUT_FILENO);
pthread_join(tid, NULL);
}

How to handle Ctrl+C with <signal.h>

I'm attempting communications between a user-level program and a kernel module. My user-level program starts a timer then goes to sleep and is woken up by the kernel module when the time is completed, then a message is printed.
The problem I'm having is if I Ctrl+C while the user-level program is sleeping, when the timer expires it still prints the message (which I don't want it to do). This is my first time using <signal.h> and I'm having trouble figuring out how to implement this. I found a website which said that SIGHUP is the signal that is sent when Ctrl+C is pressed, so I think I should implement some case for this signal in my sighandler() function, but I'm pretty lost at the moment on how to go about this. Here's what I've pieced together so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>
void sighandler(int);
int main(int argc, char **argv) {
char line[256];
int lenNum, lenName;
struct sigaction action, oa;
int pFile, oflags;
pFile = open("/dev/mytimer", O_RDWR);
if (pFile < 0) {
fprintf (stderr, "mytimer module isn't loaded\n");
return 1;
}
// Setup signal handler
memset(&action, 0, sizeof(action));
action.sa_handler = sighandler;
action.sa_flags = SA_SIGINFO;
sigemptyset(&action.sa_mask);
sigaction(SIGIO, &action, NULL);
fcntl(pFile, F_SETOWN, getpid()); // process ID o owner saved to filp->f_owner
oflags = fcntl(pFile, F_GETFL);
fcntl(pFile, F_SETFL, oflags | FASYNC); // enable asynchronous notification
// Check if timer set
if (argc >= 4 && strcmp(argv[1], "-s") == 0) {
lenNum = strlen(argv[2]);
lenName = strlen(argv[3]);
char *ptr;
int i;
i = asprintf(&ptr, "%s %s %s", argv[1], argv[2], argv[3]);
write(pFile, ptr, i+1);
while (read(pFile, line, strlen(line)) != 0) { //257?
printf("%s", line);
}
free(ptr);
//printf("Sleep!\n");
pause();
printf("%s\n", argv[3]);
}
// List all active timers
else if (argc == 2 && strcmp(argv[1], "-l") == 0) {
char *ptr;
int i;
i = asprintf(&ptr, "%s 0", argv[1]);
write(pFile, ptr, i);
while (read(pFile, line, strlen(line)) != 0) {
printf("%s", line);
}
free(ptr);
}
close(pFile);
return 0;
}
// SIGIO handler
void sighandler(int signo) {
//printf("Awaken!\n");
/* maybe something like this?
if (signo == SIGHUP) {
// clear timer
} */
}
Any suggestions are greatly appreciated.

Sending signal to all threads

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;
}
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.
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.

Fork() and Posix Queues. Send and Receive strings

what I want is this:
1 main process that create 4 children process where:
-> The main process receive messages from the children through the queue and print the message recieved.
-> The children send messages (a string with priority+message) through the queue and finish.
All in a while (1), so, when you CTRL+C, the children finish first (the signal is in the children code) and then, the parent finish.
For the moment, I am having problem with mq_send() and mq_recieve().
Well, this is my code:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>
#include <mqueue.h>
void sigint_handler()
{
/*do something*/
printf("killing process %d\n",getpid());
exit(0);
}
int main ()
{
mqd_t mqd;
struct mq_attr atributos;
// atributos.mq_maxmsg = 10;
//
// atributos.mq_msgsize = 50;
printf ("This is the parent. PID=%d\n",getpid ());
int num_children = 4;
int i;
int pid;
int status;
char buffer [50];
while (1){
for (i=0; i<num_children ;i++){
if ((pid=fork()==0)){
signal(SIGINT, sigint_handler);
int prio = rand () % 3;
printf ("%d\n",prio);
char * msg= "Hi dude";
char * priority=NULL;
if (prio == 0){
priority = "NORMAL";
}
else {
priority = "URGENT";
}
char* toSend=NULL;
toSend = malloc(strlen(msg)+1+strlen(priority));
strcpy (toSend,priority);
strcat (toSend,msg);
printf ("%s\n",toSend);
if ((mqd=mq_open("/queue.txt", O_CREAT|O_WRONLY, 0777, &atributos))==-1){
printf ("Error mq_open\n");
exit(-1);
}
if (mq_send(mqd, msg , strlen(toSend), prio) == -1) {
printf ("Error mq_send\n");
exit (-1);
}
mq_close(mqd);
printf ("This is children %d\n",getpid());
sleep(1);
exit(0);
}
}
if ((mqd=mq_open("/queue.txt", O_CREAT|O_WRONLY, 0777, &atributos))==-1){
printf ("Error mq_open\n");
exit(-1);
}
//Rest Parent code
if (mq_receive(mqd, buffer, strlen(buffer),0)==-1){
printf ("Error mq_recieve\n");
exit(-1);
}
printf("Received: %s\n",buffer);
sleep (1);
waitpid(pid,&status,0);
printf ("This is the parent again %d, children should have finished\n",getpid());
mq_close(mqd);
}
}
I don't know why both mq_send() and mq_receive() returns -1, what am I doing wrong¿?
And you you see something wrong in my code in order to do what I intend apart from the error I am talking about, let me know.
Thank you in advance, I appreciate any help.
user58697 touched upon the biggest problems.
(1) Your queue opens were failing with EINVAL because you wee passing uninitialized attributes because you commented out assignments.
(2) You were opening both queues for write-only. The parent queue needed to be opened in read mode.
(3) Execute permissions don't mean anything to a queue so 777 permissions while not invalid are unnecessary.
(4) Your sends/receives were failing because of invalid lengths. In many if not most cases it is just easier and safer to allocate your buffers to the length attribute of the queue. In this case you know the length before hand but in programs that don't you can get the value via mq_getattr.
(5) You weren't calling srand to seed the RNG before calling rand.
(6) You had a memory leak where you allocate space (unnecessarily) for the message but never freed it.
(7) What you were trying to do with passing priorities is redundant. POSIX MQs have priorities already built in. You can just use those.
I took out some of the fluff (mainly the loops & signals) to concentrate more on the queue aspects of your program.
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <signal.h>
#include <sys/wait.h>
#include <mqueue.h>
int main()
{
srand(time(NULL));
mqd_t mqd;
struct mq_attr atributos = {.mq_maxmsg = 10, .mq_msgsize = 50};
int i;
int pid;
int status;
int num_children = 4;
char buffer[atributos.mq_msgsize];
for (i = 0; i < num_children; i++)
{
if ((pid = fork() == 0))
{
int prio = rand () % 3;
char* msg = "Hi dude";
strncpy (buffer, msg, sizeof(buffer));
if ((mqd = mq_open("/queue.txt", O_CREAT | O_WRONLY, 0666, &atributos)) == -1)
{
perror("child mq_open");
exit(1);
}
if (mq_send(mqd, buffer, sizeof(buffer), prio) == -1)
{
perror("mq_send");
exit(1);
}
mq_close(mqd);
exit(0);
}
}
// parent
if ((mqd = mq_open("/queue.txt", O_CREAT | O_RDONLY, 0666, &atributos)) == -1)
{
perror("parent mq_open");
exit(1);
}
int priority;
for (int i = 0; i < num_children; ++i)
{
if (mq_receive(mqd, buffer, sizeof(buffer), &priority) == -1)
{
perror("mq_recieve");
exit(1);
}
printf("Received (%s): %s\n", (priority == 0) ? "NORMAL" : "URGENT", buffer);
pid_t childpid;
if ((childpid = waitpid(-1, &status, 0)) > 0)
{
if (WIFEXITED(status))
printf("PID %d exited normally. Exit status: %d\n",
childpid, WEXITSTATUS(status));
else
if (WIFSTOPPED(status))
printf("PID %d was stopped by %d\n",
childpid, WSTOPSIG(status));
else
if (WIFSIGNALED(status))
printf("PID %d exited due to signal %d\n.",
childpid,
WTERMSIG(status));
}
}
mq_close(mqd);
}
First and foremost, when a system call fails, print errno (and strerror(errno)).
Now, obvious mistakes:
as was mentioned, you need a read access to be able to mq_receive()
what is strlen(buffer)?
you are passing attributes without initializing them.
To summarize, print errno and see what is wrong.

How to programmatically stop reading from stdin?

fgetc() and other input functions can return when there's no data on the file descriptor. This can be simulated for console applications reading from stdin typing Ctrl-D on keyboard (at least on unix). But how to do it programmatically? For example, how to return from the fgetc() in the reader thread in the following code (NB: ignore the possible race condition)?
#include <pthread.h>
#include <stdio.h>
void* reader()
{
char read_char;
while((read_char = fgetc(stdin)) != EOF) {
;
}
pthread_exit(NULL);
}
int main(void)
{
pthread_t thread;
pthread_create(&thread, NULL, reader, NULL);
// Do something so the fgetc in the reader thread will return
pthread_exit(NULL);
}
Thanks!
It seems you want a threads to stop blocking on fgetc(stdin) when some event occurs to handle that event instead. If that's the case you could select() on both stdin and some other message pipe so that the thread can handle input from both:
fd_set descriptor_set
FD_ZERO(&descriptor_set);
FD_SET(STDIN_FILENO, &descriptor_set);
FD_SET(pipefd, &descriptor_set);
if (select(FD_SETSIZE, &descriptor_set, NULL, NULL, NULL) < 0)
{
// select() error
}
if (FD_ISSET(STDIN_FILENO, &descriptor_set)) {
// read byte from stdin
read(STDIN_FILENO, &c, 1);
}
if (FD_ISSET(pipefd, &descriptor_set))
// Special event. Do something else
Also note that only one thread in your process should be reading from stdin.
You can either 'close' standard input, or connect standard input to '/dev/null' ('NUL:' on Windows) with freopen(), or you can connect standard input to '/dev/zero'.
If you close it, every function call will fail because the file stream is not valid. If you connect it to the null data source, all reads will fail and return EOF immediately. If you connect it to the zero data source, every read will succeed and return a corresponding number of zero bytes.
It is possible one of those will suit your needs sufficiently. If not, then you probably need to give us a more detailed explanation of what you actually need.
With POSIX you can signal the thread whose primitives (e.g. "read") are blocking, and if you have set up a do-nothing signal handler with the SA_RESTART bit cleared then the primitives will fail with EINTR errors. Here's a working version of the original:
#include <pthread.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
static void SignalHandler(int signum)
{
}
void* reader(void *arg)
{
char read_char;
while((read_char = fgetc(stdin)) != EOF) {
;
}
printf("leaving reader\n");
return NULL;
}
int main(int argc, const char * argv[])
{
struct sigaction action;
memset(&action, 0, sizeof(action)); // SA_RESTART bit not set
action.sa_handler = SignalHandler;
sigaction(SIGUSR1, &action, NULL);
pthread_t thread;
pthread_create(&thread, NULL, reader, NULL);
sleep(1); // time to start reader thread
// Do something so the fgetc in the reader thread will return
pthread_kill(thread, SIGUSR1);
sleep(1); // time to exit reader thread; could join it if set up
return 0;
}
Alexandre posted the correct solution. His answer respond precisely to the question I asked. It follows simple self compiling code based on his hints:
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/select.h>
static int pipe_fds[2];
void* user_interaction()
{
char read_char;
fd_set descriptor_set;
FD_ZERO(&descriptor_set);
FD_SET(STDIN_FILENO, &descriptor_set);
FD_SET(pipe_fds[0], &descriptor_set);
while(1)
{
if (select(FD_SETSIZE, &descriptor_set, NULL, NULL, NULL) < 0) {
// select() error
}
if (FD_ISSET(STDIN_FILENO, &descriptor_set)) {
// read byte from stdin
read(STDIN_FILENO, &read_char, 1);
// Re-set the selected file descriptor so it can
// be signaled again
FD_SET(STDIN_FILENO, &descriptor_set);
}
if (FD_ISSET(pipe_fds[0], &descriptor_set))
// Special event. break
break;
}
pthread_exit(NULL);
}
int main(void)
{
pipe(pipe_fds);
pthread_t thread;
pthread_create(&thread, NULL, user_interaction, NULL);
// Before closing write pipe endpoint you are supposed
// to do something useful
sleep(5);
close(pipe_fds[1]);
pthread_join(thread, NULL);
pthread_exit(NULL);
}
I tried to use the code from this answer in a slightly modified form:
void *user_interaction()
{
char ch;
int rv;
fd_set set;
FD_ZERO(&set);
FD_SET(STDIN_FILENO, &set);
FD_SET(pipe_fds[0], &set);
while (1)
{
rv = select(pipe_fds[0] + 1, &set, NULL, NULL, NULL);
if (rv < 0)
{
printf(">>> select(): error occurred, %d\n", rv);
break;
}
if (FD_ISSET(pipe_fds[0], &set))
{
printf(">>> pipe_fds[0]: is ready\n");
break;
}
if (FD_ISSET(STDIN_FILENO, &set))
{
read(STDIN_FILENO, &ch, 1);
write(STDOUT_FILENO, &ch, 1);
}
}
pthread_exit(NULL);
}
but wasn't getting the expected behaviour. When executed like in the below:
$ echo -n 1 | ./a.out
my terminal was being rendered with 1's in the infinite loop and the pipe was never reported by select() to be ready (i.e. even after close()ing it in the main thread).
With some experimentation, I figured that you need to move FD_ZERO/FD_SET inside the loop, to get select() to work as desired:
void *user_interaction()
{
char ch;
int rv;
fd_set set;
while (1)
{
FD_ZERO(&set);
FD_SET(STDIN_FILENO, &set);
FD_SET(pipe_fds[0], &set);
rv = select(pipe_fds[0] + 1, &set, NULL, NULL, NULL);
if (rv < 0)
{
printf(">>> select(): error occurred, %d\n", rv);
break;
}
if (FD_ISSET(pipe_fds[0], &set))
{
printf(">>> pipe_fds[0]: is ready\n");
break;
}
if (FD_ISSET(STDIN_FILENO, &set))
{
read(STDIN_FILENO, &ch, 1);
write(STDOUT_FILENO, &ch, 1);
}
}
pthread_exit(NULL);
}

Resources