Boost logo

Boost :

Subject: [boost] asio and shared libraries problem (linux only)
From: Berserker (berserker_r_at_[hidden])
Date: 2008-09-23 09:00:59


io_service and shared libraries problem (linux only)

Short question: is it safe to share asio "objects" (io_services, sockets and so on...) across shared libraries/modules?

Long story: we are porting our project from Windows to Linux (actually we are working on Ubuntu with GCC 4.2.3) and we discovered a problem when an io_service is shared between modules: creating an io_service, for example, in the main executable and passing that to a shared library causes on Linux problems with handler's invocation.
I stress that on Windows we have never reported the problem (with and without IOCP defining BOOST_ASIO_DISABLE_IOCP), instead on Linux it's always reproducible (event disabling the epoll implementation with BOOST_ASIO_DISABLE_EPOLL).
The following example can reproduce the problem: "test_working_asio_example" let the shared library to create the io_service and in this case the "handle_timeout" and "handle_connection" are invoked as expected, instead "test_not_working_asio_example" freezes in the io_service::run invocation because the timeout is never invoked.

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Shared library code:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// connection.h ////////////////////////////////////////////////////////////////////////////////////////////////////

#include <boost/asio.hpp>
#include <boost/enable_shared_from_this.hpp>

// NetExport on Windows is defined as _declspec(dllexport)/_declspec(dllimport) and on Linux is defined as __attribute__ ((visibility("default")))

class NetExport connection : public boost::enable_shared_from_this<connection>
{
public:
   connection(shared_ptr<boost::asio::io_service> service = shared_ptr<boost::asio::io_service>());
   virtual ~connection();

   boost::asio::io_service & service();

   virtual void handle_connect(const boost::system::error_code &e);
   virtual void handle_timeout();

   void connect(const boost::asio::ip::tcp::endpoint &endpoint, const boost::posix_time::time_duration &timeout);

protected:
   shared_ptr<boost::asio::io_service> m_service;
   boost::asio::deadline_timer m_timeout;
   boost::asio::ip::tcp::socket m_socket;
};

// connection.cpp ////////////////////////////////////////////////////////////////////////////////////////////////////

#include "connection.h"
#include <boost/bind.hpp>

connection::connection(shared_ptr<boost::asio::io_service> service) : m_service(service ? service : shared_ptr<boost::asio::io_service>(new boost::asio::io_service())),
                                    m_timeout(*m_service),
                                    m_socket(*m_service)
{
    std::cout << "connection::ctor" << std::endl;
}

connection::~connection()
{
    std::cout << "connection::dctor" << std::endl;
}

boost::asio::io_service & connection::service()
{
    return m_socket.io_service();
}

void connection::handle_connect(const boost::system::error_code &e)
{
    m_timeout.cancel();

    if(e == boost::asio::error::operation_aborted)
    {
        std::cout << "connection::handle_connect (aborted)" << std::endl;
        return;
    }

    if(e)
        std::cout << "connection::handle_connect (error)" << std::endl;
    else
        std::cout << "connection::handle_connect (success)" << std::endl;
}

void connection::handle_timeout()
{
    std::cout << "connection::handle_timeout" << std::endl;
    m_socket.close();
}

void connection::connect(const boost::asio::ip::tcp::endpoint &endpoint, const boost::posix_time::time_duration &timeout)
{
   m_socket.async_connect(endpoint, boost::bind(&connection::handle_connect, shared_from_this(), boost::asio::placeholders::error));
   m_timeout.expires_from_now(timeout);
   m_timeout.async_wait(boost::bind(&connection::handle_timeout, shared_from_this()));
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Main executable code:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void test_working_asio_example(const boost::asio::ip::tcp::endpoint &endpoint, const boost::posix_time::time_duration &timeout)
{
   try
   {
      shared_ptr<connection> c(new connection());
           c->connect(endpoint, timeout);
      c->service().run();
   }
   catch(std::exception &e)
   {
      std::cerr << "Exception: " << e.what() << std::endl;
   }
}

void test_not_working_asio_example(const boost::asio::ip::tcp::endpoint &endpoint, const boost::posix_time::time_duration &timeout)
{
   try
   {
      shared_ptr<connection> c(new connection(shared_ptr<boost::asio::io_service>(new boost::asio::io_service())));
           c->connect(endpoint, timeout);
      c->service().run();
   }
   catch(std::exception &e)
   {
      std::cerr << "Exception: " << e.what() << std::endl;
   }
}

int main(int argc, char *argv[])
{
   // 88.149.0.2:32123 is just a dummy endpoint that will "probably" timeout in 1 second ( if not try another fake one :) )
   boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address_v4::from_string("88.149.0.2"), 32123);
   boost::posix_time::time_duration timeout = boost::posix_time::seconds(1);
      
   test_working_asio_example(endpoint, timeout);
   test_not_working_asio_example(endpoint, timeout);

   return 0;
}

_________________________________________________________________
Stanco della solita finestra? Personalizza la tua Hotmail!
http://www.messenger.it/personalizza.html#sfondi


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk