Cfunction error handler

ccall

#1

Hi

I am trying to wrap a C function to set a error handler in the CALCEPH C library. The prototype is:

void calceph_seterrorhandler(int typehandler, void (*userfunc)(const char*))

The error message is passed to userfunc if typehandler=3.
typehandler = 1 is the default behavior printing messages to stdin.

Reference: https://www.imcce.fr/content/medias/recherche/equipes/asd/calceph/html/c/calceph.error.html#menu-calceph-seterrorhandler

and my wrapper is:

function disableCustomHandler()
    ccall((:calceph_seterrorhandler, libcalceph), Cvoid,
                  (Cint, Ptr{Cvoid}), 1, C_NULL)
end

mutable struct UserHandlerContainer
    f::Function
end

const userHandlerContainerInstance = UserHandlerContainer(s::String->Nothing)

function userHandlerWrapper(msg::Cstring)::Cvoid
    global userHandlerContainerInstance
    s = unsafe_string(msg)
    #println(s)
    userHandlerContainerInstance.f(s)
    return
end

userHandlerCWrapper = @cfunction(userHandlerWrapper, Cvoid, (Cstring,))

function setCustomHandler(f::Function)
    global userHandlerContainerInstance
    global userHandlerCWrapper
    userHandlerContainerInstance.f = f
    ccall((:calceph_seterrorhandler, libcalceph), Cvoid,
                  (Cint, Ptr{Cvoid}), 3, userHandlerCWrapper)
end


Now if I do:

CALCEPH.setCustomHandler(s::String->Nothing)

I get a segmentation fault whenever an error occurs in the C library CALCEPH.
unless I disable the custom handler before triggering the error:

CALCEPH.disableCustomHandler()

What is wrong my wrapper?

Thank you

This is the stack trace:


signal (11): Segmentation fault
in expression starting at /home/bgodard/.julia/dev/CALCEPH/test/runtests.jl:148
unknown function (ip: 0xffffffffffffffff)
calceph_spice_compute_unit at /home/bgodard/.julia/dev/CALCEPH/deps/usr/lib/libcalceph.so (unknown line)
calceph_compute_order at /home/bgodard/.julia/dev/CALCEPH/deps/usr/lib/libcalceph.so (unknown line)
calceph_compute_unit at /home/bgodard/.julia/dev/CALCEPH/deps/usr/lib/libcalceph.so (unknown line)
unsafe_compute! at /home/bgodard/.julia/dev/CALCEPH/src/compute.jl:161 [inlined]
macro expansion at /home/bgodard/.julia/dev/CALCEPH/src/ephem.jl:14 [inlined]
compute at /home/bgodard/.julia/dev/CALCEPH/src/compute.jl:131
unknown function (ip: 0x7f11fcd8fdc9)
jl_fptr_trampoline at /buildworker/worker/package_linux64/build/src/gf.c:1831
jl_apply_generic at /buildworker/worker/package_linux64/build/src/gf.c:2184
do_call at /buildworker/worker/package_linux64/build/src/interpreter.c:324
eval_value at /buildworker/worker/package_linux64/build/src/interpreter.c:430
eval_stmt_value at /buildworker/worker/package_linux64/build/src/interpreter.c:363 [inlined]
eval_body at /buildworker/worker/package_linux64/build/src/interpreter.c:682
eval_body at /buildworker/worker/package_linux64/build/src/interpreter.c:613
jl_interpret_toplevel_thunk_callback at /buildworker/worker/package_linux64/build/src/interpreter.c:806
unknown function (ip: 0xfffffffffffffffe)
unknown function (ip: 0x7f120930518f)
unknown function (ip: (nil))
jl_interpret_toplevel_thunk at /buildworker/worker/package_linux64/build/src/interpreter.c:815
jl_toplevel_eval_flex at /buildworker/worker/package_linux64/build/src/toplevel.c:805
jl_parse_eval_all at /buildworker/worker/package_linux64/build/src/ast.c:838
jl_load at /buildworker/worker/package_linux64/build/src/toplevel.c:839
include at ./boot.jl:317 [inlined]
include_relative at ./loading.jl:1044
include at ./sysimg.jl:29
jl_apply_generic at /buildworker/worker/package_linux64/build/src/gf.c:2184
include at ./client.jl:392
jl_fptr_trampoline at /buildworker/worker/package_linux64/build/src/gf.c:1831
jl_apply_generic at /buildworker/worker/package_linux64/build/src/gf.c:2184
do_call at /buildworker/worker/package_linux64/build/src/interpreter.c:324
eval_value at /buildworker/worker/package_linux64/build/src/interpreter.c:430
eval_stmt_value at /buildworker/worker/package_linux64/build/src/interpreter.c:363 [inlined]
eval_body at /buildworker/worker/package_linux64/build/src/interpreter.c:682
jl_interpret_toplevel_thunk_callback at /buildworker/worker/package_linux64/build/src/interpreter.c:806
unknown function (ip: 0xfffffffffffffffe)
unknown function (ip: 0x7f1207e8fe3f)
unknown function (ip: 0xffffffffffffffff)
jl_interpret_toplevel_thunk at /buildworker/worker/package_linux64/build/src/interpreter.c:815
jl_toplevel_eval_flex at /buildworker/worker/package_linux64/build/src/toplevel.c:805
jl_toplevel_eval_flex at /buildworker/worker/package_linux64/build/src/toplevel.c:754
jl_toplevel_eval_in at /buildworker/worker/package_linux64/build/src/builtins.c:622
eval at ./boot.jl:319
jl_apply_generic at /buildworker/worker/package_linux64/build/src/gf.c:2184
macro expansion at ./logging.jl:317 [inlined]
exec_options at ./client.jl:219
_start at ./client.jl:425
jl_apply_generic at /buildworker/worker/package_linux64/build/src/gf.c:2184
jl_apply at /buildworker/worker/package_linux64/build/ui/../src/julia.h:1537 [inlined]
true_main at /buildworker/worker/package_linux64/build/ui/repl.c:112
main at /buildworker/worker/package_linux64/build/ui/repl.c:233
__libc_start_main at /lib64/libc.so.6 (unknown line)
_start at /local/scratch/bgodard/sw/julia-1.0.2/bin/julia (unknown line)
Allocations: 20833061 (Pool: 20832036; Big: 1025); GC: 45
ERROR: Package CALCEPH errored during testing


#2

FYI it also does not work with the simpler:

function userHandlerWrapper(msg::Cstring)::Cvoid
    return
end

userHandlerCWrapper = @cfunction(userHandlerWrapper, Cvoid, (Cstring,))

ie not using the user provided function.


#3

I found out a way to fix the problem:

It looks like the cfunction pointer is NULL:

julia> using CALCEPH

julia> CALCEPH.userHandlerCWrapper
Ptr{Nothing} @0x0000000000000000

but if I create again the cfunction in the REPL this time its adress is not null:

julia> @cfunction(CALCEPH.userHandlerWrapper, Cvoid, (Cstring,))
Ptr{Nothing} @0x00007fa7e5cce2e0

And if I redefine CALCEPH.setrErrorHandler to create the cfunction inside the method instead of outside, the method work as expected:


#userHandlerCWrapper = @cfunction(userHandlerWrapper, Cvoid, (Cstring,))
userHandlerCWrapper = Nothing

function setCustomHandler(f::Function)
    global userHandlerContainerInstance
    global userHandlerCWrapper
    userHandlerCWrapper = @cfunction(userHandlerWrapper, Cvoid, (Cstring,))
    userHandlerContainerInstance.f = f
    ccall((:calceph_seterrorhandler, libcalceph), Cvoid,
                  (Cint, Ptr{Cvoid}), 3, userHandlerCWrapper)
end

although I do not understand why I need to do this.