views:

239

answers:

3

I'm using the following code to write data through a named pipe from one application to another. The thread where the writing is taken place should never be exited. But if r_write() returns less than it should, the thread/program stops for some reason. How can I make the thread continue once write has returned less than it should?

ssize_t r_write(int fd, char *buf, size_t size) 
{
   char *bufp;
   size_t bytestowrite;
   ssize_t byteswritten;
   size_t totalbytes;

   for (bufp = buf, bytestowrite = size, totalbytes = 0;
        bytestowrite > 0;
        bufp += byteswritten, bytestowrite -= byteswritten) {
      byteswritten = write(fd, bufp, bytestowrite);
      if ((byteswritten) == -1 && (errno != EINTR))
         return -1;
      if (byteswritten == -1)
         byteswritten = 0;
      totalbytes += byteswritten;
   }
   return totalbytes;
}




void* sendData(void *thread_arg)
{
    int fd, ret_val, count, numread;
    string word;
    char bufpipe[5];

    ret_val = mkfifo(pipe, 0777); //make the sprout pipe

    if (( ret_val == -1) && (errno != EEXIST)) 
    {
     perror("Error creating named pipe");
     exit(1);
    } 
    while(1)
    {
     if(!sproutFeed.empty())
     {  
      string s;
      s.clear();
      s = sproutFeed.front();

      int sizeOfData = s.length();
      snprintf(bufpipe, 5, "%04d", sizeOfData);

      char stringToSend[strlen(bufpipe) + sizeOfData +1];
      bzero(stringToSend, sizeof(stringToSend));     
      strncpy(stringToSend,bufpipe, strlen(bufpipe));   
      strncat(stringToSend,s.c_str(),strlen(s.c_str()));
      strncat(stringToSend, "\0", strlen("\0"));     
      int fullSize = strlen(stringToSend);
      cout << "sending string" << stringToSend << endl;
      fd = open(pipe,O_WRONLY);
      int numWrite = r_write(fd, stringToSend, strlen(stringToSend) );
      if(numWrite != fullSize)
      {
       bzero(bufpipe, strlen(bufpipe));
       bzero(stringToSend, strlen(stringToSend));
       cout << "NOT FULL SIZE WRITE " << endl; //program ends here??
      }
      else
      {
              sproutFeed.pop();
              bzero(bufpipe, strlen(bufpipe));
              bzero(stringToSend, strlen(stringToSend));
      }     
     }
     else
     {
      sleep(1);
     }
    }

}
+1  A: 

The write to the FIFO failed. Investigate the value of errno to find out why. Look in errno.h on your system to decipher the value of errno. If the program is ending upon trying to write to the console, the reason may be related.

Also, your loop doesn't appear to be closing the file descriptor for the FIFO (close(fd)).

Finally, you mention multithreading. The standard library stream cout on your system may not (and probably isn't) thread-safe. In that case, writing to the console concurrently from multiple threads will cause unpredictable errors.

Barry Kelly
I added this line under the call to write.if(errno == EPIPE){ signal(SIGPIPE,SIG_IGN);}Now it will transfer alot more data, but eventually it will still receive a broken pipe signal on write. Is there another way to completly ignore all broken pipe errors? I've found a way to do it through sockets but not with files.
whatWhat
A: 

You need to make the file descriptor non-blocking. You can do it like this:

fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);


Explanation

This is how fcntl works (not a complete description - look at man fcntl for that). First of all, the includes:

#include <unistd.h>
#include <fcntl.h>

reading the file descriptor's flags

Use F_GETFL to get the file descriptor's flags. From man fcntl:

       F_GETFL
              Read the file descriptor's flags.

RETURN VALUE
       For a successful call, the return value depends on the operation:

       F_GETFL  Value of flags.

and this is how it's used:

int fd_flags = fcntl(fd, F_GETFL);

writing the file descriptor's flags

Use F_SETFL to set the O_NONBLOCK flag. Again, quoting from man fcntl:

       F_SETFL
              Set the file status flags part of the descriptor's flags to the
              value specified by arg.  Remaining bits (access mode, file cre?
              ation  flags)  in  arg  are ignored.  On Linux this command can
              only change the O_APPEND,  O_NONBLOCK,  O_ASYNC,  and  O_DIRECT
              flags.

and this is how it's used:

fcntl(fd, F_SETFL, fd_flags | O_NONBLOCK);
Nathan Fellman
+1  A: 

If the write() returns a positive (non-zero, non-negative) value for the number of bytes written, it was successful, but there wasn't room for all the data. Try again, writing the remainder of the data from the buffer (and repeat as necessary). Don't forget, a FIFO has a limited capacity - and writers will be held up if necessary.

If the write() returns a negative value, the write failed. The chances are that you won't be able to recover, but check errno for the reason why.

I think the only circumstance where write() can return zero is if you have the file descriptor open with O_NONBLOCK and the attempt to write would block. You might need to scrutinize the manual page for write() to check for any other possibilities.

What your thread does then depends on why it experienced a short write, and what you want to do about it.

Jonathan Leffler