5
votes

I am building a shared library which uses Boost.thread internally. As a result, Boost.system is also used since Boost.thread depends on that. My shared library exports a C interface, so I want to hide all my internal exception handling and thread usage etc from the end user. It is supposed to be a black box so to speak. However, when I link with a client application, while the program runs fine - as soon as it is time to stop the processing by invoking a library function I get:

terminate called after throwing an instance of 'boost::thread_interrupted'

I catch this exception internally in the library, so I have no idea why it is not actually being caught. The end user's program is not meant to know about or handle Boost exceptions in any way. When building the shared library, I use static linking for both Boost.thread and Boost.system so the outside world is never meant to see them. I am on GCC 4.7 on Ubuntu 12. On Windows, I have no such problems (neither with MSVC or MinGw).

(EDIT)

I am editing the question to show a minimalistic example that reproduces the problem, as per the requests in the comments.

Here first is the code for testlib.cpp and testlib.h.

testlib.cpp:

#include <boost/thread/thread.hpp>

void thread_func()
{
while(1)
{
boost::this_thread::interruption_point();
}
}

void do_processing()
{
// Start a thread that will execute the function above.
boost::thread worker(thread_func);

// We assume the thread started properly for the purposes of this example.

// Now let's interrupt the thread.
worker.interrupt();

// And now let's wait for it to finish.
worker.join();
}

And now testlib.h:

#ifndef TESTLIB_H
#define TESTLIB_H

void do_processing();

#endif

I build this into a shared library with the following command:

g++ -static-libgcc -static -s -DNDEBUG -I /usr/boost_1_54_0 -L /usr/boost_1_54_0/stage/lib -Wall -shared -fPIC -o libtestlib.so testlib.cpp -lboost_thread -lboost_system -lpthread -O3

Then, I have the code for a trivial client program which looks as follows:

#include "testlib.h"
#include <cstdio>

int main()
{
do_processing();
printf("Execution completed properly.\n");
return 0;
}

I build the client as follows:

g++ -DNDEBUG -I /usr/boost_1_54_0 -L ./ -Wall -o client client.cpp -ltestlib -O3

When I run the client, I get:

terminate called after throwing an instance of 'boost::thread_interrupted'
Aborted (core dumped)

I am not explicitly catching the thread interruption exception, but according to the Boost documentation Boost.thread does that and terminates the given thread only. I tried explicitly catching the exception from within the thread_func function, but that made no difference.

(End OF EDIT)

(EDIT 2)

It is worth noting that even with -fexceptions turned on, the problem still persists. Also, I tried to throw and catch an exception that is defined in the same translation unit as the code that catches and throws it, with no improvement. In short, all exceptions appear to remain uncaught in the shared library even though I definitely have catch handlers for them. When I compile the client file and the testlib file as part of a single program, that is to say without making testlib into a shared library, everything works as expected.

(End OF EDIT 2)

Any tips?

3
I'd suggest making a minimal test that demonstrates the issue (think 5-line Makefile). Put it on github/gist. Chances are, you'll find a cause/workaround in the process. If not, you'll actually have something to show us.sehe
@sehe comments like this should be upvoted infinitely, especially for the 'Chances are, you'll find a cause/workaround in the process'. Countless are the times I started a question on SO just to find it solving itself while trying to get a minimal piece of reproduction code.stijn
@stijn I know. To be honest, I only said it, because we literally have no other option, besides wild guesswork, as it is now. I'm really willing to help look into this, but there's nothing to go on.sehe
I just edited the question with a minimalistic example as requested. I'm having trouble figuring out how to mark certain sections as code though, so it looks a bit odd. It seems to treat my # sign as a heading, like in markdown.Philip Bennefall
Edited it again to try to improve the code sections.Philip Bennefall

3 Answers

1
votes

I finally figured it out. The -static flag should never be specified when -shared is specified. My belief was that it merely told the linker to prefer static versions of libraries that it links, but instead it makes the generated dynamic library unsuitable for dynamic linking which is a bit ironic. But there it is. Removing -static solved all my problems, and I am able to link Boost statically just fine inside my dynamic library which handles exceptions perfectly.

0
votes

Maybe this?

If you have a library L which throws E, then both L and the application A MUST be linked against X, the library containing the definition of E.

Try to link executable against boost, too.

0
votes

A shared library that itself includes statically linked libraries is not such a good idea, and I don't think that this scenario is well supported in the GNU toolchain.

I think that your particular problem arises from the option -static-libgcc, but I've been unable to compile it in my machine with your options. Not that linking statically-dinamically to libpthread.so sounds as such a good idea either... What will happen if the main executable wants to create its own threads? Will it be compiled with -pthread? If it is, then you will link twice the thread functions; if it isn't, it will have the functions but not the precompiler macros nor the thread-safe library functions.

My advice is simply not to compile your library statically, that's just not the Linux way.

Actually that should not be a real problem, even if you don't want to rely on the distribution version of boost: compile your program against the shared boost libraries and deploy all these files (libboost_thread.so.1.54.0, libboost_system.so.1.54.0 and libtestlib.so) to the same directory. Then run your program with LD_LIBRARY_PATH=<path-to-so-files>. Since the client is not intended to use boost directly, it doesn't need the boost headers, nor link them in the compiler command. You still have your black box, but now it is formed by 3 *so files, instead of just 1.