mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-06-01 21:21:15 -03:00
Introduce debounce_t
debounce_t will be used to limit thread creation from background highlighting and autosuggestion scenarios. This is a one-element queue backed by a single thread. New requests displace any existing queued request; this reflects the fact that autosuggestions and highlighting only care about the most recent result. A timeout allows for abandoning hung threads, which may happen if you attempt to e.g. access a dead hard-mounted NFS server. We don't want this to defeat autosuggestions and highlighting permanently, so allow spawning a new thread after the timeout (here 500 ms).
This commit is contained in:
@@ -881,6 +881,100 @@ static void test_pthread() {
|
||||
do_test(val == 5);
|
||||
}
|
||||
|
||||
static void test_debounce() {
|
||||
say(L"Testing debounce");
|
||||
// Run 8 functions using a condition variable.
|
||||
// Only the first and last should run.
|
||||
debounce_t db;
|
||||
constexpr size_t count = 8;
|
||||
std::array<bool, count> handler_ran = {};
|
||||
std::array<bool, count> completion_ran = {};
|
||||
|
||||
bool ready_to_go = false;
|
||||
std::mutex m;
|
||||
std::condition_variable cv;
|
||||
|
||||
// "Enqueue" all functions. Each one waits until ready_to_go.
|
||||
for (size_t idx = 0; idx < count; idx++) {
|
||||
do_test(handler_ran[idx] == false);
|
||||
db.perform(
|
||||
[&, idx] {
|
||||
std::unique_lock<std::mutex> lock(m);
|
||||
cv.wait(lock, [&] { return ready_to_go; });
|
||||
handler_ran[idx] = true;
|
||||
return idx;
|
||||
},
|
||||
[&](size_t idx) { completion_ran[idx] = true; });
|
||||
}
|
||||
|
||||
// We're ready to go.
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m);
|
||||
ready_to_go = true;
|
||||
}
|
||||
cv.notify_all();
|
||||
|
||||
// Wait until the last completion is done.
|
||||
while (!completion_ran.back()) {
|
||||
iothread_service_completion();
|
||||
}
|
||||
iothread_drain_all();
|
||||
|
||||
// Each perform() call may displace an existing queued operation.
|
||||
// Each operation waits until all are queued.
|
||||
// Therefore we expect the last perform() to have run, and at most one more.
|
||||
|
||||
do_test(handler_ran.back());
|
||||
do_test(completion_ran.back());
|
||||
|
||||
size_t total_ran = 0;
|
||||
for (size_t idx = 0; idx < count; idx++) {
|
||||
total_ran += (handler_ran[idx] ? 1 : 0);
|
||||
do_test(handler_ran[idx] == completion_ran[idx]);
|
||||
}
|
||||
do_test(total_ran <= 2);
|
||||
}
|
||||
|
||||
static void test_debounce_timeout() {
|
||||
using namespace std::chrono;
|
||||
say(L"Testing debounce timeout");
|
||||
|
||||
// Verify that debounce doesn't wait forever.
|
||||
// Use a shared_ptr so we don't have to join our threads.
|
||||
const long timeout_ms = 50;
|
||||
struct data_t {
|
||||
debounce_t db{timeout_ms};
|
||||
bool exit_ok = false;
|
||||
std::mutex m;
|
||||
std::condition_variable cv;
|
||||
relaxed_atomic_t<uint32_t> running{0};
|
||||
};
|
||||
auto data = std::make_shared<data_t>();
|
||||
|
||||
// Our background handler. Note this just blocks until exit_ok is set.
|
||||
std::function<void()> handler = [data] {
|
||||
data->running++;
|
||||
std::unique_lock<std::mutex> lock(data->m);
|
||||
data->cv.wait(lock, [&] { return data->exit_ok; });
|
||||
};
|
||||
|
||||
// Spawn the handler twice. This should not modify the thread token.
|
||||
uint64_t token1 = data->db.perform(handler);
|
||||
uint64_t token2 = data->db.perform(handler);
|
||||
do_test(token1 == token2);
|
||||
|
||||
// Wait 75 msec, then enqueue something else; this should spawn a new thread.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(timeout_ms + timeout_ms / 2));
|
||||
do_test(data->running == 1);
|
||||
uint64_t token3 = data->db.perform(handler);
|
||||
do_test(token3 > token2);
|
||||
|
||||
// Release all the threads.
|
||||
std::unique_lock<std::mutex> lock(data->m);
|
||||
data->exit_ok = true;
|
||||
data->cv.notify_all();
|
||||
}
|
||||
|
||||
static parser_test_error_bits_t detect_argument_errors(const wcstring &src) {
|
||||
parse_node_tree_t tree;
|
||||
if (!parse_tree_from_string(src, parse_flag_none, &tree, NULL, symbol_argument_list)) {
|
||||
@@ -5619,6 +5713,8 @@ int main(int argc, char **argv) {
|
||||
if (should_test_function("fd_monitor")) test_fd_monitor();
|
||||
if (should_test_function("iothread")) test_iothread();
|
||||
if (should_test_function("pthread")) test_pthread();
|
||||
if (should_test_function("debounce")) test_debounce();
|
||||
if (should_test_function("debounce")) test_debounce_timeout();
|
||||
if (should_test_function("parser")) test_parser();
|
||||
if (should_test_function("cancellation")) test_cancellation();
|
||||
if (should_test_function("indents")) test_indents();
|
||||
|
||||
Reference in New Issue
Block a user