Engee documentation

printf() and stdio in the Julia runtime environment

libuv shells for stdio

julia.h defines shells https://docs.libuv.org [libuv] for stdio.h streams:

uv_stream_t *JL_STDIN;
uv_stream_t *JL_STDOUT;
uv_stream_t *JL_STDERR;

and the corresponding output functions:

int jl_printf(uv_stream_t *s, const char *format, ...);
int jl_vprintf(uv_stream_t *s, const char *format, va_list args);

These printf functions are used by .c files in the src/ and cli/ directories wherever stdio is required to provide uniform output buffering.

In special cases, when, for example, signal handlers are used and when the full libuv infrastructure is too large, the jl_safe_printf() function can be used to write (write(2)) directly in `STDERR_FILENO'.

void jl_safe_printf(const char *str, ...);

Interface between JL_STD* and Julia code

Constants Base.stdin, Base.stdout and Base.stderr are bound to libuv JL_STD* threads defined in the runtime environment.

Julia`__init__() function` (in base/sysimg.jl) calls reinit_stdio()' (in `base/stream.jl) to create Julia objects for constants Base.stdin, Base.stdout and Base.stderr.

The function reinit_stdio() uses the keyword 'ccall` to get pointers to the stream JL_STD* and calls jl_uv_handle_type() to check the type of each stream. Then she creates the Julia object Base.IOStream, Base.TTY or Base.PipeEndpoint to represent each stream, for example:

$ julia -e 'println(typeof((stdin, stdout, stderr)))'
Tuple{Base.TTY,Base.TTY,Base.TTY}

$ julia -e 'println(typeof((stdin, stdout, stderr)))' < /dev/null 2>/dev/null
Tuple{IOStream,Base.TTY,IOStream}

$ echo hello | julia -e 'println(typeof((stdin, stdout, stderr)))' | cat
Tuple{Base.PipeEndpoint,Base.PipeEndpoint,Base.TTY}

Methods Base.read and Base.write a keyword is used for these streams 'ccall` to call libuv shells in `src/jl_uv.c', for example:

stream.jl: function write(s::IO, p::Ptr, nb::Integer)
               -> ccall(:jl_uv_write, ...)
  jl_uv.c:          -> int jl_uv_write(uv_stream_t *stream, ...)
                        -> uv_write(uvw, stream, buf, ...)

printf() during initialization

libuv streams, which are relied on by the function jl_printf() and the like, are unavailable until the middle of runtime initialization (see the description of init.c, init_stdio()). Error messages or warnings that should be displayed before reaching this point are sent to the 'fwrite()` function of the C standard library using the following mechanism.

In sys.c, the stream pointers JL_STD* are statically initialized to integer constants: STD*_FILENO (0, 1 and 2)'. In `jl_uv.c the function 'jl_uv_puts()` checks its argument uv_stream_t* stream and calls 'fwrite()` if the stream has the value STDOUT_FILENO or STDERR_FILENO

This allows uniform use of the jl_printf() function throughout the execution time, regardless of the availability of any particular piece of code before initialization is completed.

Outdated library ios.c

The library src/support/ios.c is inherited from https://github.com/JeffBezanson/femtolisp [femtolisp]. It provides cross-platform buffered file I/O and provides temporary buffers in memory.

'ios.c` is still used by the following files.

  • src/flisp/*.c

  • `src/dump.c' — for serializing file I/O and buffers in memory.

  • `src/staticdata.c' — for serializing file I/O and buffers in memory.

  • base/iostream.jl — for file I/O (see base/fs.jl according to the libuv equivalent).

Using ios.The C in these modules is mostly self-contained and occurs separately from the libuv I/O system. However, there is https://github.com/JuliaLang/julia/blob/master/src/flisp/print.c#L654 [one place] where femtolisp calls jl_printf() with the outdated ios_t thread.

In ios.h there is a tool that attaches a field ios_t.bm ` to `uv_stream_t.type and ensures that the values used for ios_t.bm `, will not overlap with the valid values of `UV_HANDLE_TYPE'. This allows the `uv_stream_t pointers to point to the ios_t streams.

This is necessary because the fl_print()' femtolisp function passes the 'ios_t stream to the jl_printf() function, which calls jl_static_show(). The jl_uv_puts() function in Julia handles this moment in a special way.

if (stream->type > UV_HANDLE_TYPE_MAX) {
    return ios_write((ios_t*)stream, str, n);
}