Something went wrong. Try again.
The agentic engineering control plane for the posthuman future
Something went wrong. Try again.
4.0 kB · 147 lines
C++
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148#include "FuzzyRowProvider.h"
#include <QMetaObject>#include <QPointer>#include <QRunnable>#include <QThreadPool>#include <QTimer>
#include <utility>
namespace {
constexpr int kDebounceMs = 120;
} // namespace
FuzzyRowProvider::FuzzyRowProvider(QObject* parent) : QObject(parent), m_debounce(new QTimer(this)){ m_debounce->setSingleShot(true); m_debounce->setInterval(kDebounceMs); connect(m_debounce, &QTimer::timeout, this, &FuzzyRowProvider::startWorker); // The empty query is a legitimate first input (a fresh chooser pane lists its // root, a fresh path dialog lists the home directory), so kick one compute // without waiting for a property write. setQuery("") would be dropped by its // own dedupe. invalidate();}
FuzzyRowProvider::~FuzzyRowProvider(){ // A worker still holds this object while it reads m_inputLock, calls the // subclass's rowsForQuery, and posts its result back. Wait for every such // worker to finish, so none of them outlives the members it touches. The // work is bounded by the walk cap, so the wait stays short. QMutexLocker lock(&m_lifeLock); while (m_inFlight > 0) { m_lifeDone.wait(&m_lifeLock); }}
void FuzzyRowProvider::setQuery(const QString& query){ if (query == m_query) { return; } m_query = query; invalidate();}
void FuzzyRowProvider::invalidate(){ ++m_generation; m_debounce->start();}
void FuzzyRowProvider::setBusy(bool busy){ if (m_busy == busy) { return; } m_busy = busy; emit busyChanged();}
void FuzzyRowProvider::startWorker(){ const quint64 generation = m_generation; const QString query = m_query; setBusy(true); { // Snapshot GUI-thread inputs before the worker starts. Released before the // worker runs: a QMutex is only unlocked by the thread that locked it, so // the worker takes it again for its own rowsForQuery() call. QMutexLocker lock(&m_inputLock); captureInputs(); } const QPointer<FuzzyRowProvider> guard(this); { QMutexLocker lock(&m_lifeLock); ++m_inFlight; } QThreadPool::globalInstance()->start(QRunnable::create([guard, generation, query]() { FuzzyRowProvider* const self = guard.data(); if (self != nullptr) { QVariantList rows; QString note; { QMutexLocker lock(&self->m_inputLock); // The note is per result set, so a stale one never survives the query // that follows it. self->m_pendingNote.clear(); rows = self->rowsForQuery(query); note = self->m_pendingNote; } if (guard) { // Post the finished list back to the owner's thread. The context // object drops the call if the provider dies first. QMetaObject::invokeMethod( guard.data(), [guard, generation, rows = std::move(rows), note = std::move(note)]() mutable { if (guard) { guard->applyRows(generation, std::move(rows), std::move(note)); } }, Qt::QueuedConnection); } } // Release the object. The destructor waits on this count, so the members // read above are still alive here. if (self != nullptr) { QMutexLocker lock(&self->m_lifeLock); if (--self->m_inFlight == 0) { self->m_lifeDone.wakeAll(); } } }));}
void FuzzyRowProvider::applyRows(quint64 generation, QVariantList rows, QString note){ if (generation != m_generation) { return; // A newer query superseded this result. } m_rows = std::move(rows); if (m_note != note) { m_note = std::move(note); emit noteChanged(); } setBusy(false); emit rowsChanged();}
void FuzzyRowProvider::setNote(const QString& note){ // Called by the worker while it holds inputLock(), so the write needs no // further guard. m_pendingNote = note;}
QMutex& FuzzyRowProvider::inputLock(){ return m_inputLock;}