diff options
Diffstat (limited to 'stats')
-rw-r--r-- | stats/statsview.cpp | 58 | ||||
-rw-r--r-- | stats/statsview.h | 8 |
2 files changed, 55 insertions, 11 deletions
diff --git a/stats/statsview.cpp b/stats/statsview.cpp index eb51810f8..5514aa35d 100644 --- a/stats/statsview.cpp +++ b/stats/statsview.cpp @@ -89,18 +89,20 @@ using ZNode = HideableQSGNode<QSGNode>; class RootNode : public QSGNode { public: - RootNode(QQuickWindow *w); + RootNode(StatsView &view); + ~RootNode(); + StatsView &view; std::unique_ptr<QSGRectangleNode> backgroundNode; // solid background // We entertain one node per Z-level. std::array<std::unique_ptr<ZNode>, (size_t)ChartZValue::Count> zNodes; }; -RootNode::RootNode(QQuickWindow *w) +RootNode::RootNode(StatsView &view) : view(view) { // Add a background rectangle with a solid color. This could // also be done on the widget level, but would have to be done // separately for desktop and mobile, so do it here. - backgroundNode.reset(w->createRectangleNode()); + backgroundNode.reset(view.w()->createRectangleNode()); backgroundNode->setColor(backgroundColor); appendChildNode(backgroundNode.get()); @@ -110,21 +112,31 @@ RootNode::RootNode(QQuickWindow *w) } } -QSGNode *StatsView::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *) +RootNode::~RootNode() { - // The QtQuick drawing interface is utterly bizzare with a distinct 1980ies-style memory management. - // This is just a copy of what is found in Qt's documentation. - RootNode *n = static_cast<RootNode *>(oldNode); - if (!n) - n = rootNode = new RootNode(window()); + view.emergencyShutdown(); +} - // Delete all chart items that are marked for deletion. +void StatsView::freeDeletedChartItems() +{ ChartItem *nextitem; for (ChartItem *item = deletedItems.first; item; item = nextitem) { nextitem = item->next; delete item; } deletedItems.clear(); +} + +QSGNode *StatsView::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *) +{ + // The QtQuick drawing interface is utterly bizzare with a distinct 1980ies-style memory management. + // This is just a copy of what is found in Qt's documentation. + RootNode *n = static_cast<RootNode *>(oldNode); + if (!n) + n = rootNode = new RootNode(*this); + + // Delete all chart items that are marked for deletion. + freeDeletedChartItems(); if (backgroundDirty) { rootNode->backgroundNode->setRect(plotRect); @@ -140,6 +152,32 @@ QSGNode *StatsView::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNod return n; } +// When reparenting the QQuickWidget, QtQuick decides to delete our rootNode +// and with it all the QSG nodes, even though we have *not* given the +// permission to do so! If the widget is reused, we try to delete the +// stale items, whose nodes have already been deleted by QtQuick, leading +// to a double-free(). Instead of searching for the cause of this behavior, +// let's just hook into the rootNodes destructor and delete the objects +// in a controlled manner, so that QtQuick has no more access to them. +void StatsView::emergencyShutdown() +{ + // Mark clean and dirty chart items for deletion... + cleanItems.splice(deletedItems); + dirtyItems.splice(deletedItems); + + // ...and delete them. + freeDeletedChartItems(); + + // Now delete all the pointers we might have to chart features, + // axes, etc. Note that all pointers to chart items are non + // owning, so this only resets stale references, but does not + // lead to any additional deletion of chart items. + reset(); + + // The rootNode is being deleted -> remove the reference to that + rootNode = nullptr; +} + void StatsView::addQSGNode(QSGNode *node, ChartZValue z) { int idx = std::clamp((int)z, 0, (int)ChartZValue::Count - 1); diff --git a/stats/statsview.h b/stats/statsview.h index 3623c01eb..cc497792b 100644 --- a/stats/statsview.h +++ b/stats/statsview.h @@ -43,13 +43,14 @@ public: ~StatsView(); void plot(const StatsState &state); - void updateFeatures(const StatsState &state); // Updates the visibility of chart features, such as legend, regression, etc. + void updateFeatures(const StatsState &state); // Updates the visibility of chart features, such as legend, regression, etc. QQuickWindow *w() const; // Make window available to items QSizeF size() const; QRectF plotArea() const; void addQSGNode(QSGNode *node, ChartZValue z); // Must only be called in render thread! void registerChartItem(ChartItem &item); void registerDirtyChartItem(ChartItem &item); + void emergencyShutdown(); // Called when QQuick decides to delete out root node. // Create a chart item and add it to the scene. // The item must not be deleted by the caller, but can be @@ -151,6 +152,10 @@ private: // There are three double linked lists of chart items: // clean items, dirty items and items to be deleted. + // Note that only the render thread must delete chart items, + // and therefore these lists are the only owning pointers + // to chart items. All other pointers are non-owning and + // can therefore become stale. struct ChartItemList { ChartItemList(); ChartItem *first, *last; @@ -161,6 +166,7 @@ private: }; ChartItemList cleanItems, dirtyItems, deletedItems; void deleteChartItemInternal(ChartItem &item); + void freeDeletedChartItems(); }; // This implementation detail must be known to users of the class. |