Может ли io_context::strand гарантировать порядок между обработчиком завершения async_* и пользовательским функтором?

У меня есть такая ситуация, когда мне нужно, чтобы операция async_read была «подготовлена» для чтения, прежде чем я вызову пользовательскую функцию, которая отправляет что-то на противоположную часть веб-сокета. У меня есть обработчик завершения async_read, завернутый в ту же цепочку, в которую я отправляю свой функтор.

Проблема в том, что иногда on::read не вызывается, поэтому в основном я думаю, что writeFromCounterpart() вызывается до того, как ws_1 находится в состоянии «чтения».

Насколько я понимаю, прядь гарантирует порядок между обработчиками завершения, но я не понимаю, гарантирует ли это, что операция async_* «готова» (выполняется в своем потоке и читается) перед продолжением другой операции из пряди FIFO.

Полный код ниже: при его запуске большую часть времени я видел следующий вывод:

on_read called
Run 2 Handlers

Несколько раз я мог видеть следующее (~ 1 из 20 в состоянии стресса):

on_write called
Run 1 Handlers

Я никогда не видел и on_write, и on_read

Код:

#include <boost/asio.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/core.hpp>
#include <string>

using tcp = boost::asio::ip::tcp;               // from <boost/asio/ip/tcp.hpp>
namespace websocket = boost::beast::websocket;  // from <boost/beast/websocket.hpp>

boost::beast::multi_buffer buffer;
boost::asio::io_context ioc_1;
boost::asio::io_context ioc_2;
websocket::stream<tcp::socket> ws_1(ioc_1);
websocket::stream<tcp::socket> ws_2(ioc_2);


void on_write(boost::system::error_code, std::size_t) {
  std::cout << "on_write called" << std::endl << std::flush;
}

void writeFromCounterpart() {
  ws_2.async_write(boost::asio::buffer(std::string("Hello")), on_write);
}

void on_read(boost::system::error_code, std::size_t) {
  std::cout << "on_read called" <<std::endl << std::flush;
}

int main() {
  std::thread t([](){
    auto const address = boost::asio::ip::make_address("127.0.0.1");
    tcp::acceptor acceptor{ioc_2, {address, 30000}};
    tcp::socket socket{ioc_2};

    acceptor.accept(socket);
    websocket::stream<tcp::socket> ws{std::move(socket)};
    ws.accept();
    ws_2 = std::move(ws);
    ioc_2.run();
  });
  t.detach();

  // allow acceptor to accept
  std::this_thread::sleep_for(std::chrono::milliseconds(200));

  tcp::resolver resolver_(ioc_1);
  boost::asio::io_context::strand strand_(ioc_1);
  auto const results = resolver_.resolve("127.0.0.1", "30000");
  boost::asio::connect(ws_1.next_layer(), results.begin(), results.end());
  ws_1.handshake("127.0.0.1", "/");

  ws_1.async_read(buffer, strand_.wrap(on_read));
  strand_.post(writeFromCounterpart);
  auto handlers = ioc_1.run_for(std::chrono::milliseconds(5000));
  std::cout << "Run " + std::to_string(handlers) + " Handlers" << std::endl << std::flush;
}

person Liviu Stancu    schedule 28.11.2018    source источник
comment
Может быть, вы можете сделать свой пример кода автономным, чтобы мы действительно могли видеть, о чем вы говорите. Вы можете отказаться от Beast и просто использовать asio::ip::tcp::socket сокеты для упрощения.   -  person sehe    schedule 28.11.2018


Ответы (1)


Короткий ответ: да.

Документация для него здесь:

Проблема в том, что иногда, когда я запускаю этот поток, on::read не вызывается, поэтому в основном я думаю, что SendSomethingOnTheWs вызывается до того, как ws_ находится в состоянии «чтения».

Почему это важно? Потоковые сокеты (ws или другие) ведут себя как потоки. Если сокет был открыт, данные будут просто буферизованы.

person sehe    schedule 28.11.2018
comment
Я не уверен, что понимаю, а также я не совсем знаком с веб-сокетами: так вы говорите, что если одна конечная точка сокета пишет, а другая еще не читает, сообщение будет буферизовано, поэтому, как только встречная часть это чтение все равно получит его? В моем случае этого не происходит, иногда сообщение просто теряется. Если я добавлю сон, он никогда не будет потерян. Кроме того, важное замечание: и сервер (обновленный ws из сеанса http boost::beast), и клиентский аналог (boost::beast::websocket) работают в одном и том же процессе на локальном веб-сокете. Я скоро добавлю полный код. - person Liviu Stancu; 28.11.2018
comment
Как я уже сказал, чтобы задать убедительный вопрос, вы должны добавить SSCCE (sscce.org) или MVCE (stackoverflow.com/help/mcve). Я буду следить за вопросом до тех пор. Удачи - person sehe; 28.11.2018