Segfault using Base.stdout on 0.7

Hi folks,

I have a very simple logging module which has a global variable that store the IO where messages will be printed to. I’m initializing it to Base.stdout. In 0.6, this worked fine. In 0.7, when I try to access the variable, I get a segfault (platform is Ubuntu 18.06). But if I do the same initialization from the REPL, everything works fine. Any ideas what the problem might be? Does it have to do with Base.stdout not being available at import time? Thanks,

Dara

MLog.jl

module MLog

out = Base.stdout

end
julia> import MLog

julia> MLog.out

signal (11): Segmentation fault
in expression starting at no file:0
uv_fileno at /buildworker/worker/package_linux64/build/deps/srccache/libuv-ed3700c849289ed01fe04273a7bf865340b2bd7e/src/unix/core.c:726
_fd at ./stream.jl:902 [inlined]
show at ./stream.jl:194 [inlined]
show at ./sysimg.jl:195
jl_fptr_trampoline at /buildworker/worker/package_linux64/build/src/gf.c:1829
jl_apply_generic at /buildworker/worker/package_linux64/build/src/gf.c:2182
display at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v0.7/REPL/src/REPL.jl:131
jl_apply_generic at /buildworker/worker/package_linux64/build/src/gf.c:2182
display at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v0.7/REPL/src/REPL.jl:135
jl_apply_generic at /buildworker/worker/package_linux64/build/src/gf.c:2182
display at ./multimedia.jl:287
jl_fptr_trampoline at /buildworker/worker/package_linux64/build/src/gf.c:1829
jl_apply_generic at /buildworker/worker/package_linux64/build/src/gf.c:2182
jl_apply at /buildworker/worker/package_linux64/build/src/julia.h:1538 [inlined]
jl_f__apply at /buildworker/worker/package_linux64/build/src/builtins.c:563
jl_f__apply_latest at /buildworker/worker/package_linux64/build/src/builtins.c:601
#invokelatest#1 at ./essentials.jl:691 [inlined]
invokelatest at ./essentials.jl:690 [inlined]
print_response at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v0.7/REPL/src/REPL.jl:154
jfptr_print_response_12102.clone_1 at /zebrium/juliapro/julia-0.7.0/lib/julia/sys.so (unknown line)
print_response at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v0.7/REPL/src/REPL.jl:139
jfptr_print_response_12097.clone_1 at /zebrium/juliapro/julia-0.7.0/lib/julia/sys.so (unknown line)
jl_apply_generic at /buildworker/worker/package_linux64/build/src/gf.c:2182
do_respond at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v0.7/REPL/src/REPL.jl:708
jfptr_do_respond_9991.clone_1 at /zebrium/juliapro/julia-0.7.0/lib/julia/sys.so (unknown line)
jl_apply_generic at /buildworker/worker/package_linux64/build/src/gf.c:2182
jl_apply at /buildworker/worker/package_linux64/build/src/julia.h:1538 [inlined]
jl_f__apply at /buildworker/worker/package_linux64/build/src/builtins.c:563
jl_f__apply_latest at /buildworker/worker/package_linux64/build/src/builtins.c:601
#invokelatest#1 at ./essentials.jl:691 [inlined]
invokelatest at ./essentials.jl:690 [inlined]
run_interface at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v0.7/REPL/src/LineEdit.jl:2261
jl_apply_generic at /buildworker/worker/package_linux64/build/src/gf.c:2182
run_frontend at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v0.7/REPL/src/REPL.jl:1029
run_repl at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v0.7/REPL/src/REPL.jl:191
jl_apply_generic at /buildworker/worker/package_linux64/build/src/gf.c:2182
#831 at ./logging.jl:311
jfptr_#831_5797.clone_1 at /zebrium/juliapro/julia-0.7.0/lib/julia/sys.so (unknown line)
jl_apply_generic at /buildworker/worker/package_linux64/build/src/gf.c:2182
jl_apply at /buildworker/worker/package_linux64/build/src/julia.h:1538 [inlined]
jl_f__apply at /buildworker/worker/package_linux64/build/src/builtins.c:563
jl_f__apply_latest at /buildworker/worker/package_linux64/build/src/builtins.c:601
#invokelatest#1 at ./essentials.jl:691 [inlined]
invokelatest at ./essentials.jl:690 [inlined]
macro expansion at ./logging.jl:308 [inlined]
run_main_repl at ./client.jl:340
exec_options at ./client.jl:252
_start at ./client.jl:432
jl_apply_generic at /buildworker/worker/package_linux64/build/src/gf.c:2182
unknown function (ip: 0x401af8)
unknown function (ip: 0x401523)
__libc_start_main at /lib/x86_64-linux-gnu/libc.so.6 (unknown line)
unknown function (ip: 0x4015c4)
Allocations: 216623 (Pool: 216540; Big: 83); GC: 0
Segmentation fault (core dumped)

This is just a (hopefully educated) guess, but I suspect that the problem is due to pre-compilation being enabled by default in v0.7, which was not the case in 0.6. In Julia 0.7, your module is setting out to Base.stdout during precompilation, which likely contains a pointer which is no longer valid when you load that module later on. Fortunately, if this is the case then it’s easy to fix. Can you try the following:

module MLog

const out = Ref(stdout)

function __init__()  # __init__ is a special function which is called when your function is loaded
  out[] = stdout  # reset `out` to the valid stdout
end

end

The only thing you’ll need to do is change any other usages of out to out[].

This should even make your code perform better than before, since the global variable out can now be marked const and therefore have a stable and inferrable type.

1 Like

Thanks - that indeed does the trick.

It’s even easier because the actual code has an Array of IO, so I can just initialize it within init().

Actually, one quick followup - is there a way to use stdout (or STDOUT) on both 0.6 and 0.7 without generating deprecation warnings? I’ve tried various combinations of ‘using Compat’, but while it works for modules that have been moved, I’ve not figured out how to use it for renamed variables. Thanks again,

Dara

julia> using Compat.Base

julia> x = stdout
ERROR: UndefVarError: stdout not defined

It is non-trivial because the stdout binding is not constant. Something like

 getstdout() = @static if isdefined(Base, :stdout) stdout; else STDOUT; end

and use getstdout() might work.

Can’t you just do:

julia> using Compat

julia> Compat.stdout
Base.TTY(RawFD(13) open, 0 bytes waiting)

?

1 Like

Yes, that did it! Thanks a bunch.