15

I am running Linux, and I would like to be able to make parallel function calls into a shared library (.so) which is unfortunately not threadsafe (I am guessing it has global datastructures).

For performance reasons, I do not want to simply wrap the function calls in a mutex.

What I would like to do is to spawn, say 4 threads, and also load 4 copies of the same library into the process memory. Each thread then makes the function calls into its own copy of the library.

Unfortunately, dlopen does not allow me to load more one instance of any library.

Does anyone know of any method which will allow me to load the library more than once? (Other than making 4 copies of the .so file, each with a different name)

mossman
  • 151
  • 1
  • 1
  • 3
  • 1
    For those who need an example, implemented the comparison [here](https://github.com/ikoryakovskiy/not_threadsafe_test). – Ivan Jun 01 '17 at 09:55

4 Answers4

15

You can load multiple independent copies of the library like this:

#define _GNU_SOURCE
#include <dlfcn.h>
...
void *handle = dlmopen(LM_ID_NEWLM, "/path/to/library.so", RTLD_NOW);

More info here.

alanc
  • 4,102
  • 21
  • 24
Employed Russian
  • 199,314
  • 34
  • 295
  • 362
  • Any idea about us poor windows users? This is super useful for linux though. – ibell Feb 07 '15 at 03:43
  • This works in Windows, too, with LoadLibrary and GetProcAddress. I think you need to have multiple physical copies of the DLL, but it works fine. – Jim Hunziker Jun 10 '15 at 18:37
  • Thank you! This lets you do something crazy like wrap a non-thread-safe C library in a C++ class. The new() operator of the class loads the library with LM_ID_NEWLM then uses dlsym() to populate the library's function pointers as fields in the class. Use like a C++ class as you normally would. A bit of an evil hack but it works when you don't want to refactor 10,000 lines of code from someone who belongs in the depths of hell for using static shared state. – AdamIerymenko Sep 01 '15 at 23:16
  • 5
    One limitation that you might miss with `dlmopen()` is that (as of today), the glibc implementation is limited to 16 namespaces per process. Each time you call `dlmopen()` with `LM_ID_NEWLM`, you consume one of those namespaces (until you unload all of the modules in that namespace). – Jason R Nov 24 '16 at 02:40
6

Instead of using threads, you can use multiple processes, each doing some of the work. This is very common on *nix, and is usually easier to code.

  • 2
    I don't know about easier to code. Sure, it's mildly easier to call `fork()` then it is to launch a thread. But if you need to communicate between processes, or share data between processes then you get into the world of IPC, (shared memory, pipes, etc.), which is arguably more complicated to code. – Charles Salvia Nov 17 '09 at 01:34
  • 2
    For starters, it's easier to use his non-thread-safe library. :) I find it easier to code because it's easier to avoid deadlocks and related problems, but, yes, they have their versions in IPC too. –  Nov 17 '09 at 01:49
  • 1
    @Charles - given the OP's objective, going to multiple processes is probably his least painful option. – Michael Kohne Nov 17 '09 at 01:50
  • 3
    @Charles: All depends of the amount of shared data between your threads/processes. The good thing with using processes is that it leads to good program design with a well controled communication system. The apparent simplicity with thread is quite often poor design. – kriss Nov 17 '09 at 01:51
  • Boost.Interprocess helps greatly to share memory... but of course ideally you would like to just give the process a copy of its own and just wait for the result. – Matthieu M. Nov 17 '09 at 13:21
1

Looks like a bad idea. That is no more possible with shared libraries as it would be with static ones.

You could probably use dlopen() with RTLD_LOCAL flag so that subsequent calls to dlopen won't see it's allready loaded and make it work as you want... but it still looks like a bad design idea. If you have performance issues it would be better avoiding to clutter memory with several copies of the same library.

I would suggest either using several processes or going the mutex way, it's probably more efficient.

As you work on Linux there also may exists other approaches if you can access the source code of the library, like renaming it's symbols to have as many separate instances as needed... Well, once you get the source there may be other ways, like making the library thread safe.

kriss
  • 23,497
  • 17
  • 97
  • 116
0

What library is it? Is it something large? I'm wondering if you couldn't fix the library to be threadsafe in some way, then build your code using the threadsafe version of the library. It depends on the size of the library, and what's wrong with it, but if you could fix the library, you'd be able to build your app the way you want, as well as help everyone else out.

Michael Kohne
  • 11,888
  • 3
  • 47
  • 79
  • 1
    Why are you wording it like exposing a non-threadsafe API is something wrong and requiring a fix? There are trade-offs both to providing thread-safety guarantees, and not. – ulidtko Dec 27 '12 at 16:59
  • @ulidtko - the original question is about using a non-threadsafe API in a manner where the lack of thread safety will break things. For the original poster's purposes, the library IS broken. Obviously there's no problem with non-threadsafe APIs, but for the OP, fixes need to be made before the library is useable. It's a context thing. – Michael Kohne Dec 27 '12 at 17:21
  • @ulidtko - Also, we don't know WHY the library isn't threadsafe. There are lots of very good reasons for code to not be threadsafe (for instance, the STL containers aren't threadsafe), but there are also lots of less good reasons (for instance, the old C strtok uses an internal static pointer, which perhaps made sense at the time, but looks pretty stupid from today's point of view). – Michael Kohne Dec 27 '12 at 17:26