8

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");
    }
}
Jens
  • 81
  • 5

1 Answers1

5

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).

Community
  • 1
  • 1
technomage
  • 9,861
  • 2
  • 26
  • 40
  • 1
    Thanks for your reply. The problem is, that I need a dynamic number of instances. Thus, the copy option is no option. The other solution is no really clear for me. Ich set the OPTION_OPEN_FLAGS option in a HasMap. The only possible values are -1, 0 and 1 (all others throw Exceptions). Anyway this results no different instances of the library. Also I set some dummy keys with different values. Also in this case I only get the same problem. – Jens Oct 12 '15 at 08:13
  • Please include the actual exceptions thrown when trying to use different `OPTION_OPEN_FLAGS` values, as well as the specific method used to pass the flags. That parameter should accept any integer value (-1 is treated as "default"). – technomage Oct 13 '15 at 13:52
  • Seems to be an error given by the OS: Exception in thread "main" java.lang.UnsatisfiedLinkError: Falscher Parameter. Which means "wrong parameter". I add the code to the question above. – Jens Oct 15 '15 at 06:40
  • The second call throws the error. So the open flag is 2. Thank you for your help. – Jens Oct 15 '15 at 06:47
  • I can't find this `RTLD_PRIVATE` flag defined anywhere on Linux. Has it been renamed or removed? – john16384 Jun 12 '20 at 22:50
  • `RTLD_LOCAL` should do nearly the same thing and it's the default. – technomage Jun 15 '20 at 14:54