Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor to reduce dependancy on ios.c
Rationale: Unravel ios.c’s legacy role as debug io library for the runtime; vs current role as backend for iostream.jl. Clean up historical debug printf functions: Replace calls to ios_printf(), ios_puts(), JL_PRINTF, JL_PUTS etc with jl_printf(), jl_puts()... Replace exit() with jl_exit() in repl.c, dump.c and init.c. Make jl_printf() and jl_exit() safe in early initialisation. Remove ios.c dependance from ast.c and builtins.c: src/flisp/flisp.c: New fl_load_system_image_str(char*, size_t) Hides detail of ios_static_buffer from ast.c ios_* no longer used in ast.c. src/builtins.c In jl_errorf(), replace ios_vprintf() with vasprintf() (following the precedent set by src/jl_uv.c:663). Use free() not LLT_FREE() to clean up after vasprintf() in jl_uv.c: man vasprintf says “asprintf() and vasprintf() dynamically allocate a new string with malloc(3)” Refactor pairs of calls to jl_printf() & jl_exit(1) into calls to jl_error[f](), which does the same thing during early init. Dead code removal: jl_uv.c: jl_bufptr(), jl_ios_buf_base(), jl_putc(), jl_pututf8() sys.c: jl_ios_size() New jl_safe_printf(). jl_safe_printf() uses a statically allocated 1000 byte buffer to vnsprintf() a message then calls write(2) to send it to stderr. jl_safe_printf() is intended to be used in places where there is the potential for out of memory, corrupted stack, re-entrancy etc. The current jl_safe_printf() implementation is not 100% safe in all situations, but it should be pretty good most of the time, especially if the formatting is limited to %d. The safety of jl_safe_printf() could be improved later, but at least for now the places were safety might matter are identified by its use. jl_safe_printf() is used in: - sigdie_handler() - sigint_handler() - _exception_handler() - catch_exception_raise() - gdblookup() Thanks to @vtjnash for assistance and feedback with these changes. Dead code cleanup & jl_write() simplificaiton. Clean up dead code: stream.jl : write!() — My apologies if this is used somewhere, I could’t find any doc or callers. builtins.c : jl_print_symbol(), jl_print_int64() ccall.cpp : if(0 …) jl_puts() jl_uv.c : jl_puts Replace 2 remaining calls to jl_puts() with calls to jl_printf() for consistency. builtins.c : jl_error() repl.c : true_main() Move jl_uv_writecb() implementation from stream.jl to jl_uv.c. This callback is only used by jl_write(), which is only called from jl_vprintf() in runtime C code. This helps to de-tangle fs.jl and stream.jl from the runtime's printf(). Use jl_safe_printf() to attempt error message output in jl_uv_writecb(). If this callback is printing an error, there was a problem writing to JL_STDOUT or JL_STDERR, so its best to try the simpelest means possible to get the error out. update doc/devdocs/stdio.rst to remove reference to: jl_write(), jl_putc(), jl_puts(). Simplify handling of STDOUT and STDERR early in initialisation: Instead of calling fwrite(), just call uv_default_loop() to get the jl_io_loop and then call jl_fs_write as usual. This means that all jl_printf()s now go through uvlib. There is a chance that this won’t work in win32, AppVeyor will tell... Remove C interface for single character writes: - jl_fs_write_byte() - jl_putc_copy() - jl_pututf8_copy() Hadnle single character writes in Julia: - write(f::File, c::UInt8) = write(f, [c]) - write(s::AsyncStream, b::UInt8) = write(s, [b]) - write(s::AsyncStream, c::Char) = write(s, string(c)) Rationale: - Writing to a libuv stream involves: "Julia does C malloc, Julia calls C, C calls libuv, libuv calls C callback, C callback calls julia hook, C callcback does c_free". Having less special cases of this makes it easier to reason about correctness. - Whatever overhead there might be in creating the temporary 1 element array (or string); it is small compared to all the machinery in libuv, and the OS that actually makes the single character write happen. - If a user is doing single character writes to a libuv stream, they either don’t care about performance, or they should be buffering in Julia before calling write(). Inline jl_write_copy() into only remaining called: jl_write(): - This is now the only place that needs to know about the trick used to store a copy of the output string after the request struct (i.e. malloc(sizeof(uv_write_t) + n)); Removed dead code: - stream.jl : make_stdout_stream() - stream.jl : jl_write_copy() Rename: - jl_write_no_copy() -> jl_uv_write() Move common code from stream.jl write() functions into uv_write macro: - ccall to jl_uv_write() (was jl_write_no_copy). - wait for current task Simplify uv_write memory management: Try to keep all the state management in one place (in macto uv_write()). Move free() of uv_write_t request object from C callback jl_uv_writecb_task() to macro uv_write(). This puts the malloc() and the free() in the same place. Move uv_req_set_data(uvw,C_NULL) from C jl_uv_write() to macro uv_write(). In _uv_hook_writecb_task() assert that uv_req_data(req) is not NULL. libuv should not call the callback from within the call to uv_write(), libuv has an internal “write_completed_queue” that is pumped by the main loop.
- Loading branch information