Search code examples
boostboost-asio

boost asio io_service multiple objects from multiple source files/threads - crash in wake_one_idle_thread_and_unlock


Seeing an unexplained crash in an application which is already using boost asio for udp socket (in some part of the program) - after adding some new class to use boost asio deadline timers in some other part of program.

Even before the new class is instantiated - the program crashes in the existing udp socket instantiation - when io_service constructor is called from that app class.

If I disable either (either disable udp socket functionality) or disable the new functionality with deadline timers in completely unrelated part of program in new class - it works OK.

But both can't seem to be able to co-exist

Any known gotchas around multiple instantiation of io_service objects from different threads or more likely my usage of io_service being incorrect? Though it's very wierd for a seg fault to be triggered when the new class with io_service/timers has not even been constructed yet.

Here is the log for the crash:

#0  wake_one_idle_thread_and_unlock (lock=..., this=0x214ea20) at /x//boost/asio/detail/impl/task_io_service.ipp:461
#1  boost::asio::detail::task_io_service::wake_one_thread_and_unlock (this=0x214ea20, lock=...) at /x//boost/asio/detail/impl/task_io_service.ipp:472
#2  0x00007fff6bc16c1a in init_task (this=0x214ea20) at /x///boost/asio/detail/impl/task_io_service.ipp:118
#3  init_task (this=<optimized out>) at /x///boost/asio/detail/impl/epoll_reactor.ipp:145
#4  reactive_socket_service_base (io_service=..., this=0x214e648) at /x//vendors/boost/1.48.0/linux-x86_64/Release/common/include/boost/asio/detail/impl/reactive_socket_service_base.ipp:34
#5  reactive_socket_service (io_service=..., this=0x214e648) at /x///boost/asio/detail/reactive_socket_service.hpp:77
#6  datagram_socket_service (io_service=..., this=0x214e620) at /x//boost/asio/datagram_socket_service.hpp:89
#7  boost::asio::detail::service_registry::create<boost::asio::datagram_socket_service<boost::asio::ip::udp> > (owner=...) at /x///boost/asio/detail/impl/service_registry.hpp:81
#8  0x00007fff6bc12a32 in do_use_service (factory=0x7fff6bc16a20 <boost::asio::detail::service_registry::create<boost::asio::datagram_socket_service<boost::asio::ip::udp> >(boost::asio::io_service&)>, key=<synthetic pointer>, this=0x214f000)
    at /x///boost/asio/detail/impl/service_registry.ipp:123
#9  use_service<boost::asio::datagram_socket_service<boost::asio::ip::udp> > (this=0x214f000) at /x///boost/asio/detail/impl/service_registry.hpp:48
#10 use_service<boost::asio::datagram_socket_service<boost::asio::ip::udp> > (ios=...) at /x///boost/asio/impl/io_service.hpp:33
#11 basic_io_object (io_service=..., this=0x214e960) at /x///boost/asio/basic_io_object.hpp:183
#12 basic_socket (io_service=..., this=0x214e960) at /x///boost/asio/basic_socket.hpp:69
#13 basic_datagram_socket (io_service=..., this=0x214e960) at /x///boost/asio/basic_datagram_socket.hpp:69
#14 (this=this@entry=0x214e930, __in_chrg=<optimized out>, __vtt_parm=<optimized out>) at /app.cpp:18

Solution

  • Even before the new class is instantiated - the program crashes in the existing udp socket instantiation - when io_service constructor is called from that app class.

    This gives me an idea. Really what you say is there cannot be runtime interference.

    This means we should look at link-time interference.

    This can only happen if ODR violations are at play¹.

    If the "parts that use ASIO for UDP" (let's call it the "UDP part") are compiled using a different version of the headers, a different compiler and/or different compilation flags, you are liable to get what's known as ODR violations: violations of the One Definition Rule.

    At link time, the compiler is free to elide copies of identical symbols originating from separate translation units. However, if they were actually NOT identical (because of ODR violation), the "other" copy being used causes Undefined Behaviour which could manifest as crashes².

    So, unless you can control how the "UDP part" is compiled, make sure you match how it was built, OR don't link in a way that clashes. I believe shared linking with strictly managed visibility lets you hide the Boost symbols that are involved in the implementation of either part of the library. Of course, this assumes that those Boost types aren't part of the public interface.


    ¹ (because Asio is header-only, so there can't really be conflicting versions of shared libraries, although if you dynamically link Boost System, check that it matches the version, build toolchain and build flags that was used for the parts that use asio for UDP).

    ² if you're lucky, actually: anything could happen, including seemingly working fine but corrupting data, dating your daughter or selling your goldfish to a Moroccan tooth fairy