lib/autobuild/progress_display.rb in autobuild-1.20.0 vs lib/autobuild/progress_display.rb in autobuild-1.21.0
- old
+ new
@@ -1,23 +1,35 @@
+require "concurrent/atomic/atomic_boolean"
+require "concurrent/array"
+
module Autobuild
# Management of the progress display
class ProgressDisplay
def initialize(io, color: ::Autobuild.method(:color))
@io = io
@cursor = TTY::Cursor
@last_formatted_progress = []
- @progress_messages = []
+ @progress_messages = Concurrent::Array.new
@silent = false
@color = color
@display_lock = Mutex.new
@next_progress_display = Time.at(0)
@progress_mode = :single_line
@progress_period = 0.1
+
+ @message_queue = Queue.new
+ @forced_progress_display = Concurrent::AtomicBoolean.new(false)
end
+ def synchronize(&block)
+ result = @display_lock.synchronize(&block)
+ refresh_display
+ result
+ end
+
# Set the minimum time between two progress messages
#
# @see period
def progress_period=(period)
@progress_period = Float(period)
@@ -84,36 +96,28 @@
def message(message, *args, io: @io, force: false)
return if silent? && !force
io = args.pop if args.last.respond_to?(:to_io)
+ @message_queue << [message, args, io]
- @display_lock.synchronize do
- if @progress_mode == :single_line
- io.print @cursor.clear_screen_down
- end
- io.puts @color.call(message, *args)
-
- io.flush if @io != io
- display_progress
- @io.flush
- end
+ refresh_display
end
def progress_start(key, *args, done_message: nil)
progress_done(key)
formatted_message = @color.call(*args)
@progress_messages << [key, formatted_message]
if progress_enabled?
- @display_lock.synchronize do
- display_progress(consider_period: false)
- end
+ @forced_progress_display.make_true
else
message " #{formatted_message}"
end
+ refresh_display
+
if block_given?
begin
result = yield
progress_done(key, message: done_message)
result
@@ -123,47 +127,70 @@
end
end
end
def progress(key, *args)
- @display_lock.synchronize do
- found = false
- @progress_messages.map! do |msg_key, msg|
- if msg_key == key
- found = true
- [msg_key, @color.call(*args)]
- else
- [msg_key, msg]
- end
+ found = false
+ @progress_messages.map! do |msg_key, msg|
+ if msg_key == key
+ found = true
+ [msg_key, @color.call(*args)]
+ else
+ [msg_key, msg]
end
- @progress_messages << [key, @color.call(*args)] unless found
- display_progress
end
+ @progress_messages << [key, @color.call(*args)] unless found
+
+ refresh_display
end
def progress_done(key, display_last = true, message: nil)
- changed = @display_lock.synchronize do
- current_size = @progress_messages.size
- @progress_messages.delete_if do |msg_key, msg|
- if msg_key == key
- message = msg if display_last && !message
- true
- end
+ current_size = @progress_messages.size
+ @progress_messages.delete_if do |msg_key, msg|
+ if msg_key == key
+ message = msg if display_last && !message
+ true
end
- current_size != @progress_messages.size
end
+ changed = current_size != @progress_messages.size
if changed
if message
message(" #{message}")
- # Note: message calls display_progress already
+ # Note: message updates the display already
else
- @display_lock.synchronize do
- display_progress
- end
+ refresh_display
end
true
end
+ end
+
+ def refresh_display
+ return unless @display_lock.try_lock
+
+ begin
+ refresh_display_under_lock
+ ensure
+ @display_lock.unlock
+ end
+ end
+
+ def refresh_display_under_lock
+ # Display queued messages
+ until @message_queue.empty?
+ message, args, io = @message_queue.pop
+ if @progress_mode == :single_line
+ io.print @cursor.clear_screen_down
+ end
+ io.puts @color.call(message, *args)
+
+ io.flush if @io != io
+ end
+
+ # And re-display the progress
+ display_progress(consider_period: @forced_progress_display.false?)
+ @forced_progress_display.make_false
+ @io.flush
end
def display_progress(consider_period: true)
return unless progress_enabled?
return if consider_period && (@next_progress_display > Time.now)