I try to write reentrant DLL based on example pi_controller project. I need to use one dll several times in a one simulation model simultaneously. I introduced struct “Converter_struct” and try to put there all variables. I used malloc to allocate memory for the struct. The struct pointer is assinged to aState->userData. After lauching simulation, it does not work. Structure variables are 0 or has random values.
A reentrant DLL must not use global variables. You are using the global variable C.
If your model contains two instances of your DLL, i1 and i2, the DLL is only loaded once. Therefore, there is only one instance of C.
You allocate memory to a Converter_struct for both instances i1 and i2 in plecsStart(). Now, as soon as the simulation starts, plecsStart() will be called for both instances i1 and i2 - but you cannot make any assumption for which instance it is called first. In any case, C will only point to the memory allocated in the last call to plecsStart().
During the simulation, plecsOutput() is called for both instances i1 and i2. Again, you can make no assumptions about which instance is called when. Since you use the global variable C directly, you will always work with the memory allocated in the last call to plecsStart(), regardless whether plecsOutput() is called for i1 or i2.
The solution is simple: Get rid of the global variable C. Instead, use a local variable declaration in both plecsStart() and plecsOutput(). In plecsOutput(), assign your local variable to the value of aState->userData, which you already used in plecsStart() to store a pointer to the memory that you allocated for C.
With the local variable approach, how do we keep the state data? In above case the integrator is persistent data.
Do we assign it to aState->states[0] in plecsOutput function call?
Are states the only allowed way to store persistent data?
Are global variables not an option in the main.cpp file or across the whole project used to generate the DLL file?
We can easily remove the globals in the main.cpp file. However since you mentioned the DLL needs to be re-entrant every function that is called within the DLL must be re-entrant as well. Please correct me if that is incorrect.
Are classes with private member variables to hold data to be shared across modules acceptable?
Hi Jay, you and I are corresponding via the official support channel, but I wanted to share my feedback here in case other users come across your questions in the future.
Are global variables not an option in the main.cpp file or across the whole project used to generate the DLL file?
We can easily remove the globals in the main.cpp file. However since you mentioned the DLL needs to be re-entrant every function that is called within the DLL must be re-entrant as well. Please correct me if that is incorrect.
DLL is only as reentrant as its weakest link. If the main function avoids globals but calls non-reentrant functions, the entire DLL becomes non-reentrant. If globals are used only for constants or truly immutable data, they may be acceptable, but any global that holds mutable state can compromise reentrancy.
Are classes with private member variables to hold data to be shared across modules acceptable?
I’m assuming “hold data to be shared across modules” implies various submodules within a DLL and not data shared between DLLs.
This should be thread-safe as long as the class instances themselves are not shared across threads and the class doesn’t access global or static resources. Each instance of the class maintains its own state in its private variables.
What about filescope variables [static]?
These are essentially equivalent to globals from a reentrancy perspective. They maintain state between calls and can cause issues in concurrent environments.