Boost :: asio :: io_service fails on win_mutex lock

I had a problem with boost :: asio where timer and / or sockets were created using a global failure of the io_service instance during build. The system that is crashing looks like this:

  • Windows 7

  • Visual Studio 2013 Express for Windows Desktop v 12.0.31101.00 Update 4

  • Boost 1.57, dynamically linked, compiled using multithreading, e.g. boost_thread-vc120-t-GD-1_57.dll

I was able to replicate the problem in the following simplified code:

// global_io_service.h file

#ifndef INCLUDED_GLOBAL_IO_SERVICE_H
#define INCLUDED_GLOBAL_IO_SERVICE_H

#include <boost/asio/io_service.hpp>

#include <iostream>
#include <string>

namespace foo{

extern boost::asio::io_service test_io_service;

class foo_base_io_service{ 

public:

    foo_base_io_service(const std::string& name)
      : d_who_am_i(name)
    {
        std::cout << "constructing copy " << ++foo_base_io_service::num_instances << "my name is " << d_who_am_i << std::endl;
    }

    boost::asio::io_service& get_ref()
    {
        std::cout << "class requested copy of " << d_who_am_i << std::endl;
        return d_ios;
    }

    ~foo_base_io_service()
    {
        std::cout << "Someone 86'd the base_io_service..." << std::endl;
    }

private:

    // this class is not copyable
    foo_base_io_service(const foo_base_io_service&);
    foo_base_io_service& operator=(const foo_base_io_service&);

    std::string d_who_am_i;
    static int num_instances;

    boost::asio::io_service d_ios;
};

extern foo_base_io_service global_timer_io_service;

} // namespace foo

#endif

      

// File global_io_service.cpp

#include "global_io_service.h"

namespace foo{
    boost::asio::io_service test_io_service;

    foo_base_io_service global_timer_io_service("FOO_TIMER_SERVICE");

    // static initialization
    int foo_base_io_service::num_instances = 0;
}

      

// FILE main.cpp

#include <WinSock2.h>
#include "global_io_service.h"
#include <boost/asio/deadline_timer.hpp>

int main(int argc, char *argv[])
{
    // also causes crash
    boost::asio::deadline_timer crash_timer2(foo::test_io_service);    

    // causes crash
    boost::asio::deadline_timer crash_timer(foo::global_timer_io_service.get_ref());


    return 0 ;
}

      

Here's the downside of the failure:

test_io_service.exe! boost :: asio :: detail :: win_mutex :: lock () Line 51

test_io_service.exe! boost :: asio :: detail :: scoped_lock :: scoped_lock (boost :: asio :: detail :: win_mutex and m) Line 47

test_io_service.exe! boost :: asio :: detail :: win_iocp_io_service :: do_add_timer_queue (boost :: asio :: detail :: timer_queue_base and queue) Line 477

test_io_service.exe! boost :: asio :: detail :: win_iocp_io_service :: add_timer_queue> (boost :: asio :: detail :: timer_queue> and queue) Line 79

test_io_service.exe! boost :: asio :: detail :: deadline_timer_service> :: deadline_timer_service> (boost :: asio :: io_service and io_service) Line 69

test_io_service.exe! boost :: asio :: deadline_timer_service> :: deadline_timer_service> (boost :: asio :: io_service and io_service) Line 78

test_io_service.exe! boost :: asio :: detail :: service_registry :: create -> (boost :: asio :: io_service and owner) Line 81

test_io_service.exe! boost :: asio :: detail :: service_registry :: do_use_service (const boost :: asio :: io_service :: service :: key and key, boost :: asio :: io_service :: service * (boost :: asio :: io_service &) * factory) Line 123

test_io_service.exe! boost :: asio :: detail :: service_registry :: use_service -> () Line 49

test_io_service.exe! boost :: asio :: use_service -> (boost :: asio :: io_service and ios) Line 34

test_io_service.exe! boost :: asio :: basic_io_object>, 0> :: basic_io_object>, 0> (boost :: asio :: io_service and io_service) Line 91

test_io_service.exe! boost :: asio :: basic_deadline_timer, boost :: asio :: deadline_timer_service -> :: basic_deadline_timer, boost :: asio :: deadline_timer_service -> (boost :: asio :: io_service and io_service) Line 151

test_io_service.exe! main (int argc, char * * argv) Line 16 C ++

Here's what I learned:

  • The problem does not occur on Ubuntu 14.04, Ubuntu 14.10, or Red Hat 6.5 up 1.54.
  • The problem is related to the order in which Winsock2 is enabled. For example, replacing the include order with global_io_service.h prevents a crash.
  • The problem is with the external link global_timer_io_service. Moving the definition of global_timer_io_service to main.cpp prevents a crash.
  • I have found reports of similar crashes occurring in internal critical sections of the io_service. These problems were mainly due to the fact that io_service objects are passed to the timer / socket constructors. In my case, I think that the io_service I am using has already been constructed before entering main.
  • My gut says there is a race condition (maybe some kind of global state setting in WinSock2?) That prevents the io_service object from being constructed correctly.

Hope I'm having a bad day and undefined behavior is being called. Otherwise, I would like to understand why this is happening? Thanks in advance.

+3


source to share


2 answers


The problem is that ASIO chooses its io_service implementation on Windows by definition BOOST_ASIO_HAS_IOCP

boost/asio/detail/config.hpp

. If defined, it will use win_iocp_io_service

. If not, it will use task_io_service

- see boost/asio/io_service.hpp

. If this choice differs from translation units, you end up initializing the io_service as a whole and using it as a different one. They differ in subtle ways, for example. which mutexes are being initialized, so this problem can manifest itself as a crash due to the use of an uninitialized mutex.

As for the choice BOOST_ASIO_HAS_IOCP

, consider config.hpp

:

#if !defined(BOOST_ASIO_HAS_IOCP)
# if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__)
#  if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400)
#   if !defined(UNDER_CE)
#    if !defined(BOOST_ASIO_DISABLE_IOCP)
#     define BOOST_ASIO_HAS_IOCP 1
#    endif // !defined(BOOST_ASIO_DISABLE_IOCP)
#   endif // !defined(UNDER_CE)
#  endif // defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400)
# endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__)
#endif // !defined(BOOST_ASIO_HAS_IOCP)

      



In this case, a controversial macro _WIN32_WINNT

that appears to be defined WinSock2.h

in your project. Since it is defined in main.cpp

but not defined in global_io_service.cpp

, you initialize io_service to use task_io_service

and call it as if it were usingwin_iocp_io_service

To fix the problem, either specify _WIN32_WINNT

in your compiler definitions, or in the global header file, or simply turn off the IOCP reactor completely by specifying BOOST_ASIO_DISABLE_IOCP

(again globally).

+4


source


The problem is the yorervice lifetime. You pull it out of the object.

Iosservice should live longer than all services.



In this example http://www.boost.org/doc/libs/1_58_0/doc/html/boost_asio/tutorial/tuttimer2.html Ioservice lives longer than a timer.

Edit: Here is an online online book from boris schรคling http://dieboostcppbibliotheken.de/boost.asio-ioservices-und-objekte

0


source







All Articles