Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Crystal.print_buffered(io) and Crystal.print_error_buffered #15343

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions src/crystal/print_buffered.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
module Crystal
# Prepares an error message, with an optional exception or backtrace, to an
# in-memory buffer, before writing to an IO, usually STDERR, in a single write
# operation.
#
# Avoids intermingled messages caused by multiple threads writing to a STDIO
# in parallel. This may still happen, since writes may not be atomic when the
# overall size is larger than PIPE_BUF, buf it should at least write 512 bytes
# atomically.
def self.print_buffered(message : String, *args, to io : IO, exception = nil, backtrace = nil) : Nil
buf = buffered_message(message, *args, exception: exception, backtrace: backtrace)
io.write(buf.to_slice)
io.flush unless io.sync?
end

# Identical to `#print_buffered` but eventually calls `System.print_error(bytes)`
# to write to stderr without going through the event loop.
def self.print_error_buffered(message : String, *args, exception = nil, backtrace = nil) : Nil
buf = buffered_message(message, *args, exception: exception, backtrace: backtrace)
System.print_error(buf.to_slice)
end

private def self.buffered_message(message : String, *args, exception = nil, backtrace = nil)
buf = IO::Memory.new(4096)

if args.empty?
buf << message
else
System.printf(message, *args) { |bytes| buf.write(bytes) }
end

if exception
buf << ": "
exception.inspect_with_backtrace(buf)
else
buf.puts
backtrace.try(&.each { |line| buf << " from " << line << '\n' })
end

buf
end
end
15 changes: 3 additions & 12 deletions src/fiber.cr
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require "crystal/system/thread_linked_list"
require "crystal/print_buffered"
require "./fiber/context"

# :nodoc:
Expand Down Expand Up @@ -147,21 +148,11 @@ class Fiber
GC.unlock_read
@proc.call
rescue ex
io = {% if flag?(:preview_mt) %}
IO::Memory.new(4096) # PIPE_BUF
{% else %}
STDERR
{% end %}
if name = @name
io << "Unhandled exception in spawn(name: " << name << "): "
Crystal.print_buffered("Unhandled exception in spawn(name: %s)", name, exception: ex, to: STDERR)
else
io << "Unhandled exception in spawn: "
Crystal.print_buffered("Unhandled exception in spawn", exception: ex, to: STDERR)
end
ex.inspect_with_backtrace(io)
{% if flag?(:preview_mt) %}
STDERR.write(io.to_slice)
{% end %}
STDERR.flush
ensure
# Remove the current fiber from the linked list
Fiber.inactive(self)
Expand Down
Loading