Discussion:
[asio-users] shared libraries problem (linux only)
Berserker
2008-09-23 13:01:44 UTC
Permalink
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
Christopher Kohlhoff
2008-09-24 13:28:10 UTC
Permalink
Post by Berserker
Short question: is it safe to share asio "objects" (io_services, sockets
and so on...) across shared libraries/modules?
Yes, it should be.
Post by Berserker
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).
"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.
I can't reproduce any problem using your test code. What command lines
are you using to compile and link the shared library and exectuables.

Cheers,
Chris
Rogerio Pontual
2012-05-15 17:46:05 UTC
Permalink
Post by Christopher Kohlhoff
Post by Berserker
Short question: is it safe to share asio "objects" (io_services, sockets
and so on...) across shared libraries/modules?
Yes, it should be.
Post by Berserker
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).
"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.
I can't reproduce any problem using your test code. What command lines
are you using to compile and link the shared library and exectuables.
Cheers,
Chris
-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
Hi Berserker,

I'm facing a similar problem trying to execute a cross-compiled (for mipsel)
simple example from boost:
http://www.boost.org/doc/libs/1_46_1/doc/html/boost_asio/example/http/client/asy
nc_client.cpp

I've tried using either -DBOOST_ASIO_DISABLE_EPOLL or -
DBOOST_ASIO_DISABLE_EVENTFD, and both. Without -DBOOST_ASIO_DISABLE_EPOLL, I'm
getting a hanging epoll_wait while if EPOLL is disabled, boost is instrumented
with select, and strace shows a _newselect syscall with tv_sec timeout parameter
equal to 300 seconds.

Could you overcome the io_service::run freeze? I'd appreciate a lot to hear how
this issue has unfolded for you.

Best Regards,
Roger
Gruenke, Matt
2012-05-15 18:27:33 UTC
Permalink
I didn't see the original message in this thread, but I wanted to point
out that any code relying on RTTI, such as dynamic_cast<> or exception
handling, can malfunction when applied to objects created on the other
side of the boundary between a runtime-loaded shared library (i.e.
loaded using dlopen()) and the program that loaded it (or other
runtime-loaded shared libraries).

Even if Boost.Asio doesn't use such features directly, any other
libraries it uses or that are used in conjunction with it, that rely on
these features, could cause apparent malfunctions in Boost.Asio.

So, if you have some sort of "plug-in" mechanism in your program, you're
advised to think hard about the data types passed across that interface.
Virtual functions are fine (assuming compatible versions of the class),
but don't expect to handle any exceptions thrown by the other side.


Matt


-----Original Message-----
From: Rogerio Pontual
Sent: Tuesday, May 15, 2012 13:46
To: asio-***@lists.sourceforge.net
Subject: Re: [asio-users] shared libraries problem (linux only)
Post by Christopher Kohlhoff
Post by Berserker
Short question: is it safe to share asio "objects" (io_services,
sockets and so on...) across shared libraries/modules?
Yes, it should be.
Post by Berserker
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).
"test_working_asio_example" let the shared library to create the
io_service and in this case the "handle_timeout" and
"handle_connection"
Post by Christopher Kohlhoff
Post by Berserker
are invoked as expected, instead "test_not_working_asio_example"
freezes in the io_service::run invocation because the timeout is
never invoked.
Post by Christopher Kohlhoff
I can't reproduce any problem using your test code. What command lines
are you using to compile and link the shared library and exectuables.
Cheers,
Chris
----------------------------------------------------------------------
--- This SF.Net email is sponsored by the Moblin Your Move Developer's
challenge Build the coolest Linux based applications with Moblin SDK &
win great prizes Grand prize is a trip for two to an Open Source event
anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
Hi Berserker,

I'm facing a similar problem trying to execute a cross-compiled (for
mipsel) simple example from boost:
http://www.boost.org/doc/libs/1_46_1/doc/html/boost_asio/example/http/cl
ient/asy
nc_client.cpp

I've tried using either -DBOOST_ASIO_DISABLE_EPOLL or -
DBOOST_ASIO_DISABLE_EVENTFD, and both. Without
-DBOOST_ASIO_DISABLE_EPOLL, I'm getting a hanging epoll_wait while if
EPOLL is disabled, boost is instrumented with select, and strace shows a
_newselect syscall with tv_sec timeout parameter equal to 300 seconds.

Could you overcome the io_service::run freeze? I'd appreciate a lot to
hear how this issue has unfolded for you.

Best Regards,
Roger

Berserker
2008-09-25 10:22:21 UTC
Permalink
Thank for the reply Chris, I just discovered the problem: try adding -fvisibility=hidden to the command line and decorate the connection class with __attribute__ ((visibility("default"))), this will reproduce the problem.
I have uploaded a codeblocks project (with precompiled executables) as example: http://www.megaupload.com/?d=7IJSWXAP
You can switch to a working version simply removing on both the projects "-fvisibility=hidden" under "build options..." -> "other options" and commenting out the "NetExport" decoration of the connection class.
Now it's "fixed" but the question is: any idea about that?

_________________________________________________________________
Cerchi un locale per la serata? Chiedilo a Live Maps!
http://maps.live.it/
Christopher Kohlhoff
2008-09-25 23:49:23 UTC
Permalink
Post by Berserker
Thank for the reply Chris, I just discovered the problem: try adding
-fvisibility=hidden to the command line and decorate the connection
class with __attribute__ ((visibility("default"))), this will reproduce
the problem.
Ok, yes I can reproduce it now.
Post by Berserker
I have uploaded a codeblocks project (with precompiled executables) as
example: http://www.megaupload.com/?d=7IJSWXAP
You can switch to a working version simply removing on both the projects
"-fvisibility=hidden" under "build options..." -> "other options" and
commenting out the "NetExport" decoration of the connection class.
Now it's "fixed" but the question is: any idea about that?
The visibility=hidden option means that your shared library and
executable will each have a separate copy of the asio code at runtime,
and so that may cause some problems (particular if there is some static
data involved).

I don't think I've ever built a shared library with that option before.
In my opinion it's not a good option to use if you're writing C++, since
classes and templates tend to create lots of non-obvious linkages.

Cheers,
Chris
Berserker
2008-09-26 08:28:04 UTC
Permalink
Post by Christopher Kohlhoff
The visibility=hidden option means that your shared library and
executable will each have a separate copy of the asio code at runtime,
and so that may cause some problems (particular if there is some static
data involved).
I don't think I've ever built a shared library with that option before.
In my opinion it's not a good option to use if you're writing C++, since
classes and templates tend to create lots of non-obvious linkages.
According to this link http://gcc.gnu.org/wiki/Visibility ("Why is the
new C++ visibility support so useful?"), it says:

# It very substantially improves load times of your DSO (Dynamic Shared
Object). For example, a huge C++ template-based library which was tested
(the TnFOX Boost.Python bindings library) now loads in eight seconds
rather than over six minutes!

# It lets the optimiser produce better code. PLT indirections (when a
function call or variable access must be looked up via the Global Offset
Table such as in PIC code) can be completely avoided, thus substantially
avoiding pipeline stalls on modern processors and thus much faster code.
Furthermore when most of the symbols are bound locally, they can be
safely elided (removed) completely through the entire DSO. This gives
greater latitude especially to the inliner which no longer needs to keep
an entry point around "just in case".

# It reduces the size of your DSO by 5-20%. ELF's exported symbol table
format is quite a space hog, giving the complete mangled symbol name
which with heavy template usage can average around 1000 bytes. C++
templates spew out a huge amount of symbols and a typical C++ library
can easily surpass 30,000 symbols which is around 5-6Mb! Therefore if
you cut out the 60-80% of unnecessary symbols, your DSO can be megabytes
smaller!

# Much lower chance of symbol collision. The old woe of two libraries
internally using the same symbol for different things is finally behind
us with this patch. Hallelujah!

It's not so bad :)
Berserker
2008-09-30 10:26:34 UTC
Permalink
Post by Berserker
It's not so bad :)
Any feedback?
Simon Perreault
2008-09-26 05:30:50 UTC
Permalink
Post by Christopher Kohlhoff
The visibility=hidden option means that your shared library and
executable will each have a separate copy of the asio code at runtime,
and so that may cause some problems (particular if there is some static
data involved).
Note that it should not. It's just that asio needs some additional
declarations to make it work with GCC visibility.
Post by Christopher Kohlhoff
I don't think I've ever built a shared library with that option before.
In my opinion it's not a good option to use if you're writing C++, since
classes and templates tend to create lots of non-obvious linkages.
It is a very good idea to enable GCC visibility with C++ because
templates generate a lot of useless symbols which can be responsible for
significant bloat.

Instead it should be understood that asio currently doesn't support GCC
visibility.
--
Please try Numb, a STUN/TURN server implementation.
Free access at http://numb.viagenie.ca/.
Loading...