boost::system::error_code::message() throwing access violation exception with boost::asio socket

4.9k Views Asked by At

I am implementing a client application that has to make a small number of socket connections to hardware devices. I have broken down the problem to the following small code subset

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!!!!

}

I am using Embarcadero RAD studio XE4 C++ Builder and when I run the above code in the main VCL thread it works fine. When I run it with multiple connections, I have the above code running in multiple instances of the TThread class and that is when I get into problems with an access violation - it appears that when the error_code is modified by the connect call, the internal member m_cat of the error_code instance becomes NULL and so when I call message() I get the access violation. This happens even when I just have a single background thread running.

Is it possible that my code above is simply not thread safe in the way I need to use it? I have tried to find out why this code won't run in a background thread, but cannot find anything about it.

The boost version I am running is 1.50 as this is the integrated version that is used for building 64 bit applications in RAD studio.

Has anyone else encountered this issue in a multithreaded setting (in Embarcadero or otherwise) and if so how did you resolve it? Or is this class simply not safe to use in a multithreaded way?

3

There are 3 best solutions below

1
On

In multiple examples, they call io_service->run() in a thread: http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/example/cpp11/futures/daytime_client.cpp

I liked this one, with a ThreadPool: Thread pool using boost asio

Are you sure io_service->run() is called somewhere?

0
On

You must always be sure that iterator != boost::asio::ip::tcp::resolver::iterator().

From boost docs:

A default constructed iterator represents the end of the list.

I bet it's the problem, connect() just breaks the stack due to invalid endpoint iterator.

1
On

This is kind of a long shot, but it might be worth a try:

The system::error_code is made up of two entries: An error value and a category. The error value is basically just an int, but the category is a Singleton. This is required because error categories are compared for equality based on pointer identities (ie. two categories are equal if and only if they point to the same category object).

The problem is that initialization of the category Singleton might not be thread-safe. Asio uses the system_category, which is implemented in boost/libs/system/src/error_code.cpp. For 1.50 the implementation looks like this:

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

This is guaranteed to be thread-safe on C++11 conforming compilers, but if your compiler does not implement thread-safe initialization of function-scope statics, this might break. You can easily verify this by tracing calls to this function and see if you observe a potential race.