3
$\begingroup$

I need to initialize multiple instances of the same library that has global variables. The naive way of LibraryFunctionLoading functions multiple times doesn't work since the global variables are shared across all loaded functions. How do I confine the global variables to each instance?

Consider the minimal example:

SetDirectory[NotebookDirectory[]];
CreateLibrary[{"addUnit.cc"}, "addUnit", "TargetDirectory" -> NotebookDirectory[]];

where the source file addUnit.cc is:

//addUnit.cc
#include "WolframLibrary.h"

DLLEXPORT mint WolframLibrary_getVersion(){
    return WolframLibraryVersion;}


DLLEXPORT int WolframLibrary_initialize(WolframLibraryData libData){
    return 0;}

//global variable part of another package (through extern "C")
mint counter = 0;

//increments global variable 'counter' by one
EXTERN_C DLLEXPORT int addOne(WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res){
    counter++;
    return LIBRARY_NO_ERROR;
}

//returns the current value of the global variable 'counter'
EXTERN_C DLLEXPORT int showNumber(WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res){
    MArgument_setInteger(Res, counter);
    return LIBRARY_NO_ERROR;
}

This library has a single global variable initialized to counter=0, which upon calling addOne() increments it. The function showNumber() returns the current value. These functions may be called from Mathematica using:

addOneA = LibraryFunctionLoad["addUnit.dylib", "addOne", {}, "Void"];
showNumberA = LibraryFunctionLoad["addUnit.dylib", "showNumber", {}, Integer];

Calling addOneA[] from Mathematica increments the counter, and showNumberA[] returns the value.

addOne[]; addOne[];
showNumberA[]
(*2*)

However, I need to initialize a second (and third or even fourth) instance of the library in the same session but with all its global variables confined to these instances.

Unfortunately, loading the functions from these doesn't actually give a separate instance of this library:

addOneB = LibraryFunctionLoad["addUnit.dylib", "addOne", {}, "Void"];
showNumberB = LibraryFunctionLoad["addUnit.dylib", "showNumber", {}, Integer];

for

showNumberB[]
(*2*)

is returning the value of the counter in the first instance.

Question How do I load multiple instances of the same library via LibraryLink while keeping the global variables of the library local to each instance?

$\endgroup$
4
$\begingroup$

Trying to load the same library multiple times is the wrong approach. You should look into managed library expressions instead. This is exactly the kind of use they are designed for.

To see what they look like form the Mathematica side, look at e.g. TriangleLink. Look at the workflow in the first example. Firs you create a TriangleExpression using TriangleCreate. This is a reference to a C-side data structure that encapsulates all your data. These are what you would use instead of global variables, and what your functions would operate on (like TriangleTriangulate). You can have any number of these data structures. They can be deleted manually using TriangleDelete from Mathematica, but they will be deleted automatically when there are no references to them anymore (e.g. the variable got Cleared).

Setting up a library with managed library expressions is very easy, but it is also quite laborious. My LTemplate package was designed precisely for making this task much simpler. I won't repeat the usage examples here, as they would be identical to what's in the other QA. I'll just note that you would use member variables instead of global variables.

It does require the use of C++.


Example

Here'a an example of doing this with LTemplate.

We will be working in a temporary directory, created by CreateDirectory. LTemplate always looks for the code in the current directory, so that's where we will export it.

Needs["LTemplate`"]
SetDirectory@CreateDirectory[];

The template mirrors the interface of your class:

template =
  LClass["AddUnit",
   {
    LFun["addOne", {}, "Void"],
    LFun["show", {}, Integer]
    }
   ];

To keep things simple, I'll just write the code in a Mathematica string and export it. During normal usage, you would write this into a separate .h file using a programmer's editor.

code = "
  class AddUnit {
    mint counter = 0;
  public:
    void addOne() { counter++; }
    mint show() const { return counter; }
  };
  ";
Export["AddUnit.h", code, "String"];

Compile and load the library:

CompileTemplate[template]

LoadTemplate[template]

Make two instances of the class. These will have two separate counters.

au1 = Make[AddUnit]
(* AddUnit[1] *)

au2 = Make[AddUnit]
(* AddUnit[2] *)

Show the value of au1:

au1@"show"[]
(* 0 *)

Add one to it, then show it again:

au1@"addOne"[]

au1@"show"[]
(* 1 *)

This modified only au1. The other instance, au2, stays unchanged.

au2@"show"[]
(* 0 *)
$\endgroup$
0
$\begingroup$

Based on my research, I found that this is less of a Mathematica issue and more of an OS issue.

See e.g. the following Stack Overflow questions:

https://stackoverflow.com/questions/39586237/loading-multiple-copies-of-a-shared-library

https://stackoverflow.com/questions/3433522/loading-two-instances-of-a-shared-library

https://stackoverflow.com/questions/33040652/how-to-create-multiple-instances-of-the-same-library-with-jna

I'd say it's too much trouble to make it work; and is therefore not possible.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.