From 3d526edc8a0190a5a8f465946de741a5f1c3aac3 Mon Sep 17 00:00:00 2001
From: Jon Evans <jon@craftyjon.com>
Date: Sun, 9 Feb 2025 09:18:03 -0500
Subject: [PATCH] More thread safety for TRACKS_CLEANER

Fixes https://gitlab.com/kicad/code/kicad/-/issues/19884
---
 pcbnew/connectivity/connectivity_algo.cpp     | 14 +++----
 .../dialog_cleanup_tracks_and_vias.cpp        | 38 +++++++++++++------
 2 files changed, 34 insertions(+), 18 deletions(-)

diff --git a/pcbnew/connectivity/connectivity_algo.cpp b/pcbnew/connectivity/connectivity_algo.cpp
index a96e9ff5e5..1bb9b6c512 100644
--- a/pcbnew/connectivity/connectivity_algo.cpp
+++ b/pcbnew/connectivity/connectivity_algo.cpp
@@ -350,8 +350,10 @@ CN_CONNECTIVITY_ALGO::SearchClusters( CLUSTER_SEARCH_MODE aMode, const std::vect
     if( m_itemList.IsDirty() )
         searchConnections();
 
+    std::set<CN_ITEM*> visited;
+
     auto addToSearchList =
-            [&item_set, withinAnyNet, aSingleNet, &aTypes, rootItem ]( CN_ITEM *aItem )
+            [&item_set, withinAnyNet, aSingleNet, &aTypes, rootItem, &visited]( CN_ITEM *aItem )
             {
                 if( withinAnyNet && aItem->Net() <= 0 )
                     return;
@@ -376,8 +378,6 @@ CN_CONNECTIVITY_ALGO::SearchClusters( CLUSTER_SEARCH_MODE aMode, const std::vect
                 if( !found && aItem != rootItem )
                     return;
 
-                aItem->SetVisited( false );
-
                 item_set.insert( aItem );
             };
 
@@ -392,14 +392,14 @@ CN_CONNECTIVITY_ALGO::SearchClusters( CLUSTER_SEARCH_MODE aMode, const std::vect
         CN_ITEM*                    root;
         auto                        it = item_set.begin();
 
-        while( it != item_set.end() && (*it)->Visited() )
+        while( it != item_set.end() && visited.contains( *it ) )
             it = item_set.erase( item_set.begin() );
 
         if( it == item_set.end() )
             break;
 
         root = *it;
-        root->SetVisited( true );
+        visited.insert( root );
 
         Q.clear();
         Q.push_back( root );
@@ -416,9 +416,9 @@ CN_CONNECTIVITY_ALGO::SearchClusters( CLUSTER_SEARCH_MODE aMode, const std::vect
                 if( withinAnyNet && n->Net() != root->Net() )
                     continue;
 
-                if( !n->Visited() && n->Valid() )
+                if( !visited.contains( n ) && n->Valid() )
                 {
-                    n->SetVisited( true );
+                    visited.insert( n );
                     Q.push_back( n );
                 }
             }
diff --git a/pcbnew/dialogs/dialog_cleanup_tracks_and_vias.cpp b/pcbnew/dialogs/dialog_cleanup_tracks_and_vias.cpp
index 63030da517..ae1fc7db76 100644
--- a/pcbnew/dialogs/dialog_cleanup_tracks_and_vias.cpp
+++ b/pcbnew/dialogs/dialog_cleanup_tracks_and_vias.cpp
@@ -188,10 +188,29 @@ void DIALOG_CLEANUP_TRACKS_AND_VIAS::doCleanup( bool aDryRun )
     BOARD_COMMIT commit( m_parentFrame );
     TRACKS_CLEANER cleaner( m_brd, commit );
 
+    struct FILTER_STATE
+    {
+        bool selectedOnly;
+        int netCodeOnly;
+        wxString netClassOnly;
+        int layerOnly;
+    };
+
+    FILTER_STATE filter_state = {
+            .selectedOnly = m_selectedItemsFilter->GetValue(),
+            .netCodeOnly = m_netFilterOpt->GetValue() ? m_netFilter->GetSelectedNetcode() : -1,
+            .netClassOnly = m_netclassFilterOpt->GetValue()
+                                ? m_netclassFilter->GetStringSelection()
+                                : wxString(),
+            .layerOnly = m_layerFilterOpt->GetValue()
+                             ? m_layerFilter->GetLayerSelection()
+                             : UNDEFINED_LAYER
+            };
+
     cleaner.SetFilter(
-            [&]( BOARD_CONNECTED_ITEM* aItem ) -> bool
+            [&, filter_state]( BOARD_CONNECTED_ITEM* aItem ) -> bool
             {
-                if( m_selectedItemsFilter->GetValue() )
+                if( filter_state.selectedOnly )
                 {
                     if( !aItem->IsSelected() )
                     {
@@ -205,26 +224,23 @@ void DIALOG_CLEANUP_TRACKS_AND_VIAS::doCleanup( bool aDryRun )
                     }
                 }
 
-                if( m_netFilterOpt->GetValue() && m_netFilter->GetSelectedNetcode() >= 0 )
+                if( filter_state.netCodeOnly >= 0 )
                 {
-                    if( aItem->GetNetCode() != m_netFilter->GetSelectedNetcode() )
+                    if( aItem->GetNetCode() != filter_state.netCodeOnly )
                         return true;
                 }
 
-                if( m_netclassFilterOpt->GetValue()
-                        && !m_netclassFilter->GetStringSelection().IsEmpty() )
+                if( !filter_state.netClassOnly.IsEmpty() )
                 {
-                    wxString  filterNetclass = m_netclassFilter->GetStringSelection();
                     NETCLASS* netclass = aItem->GetEffectiveNetClass();
 
-                    if( !netclass->ContainsNetclassWithName( filterNetclass ) )
+                    if( !netclass->ContainsNetclassWithName( filter_state.netClassOnly ) )
                         return true;
                 }
 
-                if( m_layerFilterOpt->GetValue()
-                        && m_layerFilter->GetLayerSelection() != UNDEFINED_LAYER )
+                if( filter_state.layerOnly != UNDEFINED_LAYER )
                 {
-                    if( aItem->GetLayer() != m_layerFilter->GetLayerSelection() )
+                    if( aItem->GetLayer() != filter_state.layerOnly )
                         return true;
                 }