0

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.

Harish Reddy
  • 65
  • 1
  • 6

1 Answers1

0

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;
}
Jeremy Friesner
  • 70,199
  • 15
  • 131
  • 234