Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fmiReload and fmiLoad not thread-save #186

Open
AnHeuermann opened this issue May 10, 2023 · 9 comments
Open

fmiReload and fmiLoad not thread-save #186

AnHeuermann opened this issue May 10, 2023 · 9 comments
Assignees

Comments

@AnHeuermann
Copy link

AnHeuermann commented May 10, 2023

Issue

I want to simulate a number of FMUs in parallel with FMI.jl and reuse the already loaded FMUs for multiple runs, but encounter an error in FMIImport where cd is used to change the directory while running in parallel.

ERROR: LoadError: IOError: pwd(): no such file or directory (ENOENT)
Stacktrace:
  [1] uv_error
    @ ./libuv.jl:97 [inlined]
  [2] pwd()
    @ Base.Filesystem ./file.jl:63
  [3] loadBinary(fmu::FMICore.FMU2)
    @ FMIImport ~/.julia/packages/FMIImport/Zfd3H/src/FMI2/ext.jl:246
  [4] fmi2Load(pathToFMU::String; unpackPath::String, type::Nothing, cleanup::Bool, logLevel::UInt32)
    @ FMIImport ~/.julia/packages/FMIImport/Zfd3H/src/FMI2/ext.jl:240
  [5] #fmiLoad#168
    @ ~/.julia/packages/FMI/PEsZa/src/FMI.jl:616 [inlined]

I believe the issue is the usage of cd in FMIImport ext.jl#L245-L252
Is cd needed here? But I guess there are other problems with using e.g. ccall in parallel, see https://docs.julialang.org/en/v1/manual/multi-threading/#@threadcall.

If it's not possible to use fmiLoad and fmiReload in parallel, maybe add a check to them to test if they are run inside a parallel region?

How to reproduce

fmuArray = [FMI.fmiLoad(fmuPath) for _ in 1:10]
locks = [ReentrantLock() for _ in 1:10]
Threads.@threads for i in 1:100
    idx = ((i-1)%10+1)
    lock(locks[idx]) do
      fmu = fmuArray[idx]
      # Do something with the FMU
      FMI.fmiInstantiate!(fmu)
      FMI.fmiSetupExperiment(fmu)
      FMI.fmiEnterInitializationMode(fmu)
      FMI.fmiExitInitializationMode(fmu)
      
     # Reload FMU
      FMI.fmiReload(fmu)
    end
end

You can get similar results with

Threads.@threads for i in 1:100
         lastDirectory = pwd()
         mkdir("temp_$i")
         cd("temp_$i")
         sleep(5)
         cd(lastDirectory)
end
@ThummeTo
Copy link
Owner

ThummeTo commented May 12, 2023

We will check this.

PS: In the current release, you don't need to load the FMU multiple times for multithreading. Loading it once and use fmiSimulate in a Thread-Loop is sufficient (one fmi2Instance is allocated per Thread, all Threads share the same FMU/DLL). But the tutorial "multiprocessing" needs an update (multiple FMUs are loaded there).

  • reproduce issue
  • update multithreading tutorial

@AnHeuermann
Copy link
Author

AnHeuermann commented Oct 5, 2023

Any news on this issue? My scripts are generating a bunch of segmentation faults when running on a single FMU in multiple threads and FMI.jl is reporting errors when I re-use a FMU on a different thread. So I can't let my scripts run over night, because they'll break most of the time.

ERROR: LoadError: TaskFailedException

    nested task error: AssertionError: ["No FMU instance allocated (in current thread with ID `18`), have you already called fmiInstantiate?"]

@ThummeTo
Copy link
Owner

ThummeTo commented Oct 5, 2023

Hm... I am using multithreading on a single FMU quite often. There is no need to load/unload the FMU in this operation mode multiple times. I do this not only for simulation, but also gradinet determination in NeuralFMUs. You should be able to load the FMU a single time (on the main thread), simulate it on different threads (monte carlo style) and being able to unload it after all threads finished (again on the main thread).

I am working on an update for the multithreading tutorial. But there where multiple other ToDos the last months (speed, sensitivities, ...)

@ThummeTo
Copy link
Owner

ThummeTo commented Oct 7, 2023

So a typical workflow might look like:

(1) load FMU (its a dynamic library, so its ok to just load it once)
(2) do things with multiple instances of the FMU (FMI.jl allocates a new instance for every simulation by default) in parallel, but dont load/unload/reload the FMU (no need to do that)
(3) finally - after every thread finished - unload the FMU at program end

@AnHeuermann
Copy link
Author

I think I found my issue. I was

My mwe looks something like this:

FMU from

model HelloWorld "Simple hello world example"
  Real x(start=1, fixed=true);
  Real y;
  Real z;
  parameter Real a = -0.5;
equation
  der(x) = a*x;
  y = sin(x);
  z = 2*abs(x);
end HelloWorld;

generated with OpenModelica:

loadFile("helloWorld.mo"); getErrorString();
buildModelFMU(HelloWorld); getErrorString();
import FMI
import FMIImport

ENV["JULIA_DEBUG"] = "FMI,FMIIMport,FMICore"

function runAll()
  fmuPath = "fmu/HelloWorld.fmu"
  inputVars = ["x"]
  outputVars = ["y", "z"]

  nInputs = length(inputVars)
  nOutputs = length(outputVars)
  nVars = nInputs + nOutputs

  fmu = FMI.fmiLoad(fmuPath)

  Threads.@threads for _ in 1:Threads.nthreads()*10
    @info "Thread $(Threads.threadid()) starting!"
    comp = FMI.fmiInstantiate!(fmu; loggingOn = false, externalCallbacks=false)
    FMI.fmiSetupExperiment(comp)
    FMI.fmiEnterInitializationMode(comp)
    FMI.fmiExitInitializationMode(comp)

    # Set random data
    row = Array{Float64}(undef, nVars)
    row_vr = FMI.fmiStringToValueReference(fmu.modelDescription, vcat(inputVars,outputVars))

    for _ in 1:10
      row[1:nInputs] = rand(nInputs)

      FMIImport.fmi2SetReal(comp, row_vr, row)
      row[nInputs+1:end] .= FMIImport.fmi2GetReal(comp, row_vr[nInputs+1:end])
    end

    #FMI.fmiFreeInstance!(comp)
    @info "Thread $(Threads.threadid()) finished!"
  end

  FMI.fmiUnload(fmu)
end

Output:

julia> runAll()
[ Info: Thread 2 starting!
[ Info: Thread 13 starting!
[ Info: Thread 19 starting!
[ Info: Thread 14 starting!
[ Info: Thread 3 starting!
[ Info: Thread 11 starting!
[ Info: Thread 5 starting!
[ Info: Thread 9 starting!
[ Info: Thread 11 starting!
[ Info: Thread 3 starting!
[ Info: Thread 8 starting!
[ Info: Thread 18 starting!
[ Info: Thread 16 starting!
[ Info: Thread 19 starting!
[ Info: Thread 10 starting!
[ Info: Thread 1 starting!
[ Info: Thread 4 starting!
[ Info: Thread 14 starting!
[ Info: Thread 13 starting!
[ Info: Thread 6 starting!
┌ Debug: fmi2Instantiate(instanceName: Ptr{UInt8} @0x00007fb4b12f2038, fmuType: 0, fmuGUID: Ptr{UInt8} @0x00007fb5afcc1cf8, fmuResourceLocation: Ptr{UInt8} @0x00007fb4b0847bd8, functions: Ptr{FMICore.fmi2CallbackFunctions} @0x00007fb49ee39ae0, visible: 0, loggingOn: 0) → Ptr{Nothing} @0x00007fb59c387290
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:25
┌ Debug: fmi2SetupExperiment(c: Ptr{Nothing} @0x00007fb59c387290, toleranceDefined: 0, tolerance: 0.0, startTime: 0.0, stopTimeDefined: 0, stopTime: 0.0) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:103
┌ Debug: fmi2Instantiate(instanceName: Ptr{UInt8} @0x00007fb4b12f2038, fmuType: 0, fmuGUID: Ptr{UInt8} @0x00007fb5afcc1cf8, fmuResourceLocation: Ptr{UInt8} @0x00007fb4b0847bd8, functions: Ptr{FMICore.fmi2CallbackFunctions} @0x00007fb4a15b0580, visible: 0, loggingOn: 0) → Ptr{Nothing} @0x00007fb594257a50
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:25
┌ Debug: fmi2EnterInitializationMode(c: Ptr{Nothing} @0x00007fb59c387290) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:118
┌ Debug: fmi2SetupExperiment(c: Ptr{Nothing} @0x00007fb594257a50, toleranceDefined: 0, tolerance: 0.0, startTime: 0.0, stopTimeDefined: 0, stopTime: 0.0) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:103
┌ Debug: fmi2ExitInitializationMode(c: Ptr{Nothing} @0x00007fb59c387290) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:133
┌ Debug: fmi2EnterInitializationMode(c: Ptr{Nothing} @0x00007fb594257a50) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:118
┌ Debug: fmi2SetReal(c: Ptr{Nothing} @0x00007fb59c387290, vr: UInt32[0x00000000, 0x00000002, 0x00000003], nvr: 3, value: [0.007785949875737441, 0.0, 0.0])
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:193
┌ Debug: fmi2Instantiate(instanceName: Ptr{UInt8} @0x00007fb4b12f2038, fmuType: 0, fmuGUID: Ptr{UInt8} @0x00007fb5afcc1cf8, fmuResourceLocation: Ptr{UInt8} @0x00007fb4b0847bd8, functions: Ptr{FMICore.fmi2CallbackFunctions} @0x00007fb4a14a5150, visible: 0, loggingOn: 0) → Ptr{Nothing} @0x00007fb56c31e9c0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:25
┌ Debug: fmi2ExitInitializationMode(c: Ptr{Nothing} @0x00007fb594257a50) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:133
┌ Debug: fmi2SetReal(c: Ptr{Nothing} @0x00007fb594257a50, vr: UInt32[0x00000000, 0x00000002, 0x00000003], nvr: 3, value: [0.5500224536739118, 0.0, 0.0])
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:193
┌ Debug: fmi2GetReal(c: Ptr{Nothing} @0x00007fb59c387290, vr: UInt32[0x00000002, 0x00000003], nvr: 2, value: [0.007785871210611238, 0.015571899751474882]) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:178
┌ Debug: fmi2SetupExperiment(c: Ptr{Nothing} @0x00007fb56c31e9c0, toleranceDefined: 0, tolerance: 0.0, startTime: 0.0, stopTimeDefined: 0, stopTime: 0.0) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:103
┌ Debug: fmi2FreeInstance(c: Ptr{Nothing} @0x00007fb59c387290) → [nothing]
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:41
┌ Debug: fmi2EnterInitializationMode(c: Ptr{Nothing} @0x00007fb56c31e9c0) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:118
┌ Debug: fmi2GetReal(c: Ptr{Nothing} @0x00007fb594257a50, vr: UInt32[0x00000002, 0x00000003], nvr: 2, value: [0.5227063711065167, 1.1000449073478236]) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:178
[ Info: Thread 2 finished!
┌ Debug: fmi2ExitInitializationMode(c: Ptr{Nothing} @0x00007fb56c31e9c0) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:133
┌ Debug: fmi2FreeInstance(c: Ptr{Nothing} @0x00007fb594257a50) → [nothing]
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:41
┌ Debug: fmi2Instantiate(instanceName: Ptr{UInt8} @0x00007fb4b12f2038, fmuType: 0, fmuGUID: Ptr{UInt8} @0x00007fb5afcc1cf8, fmuResourceLocation: Ptr{UInt8} @0x00007fb4b0847bd8, functions: Ptr{FMICore.fmi2CallbackFunctions} @0x00007fb499d55420, visible: 0, loggingOn: 0) → Ptr{Nothing} @0x00007fb55444f3b0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:25
[ Info: Thread 3 finished!
┌ Debug: fmi2SetReal(c: Ptr{Nothing} @0x00007fb56c31e9c0, vr: UInt32[0x00000000, 0x00000002, 0x00000003], nvr: 3, value: [0.8363120420636442, 0.0, 0.0])
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:193
┌ Debug: fmi2SetupExperiment(c: Ptr{Nothing} @0x00007fb55444f3b0, toleranceDefined: 0, tolerance: 0.0, startTime: 0.0, stopTimeDefined: 0, stopTime: 0.0) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:103
┌ Debug: fmi2GetReal(c: Ptr{Nothing} @0x00007fb56c31e9c0, vr: UInt32[0x00000002, 0x00000003], nvr: 2, value: [0.7421764867727053, 1.6726240841272884]) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:178
┌ Debug: fmi2EnterInitializationMode(c: Ptr{Nothing} @0x00007fb55444f3b0) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:118
┌ Debug: fmi2Instantiate(instanceName: Ptr{UInt8} @0x00007fb4b12f2038, fmuType: 0, fmuGUID: Ptr{UInt8} @0x00007fb5afcc1cf8, fmuResourceLocation: Ptr{UInt8} @0x00007fb4b0847bd8, functions: Ptr{FMICore.fmi2CallbackFunctions} @0x00007fb4a1345750, visible: 0, loggingOn: 0) → Ptr{Nothing} @0x00007fb58c44f120
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:25
┌ Debug: fmi2ExitInitializationMode(c: Ptr{Nothing} @0x00007fb55444f3b0) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:133
┌ Debug: fmi2FreeInstance(c: Ptr{Nothing} @0x00007fb56c31e9c0) → [nothing]
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:41
┌ Debug: fmi2SetupExperiment(c: Ptr{Nothing} @0x00007fb58c44f120, toleranceDefined: 0, tolerance: 0.0, startTime: 0.0, stopTimeDefined: 0, stopTime: 0.0) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:103
[ Info: Thread 12 finished!
┌ Debug: fmi2EnterInitializationMode(c: Ptr{Nothing} @0x00007fb58c44f120) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:118
┌ Debug: fmi2SetReal(c: Ptr{Nothing} @0x00007fb55444f3b0, vr: UInt32[0x00000000, 0x00000002, 0x00000003], nvr: 3, value: [0.8703188848113075, 0.0, 0.0])
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:193
┌ Debug: fmi2ExitInitializationMode(c: Ptr{Nothing} @0x00007fb58c44f120) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:133
┌ Debug: fmi2Instantiate(instanceName: Ptr{UInt8} @0x00007fb4b12f2038, fmuType: 0, fmuGUID: Ptr{UInt8} @0x00007fb5afcc1cf8, fmuResourceLocation: Ptr{UInt8} @0x00007fb4b0847bd8, functions: Ptr{FMICore.fmi2CallbackFunctions} @0x00007fb49db567a0, visible: 0, loggingOn: 0) → Ptr{Nothing} @0x00007fb57c2df520
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:25
┌ Debug: fmi2GetReal(c: Ptr{Nothing} @0x00007fb55444f3b0, vr: UInt32[0x00000002, 0x00000003], nvr: 2, value: [0.7645345235525051, 1.740637769622615]) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:178
┌ Debug: fmi2SetupExperiment(c: Ptr{Nothing} @0x00007fb57c2df520, toleranceDefined: 0, tolerance: 0.0, startTime: 0.0, stopTimeDefined: 0, stopTime: 0.0) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:103
┌ Debug: fmi2FreeInstance(c: Ptr{Nothing} @0x00007fb55444f3b0) → [nothing]
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:41
[ Info: Thread 15 finished!
┌ Debug: fmi2SetReal(c: Ptr{Nothing} @0x00007fb58c44f120, vr: UInt32[0x00000000, 0x00000002, 0x00000003], nvr: 3, value: [0.3752589544361712, 0.0, 0.0])
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:193
┌ Debug: fmi2EnterInitializationMode(c: Ptr{Nothing} @0x00007fb57c2df520) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:118
┌ Debug: fmi2GetReal(c: Ptr{Nothing} @0x00007fb58c44f120, vr: UInt32[0x00000002, 0x00000003], nvr: 2, value: [0.36651347587929817, 0.7505179088723424]) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:178
┌ Debug: fmi2ExitInitializationMode(c: Ptr{Nothing} @0x00007fb57c2df520) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:133
┌ Debug: fmi2FreeInstance(c: Ptr{Nothing} @0x00007fb58c44f120) → [nothing]
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:41
[ Info: Thread 6 finished!
┌ Debug: fmi2SetReal(c: Ptr{Nothing} @0x00007fb57c2df520, vr: UInt32[0x00000000, 0x00000002, 0x00000003], nvr: 3, value: [0.0542789737565329, 0.0, 0.0])
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:193
┌ Debug: fmi2Instantiate(instanceName: Ptr{UInt8} @0x00007fb4b12f2038, fmuType: 0, fmuGUID: Ptr{UInt8} @0x00007fb5afcc1cf8, fmuResourceLocation: Ptr{UInt8} @0x00007fb4b0847bd8, functions: Ptr{FMICore.fmi2CallbackFunctions} @0x00007fb4a13993f0, visible: 0, loggingOn: 0) → Ptr{Nothing} @0x00007fb5441c26e0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:25
┌ Debug: fmi2GetReal(c: Ptr{Nothing} @0x00007fb57c2df520, vr: UInt32[0x00000002, 0x00000003], nvr: 2, value: [0.054252324833830703, 0.1085579475130658]) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:178
┌ Debug: fmi2SetupExperiment(c: Ptr{Nothing} @0x00007fb5441c26e0, toleranceDefined: 0, tolerance: 0.0, startTime: 0.0, stopTimeDefined: 0, stopTime: 0.0) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:103
┌ Debug: fmi2FreeInstance(c: Ptr{Nothing} @0x00007fb57c2df520) → [nothing]
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:41
┌ Debug: fmi2EnterInitializationMode(c: Ptr{Nothing} @0x00007fb5441c26e0) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:118
┌ Debug: fmi2Instantiate(instanceName: Ptr{UInt8} @0x00007fb4b12f2038, fmuType: 0, fmuGUID: Ptr{UInt8} @0x00007fb5afcc1cf8, fmuResourceLocation: Ptr{UInt8} @0x00007fb4b0847bd8, functions: Ptr{FMICore.fmi2CallbackFunctions} @0x00007fb4a1225bd0, visible: 0, loggingOn: 0) → Ptr{Nothing} @0x00007fb5340b6270
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:25
[ Info: Thread 9 finished!
┌ Debug: fmi2ExitInitializationMode(c: Ptr{Nothing} @0x00007fb5441c26e0) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:133
┌ Debug: fmi2SetupExperiment(c: Ptr{Nothing} @0x00007fb5340b6270, toleranceDefined: 0, tolerance: 0.0, startTime: 0.0, stopTimeDefined: 0, stopTime: 0.0) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:103
┌ Debug: fmi2SetReal(c: Ptr{Nothing} @0x00007fb5441c26e0, vr: UInt32[0x00000000, 0x00000002, 0x00000003], nvr: 3, value: [0.5863938555148537, 0.0, 0.0])
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:193
┌ Debug: fmi2EnterInitializationMode(c: Ptr{Nothing} @0x00007fb5340b6270) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:118
┌ Debug: fmi2GetReal(c: Ptr{Nothing} @0x00007fb5441c26e0, vr: UInt32[0x00000002, 0x00000003], nvr: 2, value: [0.5533609197270968, 1.1727877110297074]) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:178
┌ Debug: fmi2Instantiate(instanceName: Ptr{UInt8} @0x00007fb4b12f2038, fmuType: 0, fmuGUID: Ptr{UInt8} @0x00007fb5afcc1cf8, fmuResourceLocation: Ptr{UInt8} @0x00007fb4b0847bd8, functions: Ptr{FMICore.fmi2CallbackFunctions} @0x00007fb4a1569330, visible: 0, loggingOn: 0) → Ptr{Nothing} @0x00007fb5503802b0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:25
┌ Debug: fmi2FreeInstance(c: Ptr{Nothing} @0x00007fb5441c26e0) → [nothing]
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:41
┌ Debug: fmi2SetupExperiment(c: Ptr{Nothing} @0x00007fb5503802b0, toleranceDefined: 0, tolerance: 0.0, startTime: 0.0, stopTimeDefined: 0, stopTime: 0.0) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:103
┌ Debug: fmi2ExitInitializationMode(c: Ptr{Nothing} @0x00007fb5340b6270) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:133
┌ Debug: fmi2Instantiate(instanceName: Ptr{UInt8} @0x00007fb4b12f2038, fmuType: 0, fmuGUID: Ptr{UInt8} @0x00007fb5afcc1cf8, fmuResourceLocation: Ptr{UInt8} @0x00007fb4b0847bd8, functions: Ptr{FMICore.fmi2CallbackFunctions} @0x00007fb4a14a5270, visible: 0, loggingOn: 0) → Ptr{Nothing} @0x00007fb55c235de0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:25
[ Info: Thread 13 finished!
┌ Debug: fmi2EnterInitializationMode(c: Ptr{Nothing} @0x00007fb5503802b0) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:118
┌ Debug: fmi2SetupExperiment(c: Ptr{Nothing} @0x00007fb55c235de0, toleranceDefined: 0, tolerance: 0.0, startTime: 0.0, stopTimeDefined: 0, stopTime: 0.0) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:103
┌ Debug: fmi2SetReal(c: Ptr{Nothing} @0x00007fb5340b6270, vr: UInt32[0x00000000, 0x00000002, 0x00000003], nvr: 3, value: [0.7396688106025051, 0.0, 0.0])
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:193
┌ Debug: fmi2ExitInitializationMode(c: Ptr{Nothing} @0x00007fb5503802b0) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:133
┌ Debug: fmi2EnterInitializationMode(c: Ptr{Nothing} @0x00007fb55c235de0) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:118
┌ Debug: fmi2SetReal(c: Ptr{Nothing} @0x00007fb5503802b0, vr: UInt32[0x00000000, 0x00000002, 0x00000003], nvr: 3, value: [0.17655822007580613, 0.0, 0.0])
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:193
┌ Debug: fmi2ExitInitializationMode(c: Ptr{Nothing} @0x00007fb55c235de0) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:133
┌ Debug: fmi2GetReal(c: Ptr{Nothing} @0x00007fb5503802b0, vr: UInt32[0x00000002, 0x00000003], nvr: 2, value: [0.17564234626318317, 0.35311644015161225]) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:178
┌ Debug: fmi2SetReal(c: Ptr{Nothing} @0x00007fb55c235de0, vr: UInt32[0x00000000, 0x00000002, 0x00000003], nvr: 3, value: [0.7221951008339035, 0.0, 0.0])
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:193
┌ Debug: fmi2FreeInstance(c: Ptr{Nothing} @0x00007fb5503802b0) → [nothing]
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:41
┌ Debug: fmi2Instantiate(instanceName: Ptr{UInt8} @0x00007fb4b12f2038, fmuType: 0, fmuGUID: Ptr{UInt8} @0x00007fb5afcc1cf8, fmuResourceLocation: Ptr{UInt8} @0x00007fb4b0847bd8, functions: Ptr{FMICore.fmi2CallbackFunctions} @0x00007fb49db56770, visible: 0, loggingOn: 0) → Ptr{Nothing} @0x00007fb594374420
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:25
[ Info: Thread 17 finished!
┌ Debug: fmi2GetReal(c: Ptr{Nothing} @0x00007fb5340b6270, vr: UInt32[0x00000002, 0x00000003], nvr: 2, value: [0.6740433016954693, 1.4793376212050102]) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:178

[11758] signal (11.1): Segmentation fault
in expression starting at REPL[2]:1
omc_util_get_pool_state at /tmp/fmijl_2A606N/HelloWorld/binaries/linux64/HelloWorld.so (unknown line)
updateIfNeeded at /tmp/fmijl_2A606N/HelloWorld/binaries/linux64/HelloWorld.so (unknown line)
fmi2GetReal at /tmp/fmijl_2A606N/HelloWorld/binaries/linux64/HelloWorld.so (unknown line)
fmi2GetReal! at /home/USER/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:174
fmi2GetReal! at /home/USER/.julia/packages/FMIImport/hLBHK/src/FMI2/c.jl:558 [inlined]
fmi2GetReal at /home/USER/.julia/packages/FMIImport/hLBHK/src/FMI2/int.jl:143
unknown function (ip: 0x7fb4a9936b7f)
┌ Debug: fmi2SetupExperiment(c: Ptr{Nothing} @0x00007fb594374420, toleranceDefined: 0, tolerance: 0.0, startTime: 0.0, stopTimeDefined: 0, stopTime: 0.0) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:103
┌ Debug: fmi2FreeInstance(c: Ptr{Nothing} @0x00007fb5340b6270) → [nothing]
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:41
[ Info: Thread 19 finished!
┌ Debug: fmi2EnterInitializationMode(c: Ptr{Nothing} @0x00007fb594374420) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:118
_jl_invoke at /cache/build/default-amdci4-6/julialang/julia-release-1-dot-9/src/gf.c:2758 [inlined]
ijl_apply_generic at /cache/build/default-amdci4-6/julialang/julia-release-1-dot-9/src/gf.c:2940
┌ Debug: fmi2ExitInitializationMode(c: Ptr{Nothing} @0x00007fb594374420) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:133
#fmi2GetReal#10 at /home/USER/.julia/packages/FMI/1VUBe/src/FMI2/comp_wraps.jl:107
fmi2GetReal at /home/USER/.julia/packages/FMI/1VUBe/src/FMI2/comp_wraps.jl:106 [inlined]
macro expansion at /home/USER/workspace/Testitesttest/FMI-multithread/runAllTheFMUs.jl:34 [inlined]
#6#threadsfor_fun#3 at ./threadingconstructs.jl:163
#6#threadsfor_fun at ./threadingconstructs.jl:130 [inlined]
#1 at ./threadingconstructs.jl:108
unknown function (ip: 0x7fb4a992aa0f)
_jl_invoke at /cache/build/default-amdci4-6/julialang/julia-release-1-dot-9/src/gf.c:2758 [inlined]
ijl_apply_generic at /cache/build/default-amdci4-6/julialang/julia-release-1-dot-9/src/gf.c:2940
┌ Debug: fmi2SetReal(c: Ptr{Nothing} @0x00007fb594374420, vr: UInt32[0x00000000, 0x00000002, 0x00000003], nvr: 3, value: [0.6784114589340956, 0.0, 0.0])
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:193
jl_apply at /cache/build/default-amdci4-6/julialang/julia-release-1-dot-9/src/julia.h:1879 [inlined]
start_task at /cache/build/default-amdci4-6/julialang/julia-release-1-dot-9/src/task.c:1092
Allocations: 61803428 (Pool: 61766991; Big: 36437); GC: 98
┌ Debug: fmi2GetReal(c: Ptr{Nothing} @0x00007fb594374420, vr: UInt32[0x00000002, 0x00000003], nvr: 2, value: [0.6275570249747953, 1.3568229178681912]) → 0
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:178
┌ Debug: fmi2FreeInstance(c: Ptr{Nothing} @0x00007fb594374420) → [nothing]
└ @ FMICore ~/.julia/packages/FMICore/JhS6T/src/FMI2/cfunc.jl:41
[ Info: Thread 3 finished!
[1]    11758 segmentation fault  julia --threads=auto

@ThummeTo
Copy link
Owner

Is this error FMU-specific?
If no, can you use one public available (like from the modelica refernce FMUs or the FMIZoo.jl).
This would be much easier to reproduce, because I don't have OM installed.

@AnHeuermann
Copy link
Author

AnHeuermann commented Oct 19, 2023

I couldn't reproduce the same issue, but with 2.0 VanDerPol FMU from https://github.com/modelica/Reference-FMUs and can trigger an error in FMI.fmiFreeInstance!.

import FMI
import FMIImport

function runAll()
  fmuPath = "fmu/2.0/VanDerPol.fmu"
  inputVars = ["x0", "x1"]
  outputVars = ["x0", "x1"]

  nInputs = length(inputVars)
  nOutputs = length(outputVars)
  nVars = nInputs + nOutputs

  fmu = FMI.fmiLoad(fmuPath)

  Threads.@threads for _ in 1:Threads.nthreads()*10
    @info "Thread $(Threads.threadid()) starting!"
    comp = FMI.fmiInstantiate!(fmu; loggingOn = false, externalCallbacks=false)
    FMI.fmiSetupExperiment(comp)
    FMI.fmiEnterInitializationMode(comp)
    FMI.fmiExitInitializationMode(comp)

    # Set random data
    row = Array{Float64}(undef, nVars)
    row_vr = FMI.fmiStringToValueReference(fmu.modelDescription, vcat(inputVars,outputVars))

    for _ in 1:1000
      row[1:nInputs] = rand(nInputs)

      FMIImport.fmi2SetReal(comp, row_vr, row)
      row[nInputs+1:end] .= FMIImport.fmi2GetReal(comp, row_vr[nInputs+1:end])
    end

    FMI.fmiFreeInstance!(comp)
    @info "Thread $(Threads.threadid()) finished!"
  end

  FMI.fmiUnload(fmu)
end
julia --threads=auto -e "include(\"runAllTheFMUs.jl\"); runAll()"

[ Info: Thread 10 finished!
ERROR: TaskFailedException

    nested task error: AssertionError: fmi2FreeInstance!(...): Freeing 2 instances with one call, this is not allowed. Target address `Ptr{Nothing} @0x00007f2d0401a2a0` was found 2 times at indicies [6, 7].
    Stacktrace:
     [1] (::FMIImport.var"#78#80"{FMICore.FMU2Component{FMICore.FMU2}, Ptr{Nothing}})()
       @ FMIImport ~/.julia/packages/FMIImport/hLBHK/src/FMI2/c.jl:126
     [2] lock(f::FMIImport.var"#78#80"{FMICore.FMU2Component{FMICore.FMU2}, Ptr{Nothing}}, l::ReentrantLock)
       @ Base ./lock.jl:229
     [3] fmi2FreeInstance!(c::FMICore.FMU2Component{FMICore.FMU2}; popComponent::Bool)
       @ FMIImport ~/.julia/packages/FMIImport/hLBHK/src/FMI2/c.jl:124
     [4] fmi2FreeInstance!
       @ ~/.julia/packages/FMIImport/hLBHK/src/FMI2/c.jl:117 [inlined]
     [5] fmiFreeInstance!(str::FMICore.FMU2Component{FMICore.FMU2})
       @ FMI ~/.julia/packages/FMI/1VUBe/src/deprecated.jl:75
     [6] macro expansion
       @ ~/workspace/Testitesttest/FMI-multithread/runAllTheFMUs.jl:39 [inlined]
     [7] (::var"#6#threadsfor_fun#4"{var"#6#threadsfor_fun#3#5"{FMICore.FMU2, Int64, Int64, Vector{String}, Vector{String}, UnitRange{Int64}}})(tid::Int64; onethread::Bool)
       @ Main ./threadingconstructs.jl:163
     [8] #6#threadsfor_fun
       @ ./threadingconstructs.jl:130 [inlined]
     [9] (::Base.Threads.var"#1#2"{var"#6#threadsfor_fun#4"{var"#6#threadsfor_fun#3#5"{FMICore.FMU2, Int64, Int64, Vector{String}, Vector{String}, UnitRange{Int64}}}, Int64})()
       @ Base.Threads ./threadingconstructs.jl:108

...and 1 more exception.

Stacktrace:
 [1] threading_run(fun::var"#6#threadsfor_fun#4"{var"#6#threadsfor_fun#3#5"{FMICore.FMU2, Int64, Int64, Vector{String}, Vector{String}, UnitRange{Int64}}}, static::Bool)
   @ Base.Threads ./threadingconstructs.jl:120
 [2] macro expansion
   @ ./threadingconstructs.jl:168 [inlined]
 [3] runAll()
   @ Main ~/workspace/Testitesttest/FMI-multithread/runAllTheFMUs.jl:21
 [4] top-level scope
   @ none:1

I need to run it multiple times to see the error.

@AnHeuermann
Copy link
Author

Maybe it's more of an OpenModelica issue. What are the requirements for the FMU to be simulated in parallel?
The OpenModelica FMUs are not thread-safe and I think are using some global variables that shouldn't be changed by other threads. But would this happen in this case?

@ThummeTo
Copy link
Owner

ThummeTo commented Oct 23, 2023

I think thread-safety is definitely a hard requirement to simulate FMUs multi-threaded.
Ignoring this may lead to wrong simulation results at least (but can also crash of coure, dependent on the implementation).

However, the second issue (fmi2FreeInstance!) shouldn't appear and will be investigated.

@ThummeTo ThummeTo self-assigned this Oct 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants