boost::system::error_code::message() выбрасывает исключение нарушения доступа с boost::asio сокетом

Я реализую клиентское приложение, которое должно выполнить небольшое количество сокетных подключений к аппаратным устройствам. Я разбил проблему на следующее небольшое подмножество кода

boost::system::error_code ec;
std::string str_message = ec.message();  // no access violation before connect()
std::string str_port = "502";
std::string str_ip = "192.168.12.198";

boost::asio::io_service io_service;
boost::asio::ip::tcp::resolver resolver(io_service);
boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(),str_ip,str_port);
boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
boost::asio::ip::tcp::socket s(io_service);

ec = s.connect(*iterator,ec);
if (ec)
{
    // connection error of some kind.
    std::string str_debug = ec.message();  // BANG!!!!

}

Я использую Embarcadero RAD studio XE4 C++ Builder, и когда я запускаю приведенный выше код в основном потоке VCL, он работает нормально. Когда я запускаю его с несколькими подключениями, приведенный выше код выполняется в нескольких экземплярах TThread класс, и именно тогда у меня возникают проблемы с нарушением прав доступа - кажется, что когда error_code модифицируется connect вызов, внутренний член m_cat из error_code экземпляр становится NULL и поэтому, когда я звоню message() Я получаю нарушение доступа. Это происходит, даже когда у меня работает только один фоновый поток.

Возможно ли, что мой код выше не является потокобезопасным, так как мне нужно его использовать? Я попытался выяснить, почему этот код не работает в фоновом потоке, но не могу ничего найти о нем.

Буст-версия, которую я использую, составляет 1,50, поскольку это интегрированная версия, которая используется для создания 64-битных приложений в RAD studio.

Кто-нибудь еще сталкивался с этой проблемой в многопоточном режиме (в Embarcadero или другим способом), и если да, то как вы решили ее? Или этот класс просто небезопасен для многопоточного использования?

3 ответа

Это довольно далеко, но стоит попробовать:

system::error_code состоит из двух записей: значение ошибки и категория. Значение ошибки в основном просто int, но категория Синглтон. Это необходимо, поскольку категории ошибок сравниваются на равенство на основе идентификаторов указателей (т. Е. Две категории равны тогда и только тогда, когда они указывают на один и тот же объект категории).

Проблема в том, что инициализация категории Singleton может быть не поточно-ориентированной. Асио использует system_category, который реализован в boost/libs/system/src/error_code.cpp, Для 1.50 реализация выглядит так:

BOOST_SYSTEM_DECL const error_category & system_category() BOOST_SYSTEM_NOEXCEPT
{
  static const system_error_category  system_category_const;
  return system_category_const;
}

Это гарантированно является поточно-ориентированным на компиляторах, соответствующих C++11, но если ваш компилятор не реализует поточно-ориентированную инициализацию статики области функций, это может привести к поломке. Вы можете легко проверить это, отследив вызовы этой функции и посмотрев, наблюдаете ли вы потенциальную гонку.

Вы всегда должны быть уверены, что iterator != boost::asio::ip::tcp::resolver::iterator(),

Из форсированных документов:

Созданный по умолчанию итератор представляет конец списка.

Бьюсь об заклад, это проблема, connect() просто ломает стек из-за неправильного итератора конечной точки.

В нескольких примерах они вызывают io_service->run() в потоке: http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/example/cpp11/futures/daytime_client.cpp

Мне понравился этот, с ThreadPool: Пул потоков с использованием boost asio

Вы уверены, что io_service->run() где-то вызывается?

Другие вопросы по тегам