How to get a thread to continue after write() has written less bytes than requested?
-
05-09-2019 - |
Question
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);
}
}
}
Solution
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.
OTHER TIPS
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.
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);