From 43654cbbcc2f47acf2150d4101584bd6654241a2 Mon Sep 17 00:00:00 2001 From: Austin Blatt Date: Fri, 28 Jul 2017 13:53:14 -0700 Subject: [PATCH 1/2] Initial thoughts --- lib/tty/spinner.rb | 5 +++++ lib/tty/spinner/multi.rb | 2 ++ 2 files changed, 7 insertions(+) diff --git a/lib/tty/spinner.rb b/lib/tty/spinner.rb index 9579973..be86c08 100644 --- a/lib/tty/spinner.rb +++ b/lib/tty/spinner.rb @@ -164,6 +164,11 @@ def start # # @api public def auto_spin + # TODO: this should probably be the same lock (will need to be a Monitor + # rather than a Mutex) as execute_on_line, so that a spinner starts + # completely before another tries to redraw itself. Also, spin needs to + # run once before a spinner is fully started, so we should probably run + # it once, and then start the Thread. SPINNER_START_LOCK.synchronize do start sleep_time = 1.0 / @interval diff --git a/lib/tty/spinner/multi.rb b/lib/tty/spinner/multi.rb index eff1ac2..6bf55db 100644 --- a/lib/tty/spinner/multi.rb +++ b/lib/tty/spinner/multi.rb @@ -36,6 +36,8 @@ def initialize(options = {}) # @api public def register(pattern, options = {}) spinner = TTY::Spinner.new(pattern, @options.merge(options)) + + # TODO: If two threads add a spinner at the same time, @spinners.length will be the same for both spinner.add_multispinner(self, @spinners.length) @spinners << spinner spinner From 7602c94749aa2250471f1b4b494e41e341279a75 Mon Sep 17 00:00:00 2001 From: Austin Blatt Date: Fri, 28 Jul 2017 14:03:47 -0700 Subject: [PATCH 2/2] Attempt to fix locks --- lib/tty/spinner.rb | 17 +++++++---------- lib/tty/spinner/multi.rb | 10 +++++++--- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/lib/tty/spinner.rb b/lib/tty/spinner.rb index be86c08..dfc46e7 100644 --- a/lib/tty/spinner.rb +++ b/lib/tty/spinner.rb @@ -27,6 +27,8 @@ class Spinner TICK = '✔'.freeze CROSS = '✖'.freeze + CURSOR_USAGE_LOCK = Monitor.new + # The object that responds to print call defaulting to stderr # # @api public @@ -159,20 +161,17 @@ def start reset end - SPINNER_START_LOCK = Mutex.new # Start automatic spinning animation # # @api public def auto_spin - # TODO: this should probably be the same lock (will need to be a Monitor - # rather than a Mutex) as execute_on_line, so that a spinner starts - # completely before another tries to redraw itself. Also, spin needs to - # run once before a spinner is fully started, so we should probably run - # it once, and then start the Thread. - SPINNER_START_LOCK.synchronize do + CURSOR_USAGE_LOCK.synchronize do start sleep_time = 1.0 / @interval + + spin @thread = Thread.new do + sleep(sleep_time) while @started_at spin sleep(sleep_time) @@ -344,11 +343,9 @@ def reset private - LOCK = Mutex.new - def execute_on_line if @multispinner - LOCK.synchronize do + CURSOR_USAGE_LOCK.synchronize do lines_up = @multispinner.count_line_offset(@index) if @first_run diff --git a/lib/tty/spinner/multi.rb b/lib/tty/spinner/multi.rb index 6bf55db..0048fb7 100644 --- a/lib/tty/spinner/multi.rb +++ b/lib/tty/spinner/multi.rb @@ -20,6 +20,8 @@ class Multi def initialize(options = {}) @options = options + @create_spinner_lock = Mutex.new + @spinners = [] @callbacks = { success: [], @@ -37,9 +39,11 @@ def initialize(options = {}) def register(pattern, options = {}) spinner = TTY::Spinner.new(pattern, @options.merge(options)) - # TODO: If two threads add a spinner at the same time, @spinners.length will be the same for both - spinner.add_multispinner(self, @spinners.length) - @spinners << spinner + @create_spinner_lock.synchronize do + spinner.add_multispinner(self, @spinners.length) + @spinners << spinner + end + spinner end