I have some Java code that is calling some native code, originally written in Fortran, using JNA. (It's a numerical library, and lots of math people do their coding in Fortran.) It is compiled to a .so
library, see below:
- Fortran: https://github.com/mizzao/libmao/tree/master/src/main/fortran
- Java binding: https://github.com/mizzao/libmao/blob/master/src/main/java/net/andrewmao/probability/MvnPackDirect.java
I was getting great results with everything unit tested in my code, but then I tried using the code from multiple threads, and everything started failing with strange errors. I then looked into some stuff about reentrant Fortran code and realized that the library I was using has the equivalent of some global variables (SAVE
keywords in Fortran, which remember the values of variables when a function is called again: fortran SAVE statement)
For now I am wrapping calls to the library in synchronized
blocks, but this is hobbling performance significantly. It seems to me that it would take significant effort to re-engineer the library to be reentrant (it has a few thousand lines of numerical code, and it's not clear how the values carry over when the subroutines are being run.) Does anyone know the best way to get around the problem? My imagination suggests...
- Is there some way to get each Java thread to load a separate copy of the shared library in memory, so that the global variables are effectively thread-local? Is that even possible? I'm not sure about how JNA's direct binding or library binding works, and if there's a way to use it that way.
- Would it still be screwed even if it were called from different VMs? How can I check to make sure?
- Is there some way to get
gfortran
(gcc
) to compile the Fortran code in a way that is reentrant? - Is there some quick and dirty way to make the Fortran code reentrant? I've searched about the
RECURSIVE
keyword, which apparently keeps variables on the stack, but that doesn't seem to be compatible with the existing code. - Any other possible solutions?
I confirm that things are ok with multiple VMs; this makes sense as they don't share memory. Still a PITA and way more inconvenient than threads though.