printf() and stdio in the Julia runtime

Libuv wrappers for stdio

julia.h defines libuv wrappers for the stdio.h streams:


... and corresponding output functions:


These printf functions are used by julia/{src,ui}/*.c wherever stdio is needed to ensure that output buffering is handled in a unified way.

In special cases, like signal handlers, where the full libuv infrastructure is too heavy, jl_safe_printf() can be used to write(2) directly to STDERR_FILENO:


Interface between JL_STD* and Julia code

Base.STDIN, Base.STDOUT and Base.STDERR are bound to the JL_STD*libuv streams defined in the runtime.

Julia’s __init__() function (in base/sysimg.jl) calls reinit_stdio() (in base/stream.jl) to create Julia objects for Base.STDIN, Base.STDOUT and Base.STDERR.

reinit_stdio() uses ccall() to retrieve pointers to JL_STD* and calls jl_uv_handle_type() to inspect the type of each stream. It then creates a Julia Base.File, Base.TTY or Base.Pipe object to represent each stream, e.g.:

$ julia -e 'typeof((STDIN, STDOUT, STDERR))'(TTY,TTY,TTY)

$ julia -e 'println(typeof((STDIN, STDOUT, STDERR)))' < /dev/null 2>/dev/null

$ echo hello | julia -e 'println(typeof((STDIN, STDOUT, STDERR)))'| cat

The and Base.write() methods for these streams use ccall() to call libuv wrappers in src/jl_uv.c, e.g.:


printf() during initialisation

The libuv streams relied upon by jl_printf() etc., are not available until midway through initialisation of the runtime (see init.c, init_stdio()). Error messages or warnings that need to be printed before this are routed to the standard C library fwrite() function by the following mechanism:

In sys.c, the JL_STD* stream pointers are statically initialised to integer constants: STD*_FILENO(0,1and2). In jl_uv.c the jl_write() function checks its uv_stream_t*stream argument and calls fwrite() if stream is set to STDOUT_FILENO or STDERR_FILENO.

This allows for uniform use of jl_printf() throughout the runtime regardless of whether or not any particular piece of code is reachable before initialisation is complete.

Legacy ios.c library

The julia/src/support/ios.c library is inherited from femtolisp. It provides cross-platform buffered file IO and in-memory temporary buffers.

ios.c is still used by:

  • julia/src/flisp/*.c
  • julia/src/dump.c – for serialisation file IO and for memory buffers.
  • base/iostream.jl – for file IO (see base/fs.jl for libuv equivalent).

Use of ios.c in these modules is mostly self-contained and separated from the libuv I/O system. However, there is one place where femtolisp calls through to jl_printf() with a legacy ios_t stream.

There is a hack in ios.h that makes the field line up with the uv_stream_t.type and ensures that the values used for to not overlap with valid UV_HANDLE_TYPE values. This allows uv_stream_t pointers to point to ios_t streams.

This is needed because jl_printf() caller jl_static_show() is passed an ios_t stream by femtolisp’s fl_print() function. Julia’s jl_write() function has special handling for this: