Усиливает ли выходной сигнал boost :: asio при использовании async_read_until? - PullRequest
3 голосов
/ 09 апреля 2011

Это меня поставило в тупик !

При использовании вызова async_read_until может возникнуть проблема с распечаткой чего-либо более 4 Кбайт?

У меня есть маленькая функция, которая печатает 100 строк (чуть больше 4k).

Прекрасно работает в любой комбинации, кроме случаев регистрации обратного вызова async_read_until. В этот момент мой вывод обрезается до 4k. Заметьте, что не всегда, хотя иногда меньше, а иногда и все это напечатано, похоже, что оно связано с нагрузкой на машину, почти как в случае тайм-аута? Какая-то нить Asio? В любом случае, если я закомментирую вызов async_read_until, он работает каждый раз, независимо от того, сколько раз я звоню printLines. Я даже могу использовать функцию ioService post, она отлично работает ...

Что происходит? Кстати, я использую Linux и AMD64 машины GCC4.4. (Redhat)


Используя linux ' strace ', я получил еще несколько подсказок:

Кажется, что после вызова async_read_until asio с использованием вызовов fcntl заставляет мой дескриптор выходного файла изменить поведение?
через некоторое время он прекращает печать:

выберите (4, [0 3], [], [], {300, 0}) = 1 (в [0], слева {298, 830000})
readv (0, [{"\ n \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 "..., 512}], 1) = 1
написать (2, «Это тест длинного предложения» ..., 93) = 93
написать (2, «Это тест длинного предложения» ..., 93) = 93
... около 40 раз
написать (2, «Это тест длинного предложения» ..., 93) = 53
write (2, "Это тест длинного предложения" ..., 93) = -1 EAGAIN (Ресурс временно недоступен)
write (2, "Это тест длинного предложения" ..., 93) = -1 EAGAIN (Ресурс временно недоступен)
write (2, "Это тест длинного предложения" ..., 93) = -1 EAGAIN (Ресурс временно недоступен)
write (2, "Это тест длинного предложения" ..., 93) = -1 EAGAIN (ресурс временно недоступен)
write (2, "Это тест длинного предложения" ..., 93) = -1 EAGAIN (Ресурс временно недоступен)
... для остальных, пока 100 не будет достигнуто.

Таким образом, вы можете увидеть цикл выбора, ожидающий мою клавишу ввода ввода. Тогда мы называем мой пустой прочитайте обработчик и выйдите из ioService. В это время я вызываю свою функцию printLines и пытаюсь напечатать 100 строк, но она завершается после печати 40 некоторых.

Что-то с этим EAGAIN приводит к тому, что вывод прекращает запись.
Опять же, если я не вызову async_read_until, мой printf не будет поврежден.


Мне кажется, я знаю, что происходит , похоже, Asio переводит мой дескриптор файла stdout в асинхронный неблокирующий режим, когда я запрашиваю асинхронный режим чтения в дескрипторе файла stdin. Вот почему после некоторого вывода я получаю EAGAIN ошибки при записи. Конечно, printf игнорирует их, поэтому мой вывод усекается. Не знаете, это ошибка в Asio или просто побочный эффект в Linux?


Вот моя простая программа для дублирования проблемы:

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <iostream>

using namespace boost::asio;
using namespace std;

io_service ioService;
boost::asio::streambuf inStream;
posix::stream_descriptor input(ioService, STDIN_FILENO);

void printLines()
{
    for (int i = 0; i < 100; i++) {
        fprintf(stderr, "This is a test of a long sentence, there will be %d more sentences after this on is printed.\n", i);
    }
    fflush(stderr);
}

void readHandler(const boost::system::error_code& error)
{ // Don't care about read!! }

int main()
{
    boost::system::error_code ec;
    //printLines(); << this works if uncommented
    //ioService.post(printLines); << this works if uncommented

    boost::asio::async_read_until(input, inStream, "\n",
    bind(readHandler, placeholders::error)); // causes truncated output

    cout << "Hit Return to continue..." << endl;
    ioService.run_one( ec );
    assert(!ec);
    printLines(); // partial output if async_read_until is called?
    return 0;
}

1 Ответ

2 голосов
/ 10 апреля 2011

Если я вставлю следующий вызов в ioctl до printLines

int opt = 0;
ioctl( STDIN_FILENO, FIONBIO, &opt );
printLines();

, то ожидаемое поведение

samm@macmini ~> ./a.out
Hit Return to continue...

This is a test of a long sentence, there will be 0 more sentences after this on is printed.
...
This is a test of a long sentence, there will be 96 more sentences after this on is printed.
This is a test of a long sentence, there will be 97 more sentences after this on is printed.
This is a test of a long sentence, there will be 98 more sentences after this on is printed.
This is a test of a long sentence, there will be 99 more sentences after this on is printed.
samm@macmini ~>

Хотя мне не понятно, почему это происходит,это может быть побочный эффект ioctl, который в значительной степени зависит от ядра / устройства.Я не видел ничего описывающего это поведение при просмотре различных справочных страниц.Вы можете попробовать переключиться с использования fprintf в printLines на использование posix::stream_descriptor с STDERR_FILENO.


Редактировать: похоже, что были последние изменения Boost.Asio, который может решить эту проблему.В частности

  • Добавлены новые функции non_blocking () для управления неблокирующим поведением сокета или дескриптора.Команды io_control () с именем non_blocking_io теперь устарели в пользу этих новых функций.

  • Добавлены новые функции native_non_blocking () для управления неблокирующим режимом базового сокета или дескриптора.Эти функции предназначены для того, чтобы разрешить инкапсуляцию произвольных неблокирующих системных вызовов как асинхронные операции способом, который прозрачен для пользователя объекта сокета.Функции не влияют на поведение синхронных операций сокета или дескриптора.

выглядит мне интересно.Я использовал Boost 1.45 на своем Mac для запуска вашего репродуктора, и эти изменения будут в предстоящем Boost 1.47.Я попытаюсь взять последний выпуск разработки для asio Криса и посмотреть, изменилось ли поведение.

...