diff --git a/barkeep/barkeep.h b/barkeep/barkeep.h index 1f7ed4b..519e70e 100644 --- a/barkeep/barkeep.h +++ b/barkeep/barkeep.h @@ -257,7 +257,10 @@ class AsyncDisplayer { /// End the display. virtual void done() { if (not running()) { return; } // noop if already done() before - done_ = true; + { + std::lock_guard lock(done_m_); + done_ = true; + } done_cv_.notify_all(); join(); } @@ -614,29 +617,42 @@ class Speedometer { using Time = std::chrono::time_point; double progress_increment_sum_ = 0; // (weighted) sum of progress increments - Duration duration_increment_sum_{}; // (weighted) sum of duration increments + Duration duration_increment_sum_{}; // (weighted) sum of inter-increment gaps - Time last_start_time_; - ValueType last_progress_; + Time last_increment_time_; + ValueType last_progress_{}; public: double speed() { Time now = Clock::now(); - Duration dur = now - last_start_time_; - last_start_time_ = now; ValueType progress_copy = progress_provider_.load(); // to avoid progress_ changing below SignedType progress_increment = SignedType(progress_copy) - SignedType(last_progress_); - last_progress_ = progress_copy; - - progress_increment_sum_ = - (1 - discount_) * progress_increment_sum_ + progress_increment; - duration_increment_sum_ = (1 - discount_) * duration_increment_sum_ + dur; - return duration_increment_sum_.count() == 0 - ? 0 - : progress_increment_sum_ / duration_increment_sum_.count(); + + if (progress_increment != 0) { + Duration gap = now - last_increment_time_; + progress_increment_sum_ = + (1 - discount_) * progress_increment_sum_ + progress_increment; + duration_increment_sum_ = (1 - discount_) * duration_increment_sum_ + gap; + last_progress_ = progress_copy; + last_increment_time_ = now; + } + + if (duration_increment_sum_.count() == 0) { return 0; } + double base_speed = + progress_increment_sum_ / duration_increment_sum_.count(); + + // If we've been waiting longer than the EWMA inter-increment gap implies, + // the true speed must be at most 1/current_wait in magnitude, otherwise + // we would have already seen another increment. Clamp accordingly so the + // reported speed decays when increments stop. + Duration current_wait = now - last_increment_time_; + if (std::abs(base_speed) * current_wait.count() > 1.0) { + return std::copysign(1.0 / current_wait.count(), base_speed); + } + return base_speed; } /// Write speed to given output stream. Speed is a double (written with @@ -659,10 +675,10 @@ class Speedometer { *out << s; } - /// Start computing the speed based on the amount of change in progress. + /// Start computing the speed based on time between progress increments. void start() { last_progress_ = progress_provider_.load(); - last_start_time_ = Clock::now(); + last_increment_time_ = Clock::now(); } /// Constructor. @@ -1189,7 +1205,6 @@ class CompositeDisplay : public BaseDisplay { display->displayer_->out(&front->out()); } displayer_->parent(this); - // show(); } ~CompositeDisplay() { diff --git a/tests/demo.cpp b/tests/demo.cpp index d81b69a..8ce51e5 100644 --- a/tests/demo.cpp +++ b/tests/demo.cpp @@ -13,7 +13,7 @@ const static std::string reset = "\033[0m"; const static std::string bold = "\033[1m"; const static std::string dim = "\033[2m"; -std::vector> speeds{std::nullopt, 0, 0.1, 1}; +std::vector> speeds{std::nullopt, 0, 0.1}; class Demo { private: