0

I had perfectly correct and working program around 5 years ago. At that point I stopped using it, I upgraded the OS, time passed, dust covered the code, and finally I dug it up just to discover it does not communicate with subprocess anymore.

Here is the code (simplified, but it shows the problem):

#include <stdio.h> #include <signal.h> #include <fcntl.h> #include <errno.h> #include <sys/types.h> #include <string.h> #include <sys/time.h> #include <sys/stat.h> #include <iostream> #include <string> #include <cassert> int main() { std::cout << "Creating the pipe for reading from the process" << std::endl; const std::string pipe_name = "/tmp/proc_comm"; { int res = mkfifo(pipe_name.c_str(),0777); assert(res==0); } std::cout << "Launching subprocess" << std::endl; FILE *cmd_handle = popen(("espeak -x -q -z 1> "+pipe_name+" 2> /dev/null").c_str(), "w"); assert(cmd_handle!=0); std::cout << "Opening the pipe" << std::endl; int pipe_id = open(pipe_name.c_str(),O_RDONLY); assert(pipe_id!=-1); const std::string message = "hello\n"; std::cout << "Sending the message" << std::endl; if (!fwrite(message.c_str(),sizeof(char),message.length(),cmd_handle)) assert(0); if (ferror(cmd_handle)) assert(0); if (fflush(cmd_handle)!=0) assert(0); fd_set output_set; FD_ZERO(&output_set); FD_SET(pipe_id,&output_set); static timeval timeout; timeout.tv_sec = 10; timeout.tv_usec = 0; std::cout << "Selecting the pipe for reading" << std::endl; const int inputs = select(pipe_id+1, // max of pipe ids + 1 &output_set,0,0,&timeout); if (inputs==-1) // error assert(0); else if (inputs==0) // nothing to read assert(0); // HERE (*) else { // we can only read from our pipe assert(inputs==1); assert(FD_ISSET(pipe_id,&output_set)); const int bufsize = 20000; char char_buf[bufsize]; memset(char_buf,0,sizeof(char_buf)); std::cout << "Reading from the pipe" << std::endl; const int count = read(pipe_id,char_buf,bufsize-1); if (count==-1) assert(0); std::cout << "Read " << count << std::endl; } return 0; } 

It compiles, it runs, the pipe for reading from the process is created and opened, the subprocess is launched, the message is sent but there is nothing to read from the process (the line HERE (*)).

So how do you read from the process nowadays? If possible I would like to keep the general workflow, i.e. using pipe for reading, and a process handle for writting to process.

2
  • How do you know the subprocesss sent something before your timeout? Commented Aug 10, 2014 at 14:16
  • @jxh, I can run the process in bash and evaluate the timeout. 10 seconds is more than enough. Besides it worked :-) Commented Aug 10, 2014 at 17:29

1 Answer 1

2

Perhaps the FIFO is empty at the moment of select() returns.

This simple experiment shows it.

  • Running your code with the modified line
    FILE *cmd_handle = popen (("echo begin; espeak -x -q -z 1>" + pipe_name + " 2>/dev/null; echo end;").c_str (), "w");.
  • cat < /tmp/proc_commn &; ./a.out;
  • Output is:

    Creating the pipe for reading from the process
    Launching subprocess
    Opening the pipe
    begin
    Sending the message
    Selecting the pipe for reading
    a.out: main.cpp:64: int main(): Assertion `0' failed.
    h@l'oU       #### <--- Data in FIFO only after assertion.
    end

Using pclose().

With addition of pclose() call, the problem disapears:

if (!fwrite(message.c_str(),sizeof(char),message.length(),cmd_handle)) assert(0); if (ferror(cmd_handle)) assert(0); if (fflush(cmd_handle)!=0) assert(0); /* pclose added. */ if (pclose (cmd_hanlde) < 0) { /* handle error. */ } fd_set output_set; 

So how do you read from the process nowadays?

I don't think, that your code became obsolete in any sense. I'm not an expert, so I don't have good answer on this question. But I hope, that this post will help you with solving your problem.

Update.

Debugging with strace and espeak 1.46.2.

Running your code with strace (strace -f ./a.out) and any select() timeout:
Porcess 31985 is the parent process (main process).
Porcess 31987 is the child process (espeak).

# Child process starts trying to read from stdin. [pid 31987] read(0, <unfinished ...> [pid 31985] <... sync resumed> ) = 0 [pid 31985] write(1, "Sending the message\n", ... [pid 31985] fstat64(4, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0 [pid 31985] mmap2(NULL, 4096, PROT_READ|PROT_WRITE, ... # Main process writes message to the pipe. [pid 31985] write(4, "hello\n", 6) = 6 # Child process reads message from the pipe. [pid 31987] <... read resumed> "hello\n", 4096) = 6 [pid 31985] write(1, "Selecting the pipe for reading:3"..., 3 ... ) = 33 [pid 31987] fstat64(1, <unfinished ...> # Main process calls select(). [pid 31985] select(4, [3], NULL, NULL, {10, 0} <unfinished ...> [pid 31987] <... fstat64 resumed> {st_mode=S_IFIFO|0755, st_size=0, ...}) = 0 [pid 31987] mmap2(NULL, 4096, PROT_READ ... # Child process calls read() again (second time). # Seems like it waits another portion of input. # Note: The previous input don't processed yet. [pid 31987] read(0, <unfinished ...> # Despite big value of `select()` timeout, there is no data processing here. # Select returns by timeout. [pid 31985] <... select resumed> ) = 0 (Timeout) [pid 31985] write(2, "a.out: main.cpp:69: i ... ) = 54 [pid 31985] rt_sigprocmask(SIG_UNBLOCK, [ABRT], NULL, 8) = 0 [pid 31985] gettid() = 31985 [pid 31985] tgkill(31985, 31985, SIGABRT) = 0 [pid 31985] --- SIGABRT (Aborted) @ 0 (0) --- 

Why there is no input processing in strace log?

Let's look at the source code of espeak. This is the code of reading input from stdin (src/espeak.cpp) (Assuming correctness of finding out mapping of command line arguments on a set of internal variables):

 728 // line by line input on stdin 729 while(fgets(p_text,max,stdin) != NULL) // <--- * here first and second 730 { // <--- * read() call. 731 p_text[max-1] = 0; 732 espeak_Synth(p_text,max,0,POS_CHARACTER,0,synth_flags,NULL,NULL); 733 734 } 

Comments about espeak_Synth() function (src/speak_lib.h:268):

 /* * Synthesize speech for the specified text. * The speech sound data is passed to the calling * program in buffers by means of the callback function specified by * espeak_SetSynthCallback(). The command is asynchronous: * it is internally buffered and returns as soon as possible. * ... */ 

espeak_Synth() is asynchronous function (thats why we see two read() calls in a row). I assume that call of espeak_Synth(), should result in data written to stdout (after some delay). But it is not happening even with big value of the select() timeout. It's something strange here.

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you very much for your help +1. I am not accepting your answer (I am really grateful, don't get me wrong) for now, because there is something odd with select -- you can increase timeout, and still it will report there is no data (despite there are!). As with pclose it terminates the process, so there would be big penalty for adding it to the workflow (think of thousands of request to a subprocess). You should be a able to launch subprocess (deamon) and communicate with it without killing it on each request. All in all you gave me great hints, thank you!
Thank you for all your help and work. I came to conclusion there is a subtle bug in espeak, and the communication itself is OK. I tried simple echo (mine) program which prints back the input and it is fine.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.