diff --git a/include/core/spinlock.h b/include/core/spinlock.h new file mode 100644 index 0000000000..6228e5d9ce --- /dev/null +++ b/include/core/spinlock.h @@ -0,0 +1,60 @@ +/* +* This program source code file is part of KiCad, a free EDA CAD application. +* +* Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors. +* +* This program is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation, either version 3 of the License, or (at your +* option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License along +* with this program. If not, see . +*/ + +#ifndef __KICAD_SPINLOCK_H +#define __KICAD_SPINLOCK_H + +#include + + +/** + * A trivial spinlock implementation with no optimization. Don't use if congestion is expected! + */ +class KISPINLOCK +{ +public: + KISPINLOCK() : + m_lock( false ) + {} + + void lock() + { + while( m_lock.exchange( true, std::memory_order_acquire ) ); + } + + bool try_lock() + { + return !m_lock.exchange( true, std::memory_order_acquire ); + } + + void unlock() + { + m_lock.store( false, std::memory_order_release ); + } + + bool test() + { + return m_lock.load( std::memory_order_acquire ); + } + +private: + std::atomic m_lock; +}; + +#endif diff --git a/pcbnew/connectivity/connectivity_data.h b/pcbnew/connectivity/connectivity_data.h index 8cc03a8358..a59f21486f 100644 --- a/pcbnew/connectivity/connectivity_data.h +++ b/pcbnew/connectivity/connectivity_data.h @@ -28,6 +28,7 @@ #define __CONNECTIVITY_DATA_H #include +#include #include #include @@ -261,7 +262,7 @@ public: return m_connAlgo; } - std::mutex& GetLock() + KISPINLOCK& GetLock() { return m_lock; } @@ -307,7 +308,7 @@ private: bool m_skipRatsnest = false; - std::mutex m_lock; + KISPINLOCK m_lock; /// Map of netcode -> netclass the net is a member of; used for ratsnest painting std::map m_netclassMap; diff --git a/pcbnew/edit_zone_helpers.cpp b/pcbnew/edit_zone_helpers.cpp index 2465ff6537..482b34fe9b 100644 --- a/pcbnew/edit_zone_helpers.cpp +++ b/pcbnew/edit_zone_helpers.cpp @@ -125,6 +125,8 @@ void PCB_EDIT_FRAME::Edit_Zone_Params( ZONE* aZone ) commit.Stage( pickedList ); + std::lock_guard lock( GetBoard()->GetConnectivity()->GetLock() ); + if( zones_to_refill.size() ) { ZONE_FILLER filler( GetBoard(), &commit ); diff --git a/pcbnew/ratsnest/ratsnest_viewitem.cpp b/pcbnew/ratsnest/ratsnest_viewitem.cpp index 480dc4c3fc..83c4a59bd5 100644 --- a/pcbnew/ratsnest/ratsnest_viewitem.cpp +++ b/pcbnew/ratsnest/ratsnest_viewitem.cpp @@ -63,7 +63,7 @@ const BOX2I RATSNEST_VIEWITEM::ViewBBox() const void RATSNEST_VIEWITEM::ViewDraw( int aLayer, KIGFX::VIEW* aView ) const { - std::unique_lock lock( m_data->GetLock(), std::try_to_lock ); + std::unique_lock lock( m_data->GetLock(), std::try_to_lock ); if( !lock ) return; diff --git a/pcbnew/tools/zone_create_helper.cpp b/pcbnew/tools/zone_create_helper.cpp index cec1e6a5b0..cde19faf26 100644 --- a/pcbnew/tools/zone_create_helper.cpp +++ b/pcbnew/tools/zone_create_helper.cpp @@ -21,6 +21,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ +#include +#include #include #include #include @@ -168,6 +170,8 @@ void ZONE_CREATE_HELPER::performZoneCutout( ZONE& aZone, ZONE& aCutout ) ZONE_FILLER filler( board, &commit ); + std::lock_guard lock( board->GetConnectivity()->GetLock() ); + if( !filler.Fill( newZones ) ) { commit.Revert(); @@ -207,9 +211,12 @@ void ZONE_CREATE_HELPER::commitZone( std::unique_ptr aZone ) aZone->HatchBorder(); bCommit.Add( aZone.get() ); + BOARD* board = m_tool.getModel(); + std::lock_guard lock( board->GetConnectivity()->GetLock() ); + if( !m_params.m_keepout ) { - ZONE_FILLER filler( m_tool.getModel(), &bCommit ); + ZONE_FILLER filler( board, &bCommit ); std::vector toFill = { aZone.get() }; if( !filler.Fill( toFill ) ) diff --git a/pcbnew/tools/zone_filler_tool.cpp b/pcbnew/tools/zone_filler_tool.cpp index 99da06d7f0..2a8128e35a 100644 --- a/pcbnew/tools/zone_filler_tool.cpp +++ b/pcbnew/tools/zone_filler_tool.cpp @@ -128,6 +128,8 @@ void ZONE_FILLER_TOOL::FillAllZones( wxWindow* aCaller, PROGRESS_REPORTER* aRepo else filler.InstallNewProgressReporter( aCaller, _( "Fill All Zones" ), 3 ); + std::lock_guard lock( board()->GetConnectivity()->GetLock() ); + if( filler.Fill( toFill ) ) { commit.Push( _( "Fill Zone(s)" ), false ); @@ -171,6 +173,8 @@ int ZONE_FILLER_TOOL::ZoneFill( const TOOL_EVENT& aEvent ) ZONE_FILLER filler( board(), &commit ); filler.InstallNewProgressReporter( frame(), _( "Fill Zone" ), 4 ); + std::lock_guard lock( board()->GetConnectivity()->GetLock() ); + if( filler.Fill( toFill ) ) commit.Push( _( "Fill Zone(s)" ), false ); else diff --git a/pcbnew/zone_filler.cpp b/pcbnew/zone_filler.cpp index 8e123ae159..2063e7ab4e 100644 --- a/pcbnew/zone_filler.cpp +++ b/pcbnew/zone_filler.cpp @@ -88,10 +88,6 @@ bool ZONE_FILLER::Fill( std::vector& aZones, bool aCheck, wxWindow* aPare std::vector islandsList; std::shared_ptr connectivity = m_board->GetConnectivity(); - std::unique_lock lock( connectivity->GetLock(), std::try_to_lock ); - - if( !lock ) - return false; // Rebuild just in case. This really needs to be reliable. connectivity->Clear(); diff --git a/pcbnew/zone_filler.h b/pcbnew/zone_filler.h index 80dd840d5c..8fa0641418 100644 --- a/pcbnew/zone_filler.h +++ b/pcbnew/zone_filler.h @@ -44,6 +44,13 @@ public: void SetProgressReporter( PROGRESS_REPORTER* aReporter ); void InstallNewProgressReporter( wxWindow* aParent, const wxString& aTitle, int aNumPhases ); + + /** + * Fills the given list of zones. Invalidates connectivity - it is up to the caller to obtain + * a lock on the connectivity data before calling Fill to prevent access to stale data by other + * coroutines (for example, ratsnest redraw). This will generally be required if a UI-based + * progress reporter has been installed. + */ bool Fill( std::vector& aZones, bool aCheck = false, wxWindow* aParent = nullptr ); bool IsDebug() const { return m_debugZoneFiller; }