ProgressBar: Delay before showing a new activity

Some activities are numerous but usually very short (e.g. copying a
source file to the store) which would cause a lot of flickering. So
only show activities that have been running for at least 10 ms.
This commit is contained in:
Eelco Dolstra 2022-08-12 15:56:08 +02:00
parent e62160579f
commit c3769c6846

View file

@ -8,6 +8,7 @@
#include <map> #include <map>
#include <thread> #include <thread>
#include <iostream> #include <iostream>
#include <chrono>
namespace nix { namespace nix {
@ -48,6 +49,7 @@ private:
bool visible = true; bool visible = true;
ActivityId parent; ActivityId parent;
std::optional<std::string> name; std::optional<std::string> name;
std::chrono::time_point<std::chrono::steady_clock> startTime;
}; };
struct ActivitiesByType struct ActivitiesByType
@ -91,10 +93,11 @@ public:
state_.lock()->active = isTTY; state_.lock()->active = isTTY;
updateThread = std::thread([&]() { updateThread = std::thread([&]() {
auto state(state_.lock()); auto state(state_.lock());
auto nextWakeup = std::chrono::milliseconds::max();
while (state->active) { while (state->active) {
if (!state->haveUpdate) if (!state->haveUpdate)
state.wait(updateCV); state.wait_for(updateCV, nextWakeup);
draw(*state); nextWakeup = draw(*state);
state.wait_for(quitCV, std::chrono::milliseconds(50)); state.wait_for(quitCV, std::chrono::milliseconds(50));
} }
}); });
@ -118,7 +121,8 @@ public:
updateThread.join(); updateThread.join();
} }
bool isVerbose() override { bool isVerbose() override
{
return printBuildLogs; return printBuildLogs;
} }
@ -159,11 +163,13 @@ public:
if (lvl <= verbosity && !s.empty() && type != actBuildWaiting) if (lvl <= verbosity && !s.empty() && type != actBuildWaiting)
log(*state, lvl, s + "..."); log(*state, lvl, s + "...");
state->activities.emplace_back(ActInfo()); state->activities.emplace_back(ActInfo {
.s = s,
.type = type,
.parent = parent,
.startTime = std::chrono::steady_clock::now()
});
auto i = std::prev(state->activities.end()); auto i = std::prev(state->activities.end());
i->s = s;
i->type = type;
i->parent = parent;
state->its.emplace(act, i); state->its.emplace(act, i);
state->activitiesByType[type].its.emplace(act, i); state->activitiesByType[type].its.emplace(act, i);
@ -327,10 +333,12 @@ public:
updateCV.notify_one(); updateCV.notify_one();
} }
void draw(State & state) std::chrono::milliseconds draw(State & state)
{ {
auto nextWakeup = std::chrono::milliseconds::max();
state.haveUpdate = false; state.haveUpdate = false;
if (!state.active) return; if (!state.active) return nextWakeup;
std::string line; std::string line;
@ -341,12 +349,25 @@ public:
line += "]"; line += "]";
} }
auto now = std::chrono::steady_clock::now();
if (!state.activities.empty()) { if (!state.activities.empty()) {
if (!status.empty()) line += " "; if (!status.empty()) line += " ";
auto i = state.activities.rbegin(); auto i = state.activities.rbegin();
while (i != state.activities.rend() && (!i->visible || (i->s.empty() && i->lastLine.empty()))) while (i != state.activities.rend()) {
if (i->visible && (!i->s.empty() || !i->lastLine.empty())) {
/* Don't show activities until some time has
passed, to avoid displaying very short
activities. */
auto delay = std::chrono::milliseconds(10);
if (i->startTime + delay < now)
break;
else
nextWakeup = std::min(nextWakeup, std::chrono::duration_cast<std::chrono::milliseconds>(delay - (now - i->startTime)));
}
++i; ++i;
}
if (i != state.activities.rend()) { if (i != state.activities.rend()) {
line += i->s; line += i->s;
@ -366,6 +387,8 @@ public:
if (width <= 0) width = std::numeric_limits<decltype(width)>::max(); if (width <= 0) width = std::numeric_limits<decltype(width)>::max();
writeToStderr("\r" + filterANSIEscapes(line, false, width) + ANSI_NORMAL + "\e[K"); writeToStderr("\r" + filterANSIEscapes(line, false, width) + ANSI_NORMAL + "\e[K");
return nextWakeup;
} }
std::string getStatus(State & state) std::string getStatus(State & state)