diff --git a/examples/triage/exports.cpp b/examples/triage/exports.cpp index 4d274b1a22..0454714436 100644 --- a/examples/triage/exports.cpp +++ b/examples/triage/exports.cpp @@ -25,13 +25,10 @@ GenericExportsModel::GenericExportsModel(QWidget* parent, BinaryViewRef data): Q } m_updateTimer = new QTimer(this); - m_updateTimer->setSingleShot(true); m_updateTimer->setInterval(500); connect(m_updateTimer, &QTimer::timeout, this, &GenericExportsModel::updateModel); - connect(this, &GenericExportsModel::modelUpdate, this, [=, this]() { - if (m_updateTimer->isActive()) - return; - m_updateTimer->start(); + connect(this, &GenericExportsModel::updateTimerOnUIThread, this, [=, this]() { + updateTimer(m_needsUpdate); }); m_data->RegisterNotification(this); @@ -49,6 +46,10 @@ GenericExportsModel::~GenericExportsModel() void GenericExportsModel::updateModel() { + if (!m_needsUpdate) + return; + + setNeedsUpdate(false); beginResetModel(); m_allEntries.clear(); for (auto& sym : m_data->GetSymbolsOfType(FunctionSymbol)) @@ -237,40 +238,64 @@ void GenericExportsModel::setFilter(const std::string& filterText) endResetModel(); } - -void GenericExportsModel::OnAnalysisFunctionAdded(BinaryNinja::BinaryView* view, BinaryNinja::Function* func) +void GenericExportsModel::setNeedsUpdate(bool needed) { - emit modelUpdate(); + if (m_needsUpdate.exchange(needed) == needed) + return; + + updateTimer(needed); } +void GenericExportsModel::updateTimer(bool needsUpdate) +{ + if (needsUpdate && !m_updateTimer->isActive()) + m_updateTimer->start(); + if (!needsUpdate && m_updateTimer->isActive()) + m_updateTimer->stop(); +} -void GenericExportsModel::OnAnalysisFunctionRemoved(BinaryNinja::BinaryView* view, BinaryNinja::Function* func) +void GenericExportsModel::pauseUpdates() { - emit modelUpdate(); + m_updatesPaused = true; + setNeedsUpdate(false); } +void GenericExportsModel::resumeUpdates() +{ + m_updatesPaused = false; + setNeedsUpdate(true); +} -void GenericExportsModel::OnAnalysisFunctionUpdated(BinaryNinja::BinaryView* view, BinaryNinja::Function* func) +void GenericExportsModel::onBinaryViewNotification() { - emit modelUpdate(); + if (m_updatesPaused) + return; + + // This can be called from any thread so we cannot directly + // update the timer. Emitting a signal is relatively expensive + // given how frequently we receive notifications, so we only + // emit a signal if we didn't already need an update. + if (!m_needsUpdate.exchange(true)) + emit updateTimerOnUIThread(); } void GenericExportsModel::OnSymbolAdded(BinaryNinja::BinaryView* view, BinaryNinja::Symbol* sym) { - emit modelUpdate(); + if ((sym->GetBinding() == GlobalBinding) || (sym->GetBinding() == WeakBinding)) + onBinaryViewNotification(); } void GenericExportsModel::OnSymbolUpdated(BinaryNinja::BinaryView* view, BinaryNinja::Symbol* sym) { - emit modelUpdate(); + onBinaryViewNotification(); } void GenericExportsModel::OnSymbolRemoved(BinaryNinja::BinaryView* view, BinaryNinja::Symbol* sym) { - emit modelUpdate(); + onBinaryViewNotification(); } @@ -403,6 +428,19 @@ void ExportsTreeView::keyPressEvent(QKeyEvent* event) QTreeView::keyPressEvent(event); } +void ExportsTreeView::showEvent(QShowEvent* event) +{ + QTreeView::showEvent(event); + m_model->resumeUpdates(); +} + + +void ExportsTreeView::hideEvent(QHideEvent* event) +{ + QTreeView::hideEvent(event); + m_model->pauseUpdates(); +} + ExportsWidget::ExportsWidget(QWidget* parent, TriageView* view, BinaryViewRef data) : QWidget(parent) { diff --git a/examples/triage/exports.h b/examples/triage/exports.h index 6ab94407e2..96c2b77978 100644 --- a/examples/triage/exports.h +++ b/examples/triage/exports.h @@ -13,16 +13,25 @@ class GenericExportsModel : public QAbstractItemModel, public BinaryNinja::Binar BinaryViewRef m_data; std::vector m_allEntries, m_entries; std::string m_filter; + QTimer* m_updateTimer; Qt::SortOrder m_sortOrder; int m_sortCol; bool m_hasOrdinals; - QTimer* m_updateTimer; + + // Read from arbitrary threads while processing notifications. + std::atomic m_updatesPaused = false; + // Read/written from arbitrary threads while processing notifications. + std::atomic m_needsUpdate = true; void performSort(int col, Qt::SortOrder order); void updateModel(); - signals: - void modelUpdate(); + void updateTimer(bool); + void setNeedsUpdate(bool); + void onBinaryViewNotification(); + +signals: + void updateTimerOnUIThread(); public: GenericExportsModel(QWidget* parent, BinaryViewRef data); @@ -37,11 +46,11 @@ class GenericExportsModel : public QAbstractItemModel, public BinaryNinja::Binar virtual void sort(int col, Qt::SortOrder order) override; void setFilter(const std::string& filterText); + void pauseUpdates(); + void resumeUpdates(); + SymbolRef getSymbol(const QModelIndex& index); - virtual void OnAnalysisFunctionAdded(BinaryNinja::BinaryView* view, BinaryNinja::Function* func) override; - virtual void OnAnalysisFunctionRemoved(BinaryNinja::BinaryView* view, BinaryNinja::Function* func) override; - virtual void OnAnalysisFunctionUpdated(BinaryNinja::BinaryView* view, BinaryNinja::Function* func) override; virtual void OnSymbolAdded(BinaryNinja::BinaryView* view, BinaryNinja::Symbol* sym) override; virtual void OnSymbolUpdated(BinaryNinja::BinaryView* view, BinaryNinja::Symbol* sym) override; virtual void OnSymbolRemoved(BinaryNinja::BinaryView* view, BinaryNinja::Symbol* sym) override; @@ -75,6 +84,8 @@ class ExportsTreeView : public QTreeView, public FilterTarget protected: virtual void keyPressEvent(QKeyEvent* event) override; + virtual void showEvent(QShowEvent* event) override; + virtual void hideEvent(QHideEvent* event) override; private Q_SLOTS: void exportSelected(const QModelIndex& cur, const QModelIndex& prev);