For this answer I wrote code like:
def show_wait_spinner
dirty = false
spinner = Thread.new{
loop{
print "*"
dirty = true
sleep 0.1
print "\b"
dirty = false
}
}
yield
spinner.kill
print "\b" if dirty
end
print "A"
show_wait_spinner{ sleep rand }
puts "B"
The goal is to ensure that the final output was "AB"
—to print a final "\b"
if it was not already printed by the thread. That code seems messy to me in Ruby where begin/rescue/ensure
exists. So I tried some other implementations of show_wait_spinner
; all of them fail to ensure that "AB"
is always the output, and never "A*B"
or "AB*"
.
Is there a cleaner, more Ruby-esque way to implement this logic?
Stop at end of loop via Mutex
def show_wait_spinner
stop = false
stopm = Mutex.new
spinner = Thread.new{
loop{
print "*"
sleep 0.1
print "\b"
stopm.synchronize{ break if stop }
}
}
yield
stopm.synchronize{ stop = true }
STDOUT.flush
end
…but my logic must be off, since this always results in "A*B".
Stop at end of loop via Thread-local variable
This second attempt results in sometimes "A*B" being printed, sometimes "AB":
def show_wait_spinner
stop = false
spinner = Thread.new{
Thread.current[:stop] = false
loop{
print "*"
sleep 0.1
print "\b"
stopm.synchronize{ break if Thread.current[:stop] }
}
}
yield
spinner[:stop] = true
STDOUT.flush
end
Kill and Ensure the Thread
def show_wait_spinner
spinner = Thread.new{
dirty = false
begin
loop{
print "*"
dirty = true
sleep 0.1
print "\b"
dirty = false
}
ensure
print "\b" if dirty
end
}
yield
spinner.kill
STDOUT.flush
end
Raise and Rescue the Thread
def show_wait_spinner
spinner = Thread.new{
dirty = false
begin
loop{
print "*"
dirty = true
sleep 0.1
print "\b"
dirty = false
}
rescue
puts "YAY"
print "\b" if dirty
end
}
yield
spinner.raise
STDOUT.flush
end