Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create multiple instances of the same library with JNA?

I have a problem with Java Native Access: I have a C-library with one function, let' s say foo(). This function has a memory - a counter - with increases with every call. Is it possible to create two instances of this library within the same java process so that the counters are independent?

Thank you very much.

Here is some code:

public class A
{

    public static class Lib
    {
        NativeLibrary libInstance = NativeLibrary.getInstance("myLibrary");
        Function fn = lib.getFunction("foo");
    }

    private Lib lib = new Lib();

    public foo()
    {
        lib.fn.invoke(new Object[] {});
    }
}

If I call:

A a = new A();
A b = new A();

a.foo(); // >1
a.foo(); // >2
b.foo(); // >3
a.foo(); // >4
b.foo(); // >5
a.foo(); // >6

but I want a and b to work independent with the library:

a.foo(); // >1
a.foo(); // >2
b.foo(); // >1
a.foo(); // >3
b.foo(); // >2
a.foo(); // >4

Many thanks

This is how I try to create an instance of a lib:

public class DriverLib 
{
    private static int counter = 1;

    NativeLibrary lib;

    Function stepAction;
    Function initialize;
    Function terminate;

    Pointer input;
    Pointer output;

    public DriverLib()
    {
        // create options 
        HashMap<String, Integer> options = new HashMap<>();
        options.put(Library.OPTION_OPEN_FLAGS, new Integer(counter++));

        lib = NativeLibrary.getInstance("mylib_win64", options);

        stepAction = lib.getFunction("step");
        initialize = lib.getFunction("initialize");
        terminate  = lib.getFunction("terminate");

        input  = lib.getGlobalVariableAddress("model_U");
        output = lib.getGlobalVariableAddress("model_Y");
    }
}
like image 799
Jens Avatar asked Oct 09 '15 14:10

Jens


1 Answers

The easiest way to make this happen is to simply make a copy of your shared library with a different name.

The default behavior for loading a shared library is that no matter how many times you load it, you effectively get the same instance.

Depending on the underlying OS, you can provide options on open which indicate that you want a completely independent copy, or a copy with shared code but independent data. See documentation for LoadLibrary() (windows) and dlopen() (everything else). You can pass these options to the OS via the option Library.OPTION_OPEN_FLAGS passed to Native.loadLibrary().

JNA can support loading shared libraries with any number of additional options, and on the Java side it'll maintain the same library loaded with different options as two independent libraries. However, it'll generally treat two loads with the same options as the same logical shared library (NativeLibrary represents any given load of a shared library, and is cached according to library name and options). So you could fake it out and load the exact same library twice by providing a library option which is effectively ignored (a dummy type mapper, for instance).

Note that even if you fake out JNA, you have to ensure that the flags you pass to the underlying system (via Library.OPTION_OPEN_FLAGS) ensure that the OS does what you want it to. Otherwise the OS itself will just give you back the same library instance and there's nothing JNA can do about it.

EDIT

Figure out what flags you need to pass to the OS to ensure that it gives you a unique handle each time you call dlopen/LoadLibrary (or at least a handle which provides independent data segments). The flag you're looking for on Linux is likely RTLD_PRIVATE. That's the flag to pass in Library.OPTION_OPEN_FLAGS. After that, pass in a dummy option to Native.loadLibrary()'s option map; JNA should ignore anything it doesn't recognize, but a unique option will force JNA to cache each library load separately.

++idx;
int flags = ...; // Must be flags legal to pass to dlopen/LoadLibraryEx
Map options = new HashMap() {
    { 
        put(Library.OPTION_OPEN_FLAGS, flags); 
        put("ignored-option", idx);
    }
}
lib[idx] = Native.loadLibrary("my-library", options);

See if your system supports the RTLD_PRIVATE flag. It's not clear you can load the same library with an independent data segment on OSX or windows without a separate copy of the shared library. Under linux, there also exists dlmopen, but JNA does not make use of it.

If you bundle the shared library with JNA, you can ask JNA to unpack it for you (Native.extractFromResourcePath()), multiple times if necessary (which gives you multiple copies of the shared library to load).

like image 84
technomage Avatar answered Oct 14 '22 18:10

technomage