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

attempt to modify variable captured in a closure causes segmentation fault #13

Open
BrianGun opened this issue Oct 11, 2020 · 2 comments

Comments

@BrianGun
Copy link

When attempting to modify a field in a struct variable that is captured from the enclosing scope MiniFB crashes. If the struct variable is global then MiniFB doesn't segfault.

See the function onkeypress below for the assignment that causes the segfault.

version info:

Julia Version 1.5.2
Commit 539f3ce943 (2020-09-23 23:17 UTC)
Platform Info:
OS: Linux (x86_64-pc-linux-gnu)
CPU: AMD EPYC 7702P 64-Core Processor
WORD_SIZE: 64
LIBM: libopenlibm
LLVM: libLLVM-9.0.1 (ORCJIT, znver2)
Environment:
JULIA_NUM_THREADS = 64
JULIA_EDITOR = "/usr/share/code/code"julia> using MiniFB

minimum working example:

julia> mutable struct Simple
           a::Float64
       end

julia> function mwe()
           width = 600
           height = 600
           window = mfb_open_ex("Luxor -> MiniFB", width, height, MiniFB.WF_RESIZABLE)
           t = Simple(1.0)

           function onkeypress(window::Ptr{Cvoid}, key::mfb_key, mod::mfb_key_mod, ispressed::Bool)
               t.a += 1 #this causes the segfault. If t is a global variable no segfault
               return nothing
           end

           mfb_set_target_fps(60)
           mfb_set_keyboard_callback(window, onkeypress)

         
           while mfb_wait_sync(window) # The Luxor `@imagematrix` macro creates a 2D matrix of Color objects in ARGB32 format, which is exactly what MiniFB requires
               buffer = Luxor.@imagematrix begin
                   Luxor.background("grey40")
               end 600 600 
               # The image matrix created by Luxor is, in standard Julia convention, column first. The buffer
               # expected by MiniFB is row first. The call to `permutedims` makes that change.
               state = mfb_update(window, permutedims(buffer, (2, 1)))
               # Exit when the user closes the window.
               if state != MiniFB.STATE_OK
                   break
               end
           end

           mfb_close(window)
       end
mwe (generic function with 1 method)

julia> mwe()

Window created using X11 API

signal (11): Segmentation fault
in expression starting at REPL[5]:1
unknown function (ip: 0x7f7e8800cfc3)
Allocations: 11027408 (Pool: 11024513; Big: 2895); GC: 24
Segmentation fault (core dumped)
@jg-854
Copy link

jg-854 commented Nov 11, 2021

I have run into similar problems and it is due to the Garbage Collector (GC). When mfb_set_keyboard_callback(window, onkeypress) is called, MiniFB.jl converts `onkeypress' to a Base.CFunction object (it acts as a closure for the C interface). This object is never referenced on the Julia side after that. Therefore, the GC kicks in and frees the object from memory.

To verify this issue, call GC.enable(false) to disable the garbage collector before you run and you should get no segmentation fault.

Now to get around this issue there are multiple (though not neat) options.

  • 1.) Define your variables in the global scope so that Julia doesn't need to create a closure object in the first place.

  • 2.) Disable the GC when this function runs.

  • 3.) Directly keep a reference of the closure object keyboard_c - this object is generated when MiniFB.jl calls the @cfunction macro shown below:

    MiniFB.jl/src/helpers.jl

    Lines 40 to 43 in e690972

    function mfb_set_keyboard_callback(window, keyboard::Function)
    keyboard_c = @cfunction($keyboard, Cvoid, (Ptr{Cvoid}, mfb_key, mfb_key_mod, Bool))
    mfb_set_keyboard_callback(window, keyboard_c)
    end

To ensure that the GC doesn't free up the object, you could call anytime runtime function such as println(keyboard_c)
after the while loop. I have tried this method and it does work, although it is not very neat.

@lysogeny
Copy link

I was experiencing the same issue and this is immensely helpful!

Thank you for your insights!

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

3 participants