Oaz / Femyou

loading and running fmi-standard FMUs in dotnet core
MIT License
15 stars 5 forks source link

A callback was made on a garbage collected delegate #5

Closed Nils12345678901234567 closed 7 hours ago

Nils12345678901234567 commented 4 months ago

After running my simulation loop for a short while (setting inputs - AdvanceTime - reading outputs)the program crashes with this error:

Process terminated. A callback was made on a garbage collected delegate of type 'Femyou!Femyou.FMI2+fmi2StepFinished::Invoke'.
   at Femyou.Instance.AdvanceTime(Double)

Turns out that this happens when the garbage collector kicks in. If I call GC.Collect in my simulation loop I get the error immediately.

If I debug the program in Visual Studio it catches a ExecutionEngineException with no call stack.

Oaz commented 4 months ago

Can you provide a piece of code to reproduce this issue?

Nils12345678901234567 commented 4 months ago

If you modify the test LogWhenVariableCannotBeSet like this:

 [Test]
 public void LogWhenVariableCannotBeSet()
 {
     var spyCallback = new SpyCallback();
     using var model = Model.Load(TestTools.GetFmuPath("BouncingBall.fmu"));
     using var instance = model.CreateCoSimulationInstance("my name", spyCallback);
     try
     {
         GC.Collect();  // Force garbage collection here to reproduce bug
         instance.WriteReal((model.Variables["v_min"], 1));
     }

Note: The test will not fail, it will never finish execution. You have to look in the output window to see the error message.

Nils12345678901234567 commented 4 months ago

I tried to reproduce with the test FeedthroughReferenceScenario as well but that didi not work. I suspect that the 'Feedthrough.fmu' fmu never calls any callbacks. A similar test using BouncingBall.fmu would probably show the problem.

Nils12345678901234567 commented 4 months ago

If the local variable functions in the constructor of the Callbacks class is changed to a member variable of the Callbacks-class like shown below this should fix the problem.

 class Callbacks : IDisposable
 {
     private FMI2.fmi2CallbackFunctions functions;
     public Callbacks(Instance instance, ICallbacks cb)
     {
         Instance = instance;
         CB = cb;
         handle = GCHandle.Alloc(this);
         functions = new FMI2.fmi2CallbackFunctions
         {
             logger = LoggerCallback,
             allocateMemory = Marshalling.AllocateMemory,
             freeMemory = Marshalling.FreeMemory,
             stepFinished = StepFinishedCallback,
             componentEnvironment = GCHandle.ToIntPtr(handle)
         };
Oaz commented 7 hours ago

Thank you. The suggested change is implemented in the current version.