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 (seebase/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);
}