samuelpowell / Spinnaker.jl

A Julia interface to the FLIR/PointGrey Spinnaker SDK
MIT License
15 stars 7 forks source link

Fix crash when finalizing the same camera multiple times #88

Open Octogonapus opened 1 year ago

Octogonapus commented 1 year ago

Fix camera finalization when there are multiple of the same camera

When you have multiple camera objects referencing the same hardware, created either deliberately or from running a program multiple times without a full GC, the finalization of one camera can break interactions with the other cameras. This results in users of the other cameras getting the error SPINNAKER_ERR_NOT_INITIALIZED(-1002).

This commit introduces a deferred finalization method based on FFTW.jl, in combination with a method to avoid releasing a camera which is currently in use by another consumer.

There is an unwanted side effect of these changes, a crash at program exit from finalizing the System:

error in running finalizer: Spinnaker.SpinError(val=Spinnaker._spinError(0xfffffc14))
checkerror at /home/user/.julia/packages/Spinnaker/agc0J/src/Spinnaker.jl:31 [inlined]
spinSystemReleaseInstance at /home/user/.julia/packages/Spinnaker/agc0J/src/wrapper/spin_api.jl:101 [inlined]
_release! at /home/user/.julia/packages/Spinnaker/agc0J/src/System.jl:29
unknown function (ip: 0x7f8809310482)
_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
run_finalizer at /cache/build/default-amdci4-6/julialang/julia-release-1-dot-9/src/gc.c:417
jl_gc_run_finalizers_in_list at /cache/build/default-amdci4-6/julialang/julia-release-1-dot-9/src/gc.c:507
run_finalizers at /cache/build/default-amdci4-6/julialang/julia-release-1-dot-9/src/gc.c:553
ijl_atexit_hook at /cache/build/default-amdci4-6/julialang/julia-release-1-dot-9/src/init.c:299
jl_repl_entrypoint at /cache/build/default-amdci4-6/julialang/julia-release-1-dot-9/src/jlapi.c:718
main at /cache/build/default-amdci4-6/julialang/julia-release-1-dot-9/cli/loader_exe.c:59
__libc_start_main at /lib/x86_64-linux-gnu/libc.so.6 (unknown line)
unknown function (ip: 0x401098)
julia: /vcpkg/installed/x64-linux/include/boost/thread/pthread/recursive_mutex.hpp:108: void boost::recursive_mutex::lock(): Assertion `!posix::pthread_mutex_lock(&m)' failed.

[27940] signal (6.-6): Aborted
in expression starting at none:0
Allocations: 4826084 (Pool: 4821646; Big: 4438); GC: 16
Aborted (core dumped)

I've tried to fix this, but I don't know why it's happening, so no solution yet. I think this is a minor issue because the System is a singleton, so it will never be finalized during normal program execution.

I also created a program to test this change, which requires access to a real camera:

module DebugSpinnakerCamera

using Spinnaker

export stress_start_stop

function stress_start_stop()
    stop = Ref(false)
    @async begin
        while !stop[]
            GC.gc(true)
            sleep(0.1)
        end
    end
    try
        t1 = @async begin
            while !stop[]
                camlist = CameraList()
                cam = camlist[0]
                start!(cam)
                try
                    sleep(0.01)
                    @show powersupplyvoltage(cam)
                    framerate!(cam, 55.0)
                    gain_lims = gain_limits(cam)
                    @async gain!(cam, rand(gain_lims[1]:gain_lims[2]))
                    getimage(cam)
                    sleep(0.01)
                finally
                    stop!(cam)
                end
                yield()
            end
        end
        t2 = @async begin
            while !stop[]
                camlist = CameraList()
                cam = camlist[0]
                start!(cam)
                try
                    sleep(0.01)
                    @show powersupplyvoltage(cam)
                    framerate!(cam, 55.0)
                    gain_lims = gain_limits(cam)
                    @async gain!(cam, rand(gain_lims[1]:gain_lims[2]))
                    getimage(cam)
                    sleep(0.01)
                finally
                    stop!(cam)
                end
                yield()
            end
        end
        fetch(t1)
        fetch(t2)
    finally
        stop[] = true
    end
end

end
Octogonapus commented 1 year ago

Not sure if my test program should be added to the test suite given it needs a real camera. This CI doesn't have one, right?