Как заставить поток продолжить после того, как write () записал меньше байтов, чем запрошено? - PullRequest
1 голос
/ 24 мая 2009

Я использую следующий код для записи данных через именованный канал из одного приложения в другое. Нить, в которой происходит запись, никогда не должна закрываться. Но если r_write () возвращает меньше, чем должно, поток / программа по какой-то причине останавливается. Как я могу продолжить поток, когда запись вернула меньше, чем должна?

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

}

Ответы [ 3 ]

3 голосов
/ 25 мая 2009

Если write() возвращает положительное (ненулевое, неотрицательное) значение для количества записанных байтов, оно прошло успешно, но не было места для всех данных. Попробуйте еще раз, записав остаток данных из буфера (и повторите при необходимости). Не забывайте, что FIFO имеет ограниченные возможности - и писатели будут задержаны в случае необходимости.

Если write() возвращает отрицательное значение, запись не удалась. Скорее всего, вы не сможете восстановить, но проверьте errno по причине.

Я думаю, что единственное обстоятельство, когда write() может вернуть ноль, это если у вас есть файловый дескриптор, открытый с O_NONBLOCK, и попытка записи блокирует. Возможно, вам придется изучить страницу руководства для write(), чтобы проверить наличие других возможностей.

То, что делает ваша ветка, зависит от того, почему произошла короткая запись, и что вы хотите с ней сделать.

1 голос
/ 24 мая 2009

Запись в FIFO не удалась. Исследуйте значение errno, чтобы выяснить, почему. Посмотрите на errno.h в вашей системе, чтобы расшифровать значение errno. Если программа завершается при попытке записи в консоль, причина может быть связана.

Кроме того, ваш цикл не закрывает дескриптор файла для FIFO (close(fd)).

Наконец, вы упоминаете многопоточность. Поток стандартной библиотеки cout в вашей системе может быть (и, вероятно, не) поточно-ориентированным. В этом случае запись в консоль одновременно из нескольких потоков приведет к непредсказуемым ошибкам.

0 голосов
/ 24 мая 2009

Вам нужно сделать дескриптор файла неблокирующим. Вы можете сделать это так:

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

Объяснение

Так работает fcntl (не полное описание - посмотрите на man fcntl). Прежде всего, включает в себя:

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

чтение флагов дескриптора файла

Используйте F_GETFL, чтобы получить флаги дескриптора файла. От 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.

и вот как это используется:

int fd_flags = fcntl(fd, F_GETFL);

запись флагов дескриптора файла

Используйте F_SETFL, чтобы установить флаг O_NONBLOCK. Опять цитата из 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.

и вот как это используется:

fcntl(fd, F_SETFL, fd_flags | O_NONBLOCK);
...