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 или другим способом), и если да, то как вы решили ее? Или этот класс просто не безопасен для многопоточного использования?

4

Решение

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

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, но если ваш компилятор не реализует поточно-ориентированная инициализация статики области действия, это может сломаться. Вы можете легко проверить это, отследив вызовы этой функции и посмотрев, наблюдаете ли вы потенциальную гонку.

5

Другие решения

В нескольких примерах они вызывают 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 () где-то вызывается?

1

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

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

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

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

1