kicad/pcbnew/connectivity/connectivity_data.h

346 lines
11 KiB
C
Raw Normal View History

2017-03-22 13:43:10 +00:00
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2013-2017 CERN
2020-07-02 18:43:18 +00:00
* Copyright (C) 2018-2020 KiCad Developers, see AUTHORS.txt for contributors.
2017-03-22 13:43:10 +00:00
* @author Maciej Suminski <maciej.suminski@cern.ch>
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* 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 2
* 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef __CONNECTIVITY_DATA_H
#define __CONNECTIVITY_DATA_H
2017-03-22 13:43:10 +00:00
#include <core/typeinfo.h>
Fix issues with zone filling connectivity locking Two issues found with the locking system used to prevent access to stale connectivity data during the zone fill process: 1) a std::mutex has undefined behavior if you try to use it to guard against access from the same thread. Because of the use of wx event loops (and coroutines) it is entirely possible, and in some situations inevitable, that the same thread will try to redraw the ratsnest in the middle of zone refilling. 2) The mutex was only guarding the ZONE_FILLER::Fill method, but the callers of that method also do connectivity updates as part of the COMMIT::Push. Redrawing the ratsnest after the Fill but before the Push will result in stale connectivity pointers to zone filled areas. Fixed (1) by switching to a trivial spinlock implementation. Spinlocks would generally not be desirable if the contention for the connectivity data crossed thread boundaries, but at the moment I believe it's guaranteed that the reads and writes to connectivity that are guarded by this lock happen from the main UI thread. The writes are also quite rare compared to reads, and reads are generally fast, so I'm not really worried about the UI thread spinning for any real amount of time. Fixed (2) by moving the locking location up to the call sites of ZONE_FILLER::Fill. This issue was quite difficult to reproduce, but I found a fairly reliable way: It only happens (for me) on Windows, MSYS2 build, with wxWidgets 3.0 It also only happens if I restrict PcbNew to use 2 CPU cores. With those conditions, I can reproduce the issue described in #6471 by repeatedly editing a zone properties and changing its net. The crash is especially easy to trigger if you press some keys (such as 'e' for edit) while the progress dialog is displayed. It's easiest to do this in a debug build as the slower KiCad is running, the bigger the window is to trigger this bug. Fixes https://gitlab.com/kicad/code/kicad/-/issues/6471 Fixes https://gitlab.com/kicad/code/kicad/-/issues/7048
2021-01-18 17:24:07 +00:00
#include <core/spinlock.h>
2017-03-22 13:43:10 +00:00
#include <memory>
#include <mutex>
#include <vector>
#include <wx/string.h>
2017-03-22 13:43:10 +00:00
#include <math/vector2d.h>
#include <geometry/shape_poly_set.h>
#include <zone.h>
2017-03-22 13:43:10 +00:00
class FROM_TO_CACHE;
2017-03-22 13:43:10 +00:00
class CN_CLUSTER;
class CN_CONNECTIVITY_ALGO;
2017-03-22 15:47:15 +00:00
class CN_EDGE;
2017-03-22 13:43:10 +00:00
class BOARD;
class BOARD_COMMIT;
2017-03-22 13:43:10 +00:00
class BOARD_CONNECTED_ITEM;
class BOARD_ITEM;
class ZONE;
2017-03-22 13:43:10 +00:00
class RN_DATA;
class RN_NET;
2021-06-11 21:07:02 +00:00
class PCB_TRACK;
2020-11-12 22:30:02 +00:00
class PAD;
2020-11-13 15:15:52 +00:00
class FOOTPRINT;
class PROGRESS_REPORTER;
2017-03-22 13:43:10 +00:00
2021-11-30 14:19:39 +00:00
2017-03-22 13:43:10 +00:00
struct CN_DISJOINT_NET_ENTRY
{
int net;
2017-06-30 11:40:20 +00:00
BOARD_CONNECTED_ITEM *a, *b;
2017-03-22 13:43:10 +00:00
VECTOR2I anchorA, anchorB;
};
2021-11-30 14:19:39 +00:00
/**
* A structure used for calculating isolated islands on a given zone across all its layers
*/
struct CN_ZONE_ISOLATED_ISLAND_LIST
{
CN_ZONE_ISOLATED_ISLAND_LIST( ZONE* aZone ) :
m_zone( aZone )
{}
ZONE* m_zone;
std::map<PCB_LAYER_ID, std::vector<int>> m_islands;
};
2021-11-30 14:19:39 +00:00
2017-03-22 13:43:10 +00:00
struct RN_DYNAMIC_LINE
{
int netCode;
VECTOR2I a, b;
};
2021-11-30 14:19:39 +00:00
/**
* Controls how nets are propagated through clusters
*/
enum class PROPAGATE_MODE
{
SKIP_CONFLICTS, /// Clusters with conflicting drivers are not updated (default)
RESOLVE_CONFLICTS /// Clusters with conflicting drivers are updated to the most popular net
};
2021-11-30 14:19:39 +00:00
2017-03-22 13:43:10 +00:00
// a wrapper class encompassing the connectivity computation algorithm and the
class CONNECTIVITY_DATA
2017-03-22 13:43:10 +00:00
{
public:
CONNECTIVITY_DATA();
~CONNECTIVITY_DATA();
CONNECTIVITY_DATA( const std::vector<BOARD_ITEM*>& aItems, bool aSkipItems = false );
2017-03-22 13:43:10 +00:00
/**
* Function Build()
* Builds the connectivity database for the board aBoard.
*/
void Build( BOARD* aBoard, PROGRESS_REPORTER* aReporter = nullptr );
2017-03-22 13:43:10 +00:00
/**
* Function Build()
* Builds the connectivity database for a set of items aItems.
*/
void Build( const std::vector<BOARD_ITEM*>& aItems );
/**
* Function Add()
* Adds an item to the connectivity data.
* @param aItem is an item to be added.
* @return True if operation succeeded.
*/
bool Add( BOARD_ITEM* aItem );
/**
* Function Remove()
* Removes an item from the connectivity data.
* @param aItem is an item to be updated.
* @return True if operation succeeded.
*/
bool Remove( BOARD_ITEM* aItem );
/**
* Function Update()
* Updates the connectivity data for an item.
* @param aItem is an item to be updated.
* @return True if operation succeeded.
*/
bool Update( BOARD_ITEM* aItem );
/**
* Moves the connectivity list anchors. N.B., this does not move the bounding
* boxes for the RTree, so the use of this function will invalidate the
* connectivity data for uses other than the dynamic ratsnest
*
* @param aDelta vector for movement of the tree
*/
void Move( const VECTOR2I& aDelta );
2017-03-22 13:43:10 +00:00
/**
* Function Clear()
* Erases the connectivity database.
*/
void Clear();
/**
* Function GetNetCount()
* Returns the total number of nets in the connectivity database.
*/
int GetNetCount() const;
/**
* Function GetRatsnestForNet()
* Returns the ratsnest, expressed as a set of graph edges for a given net.
*/
RN_NET* GetRatsnestForNet( int aNet );
2017-06-30 11:40:20 +00:00
2017-03-22 13:43:10 +00:00
/**
* Propagates the net codes from the source pads to the tracks/vias.
* @param aCommit is used to save the undo state of items modified by this call
* @param aMode controls how conflicts between pads are resolved
2017-03-22 13:43:10 +00:00
*/
void PropagateNets( BOARD_COMMIT* aCommit = nullptr,
PROPAGATE_MODE aMode = PROPAGATE_MODE::SKIP_CONFLICTS );
2017-03-22 13:43:10 +00:00
bool CheckConnectivity( std::vector<CN_DISJOINT_NET_ENTRY>& aReport );
2017-03-22 13:43:10 +00:00
/**
* Function FindIsolatedCopperIslands()
* Searches for copper islands in zone aZone that are not connected to any pad.
* @param aZone zone to test
* @param aIslands list of islands that have no connections (outline indices in the polygon set)
*/
void FindIsolatedCopperIslands( ZONE* aZone, std::vector<int>& aIslands );
void FindIsolatedCopperIslands( std::vector<CN_ZONE_ISOLATED_ISLAND_LIST>& aZones );
2017-03-22 13:43:10 +00:00
/**
* Function RecalculateRatsnest()
* Updates the ratsnest for the board.
* @param aCommit is used to save the undo state of items modified by this call
2017-03-22 13:43:10 +00:00
*/
void RecalculateRatsnest( BOARD_COMMIT* aCommit = nullptr );
2017-03-22 13:43:10 +00:00
/**
* Function GetUnconnectedCount()
* Returns the number of remaining edges in the ratsnest.
*/
unsigned int GetUnconnectedCount() const;
2021-06-11 21:07:02 +00:00
bool IsConnectedOnLayer( const BOARD_CONNECTED_ITEM* aItem,
int aLayer,
const std::initializer_list<KICAD_T>& aTypes = {},
bool aCheckOptionalFlashing = false ) const;
unsigned int GetNodeCount( int aNet = -1 ) const;
unsigned int GetPadCount( int aNet = -1 ) const;
2021-06-11 21:07:02 +00:00
const std::vector<PCB_TRACK*> GetConnectedTracks( const BOARD_CONNECTED_ITEM* aItem ) const;
2020-11-12 22:30:02 +00:00
const std::vector<PAD*> GetConnectedPads( const BOARD_CONNECTED_ITEM* aItem ) const;
2017-03-22 13:43:10 +00:00
2020-11-12 22:30:02 +00:00
void GetConnectedPads( const BOARD_CONNECTED_ITEM* aItem, std::set<PAD*>* pads ) const;
/**
* Function GetConnectedItemsAtAnchor()
* Returns a list of items connected to a source item aItem at position aAnchor
* with an optional maximum distance from the defined anchor.
* @param aItem is the reference item to find other connected items.
* @param aAnchor is the position to find connected items on.
* @param aTypes allows one to filter by item types.
* @param aMaxError Maximum distance of the found items' anchors to aAnchor in IU
* @return
*/
const std::vector<BOARD_CONNECTED_ITEM*> GetConnectedItemsAtAnchor(
const BOARD_CONNECTED_ITEM* aItem,
const VECTOR2I& aAnchor,
const KICAD_T aTypes[],
const int& aMaxError = 0 ) const;
2017-03-22 15:47:15 +00:00
void GetUnconnectedEdges( std::vector<CN_EDGE>& aEdges ) const;
bool TestTrackEndpointDangling( PCB_TRACK* aTrack, VECTOR2I* aPos = nullptr );
2017-03-22 13:43:10 +00:00
/**
* Function ClearDynamicRatsnest()
* Erases the temporary dynamic ratsnest (i.e. the ratsnest lines that
* pcbnew displays when moving an item/set of items)
2017-03-22 13:43:10 +00:00
*/
void ClearDynamicRatsnest();
/**
* Hides the temporary dynamic ratsnest lines.
*/
void HideDynamicRatsnest();
2017-03-22 13:43:10 +00:00
/**
* Function ComputeDynamicRatsnest()
* Calculates the temporary dynamic ratsnest (i.e. the ratsnest lines that)
* for the set of items aItems.
*/
void ComputeDynamicRatsnest( const std::vector<BOARD_ITEM*>& aItems,
const CONNECTIVITY_DATA* aDynamicData,
VECTOR2I aInternalOffset = { 0, 0 } );
2017-03-22 13:43:10 +00:00
2017-06-23 12:20:08 +00:00
const std::vector<RN_DYNAMIC_LINE>& GetDynamicRatsnest() const
{
return m_dynamicRatsnest;
}
2017-03-22 13:43:10 +00:00
/**
* Function GetConnectedItems()
* Returns a list of items connected to a source item aItem.
* @param aItem is the reference item to find other connected items.
* @param aTypes allows one to filter by item types.
2017-03-22 13:43:10 +00:00
*/
const std::vector<BOARD_CONNECTED_ITEM*> GetConnectedItems( const BOARD_CONNECTED_ITEM* aItem,
const std::initializer_list<KICAD_T>& aTypes, bool aIgnoreNetcodes = false ) const;
2017-03-22 13:43:10 +00:00
/**
* Function GetNetItems()
* Returns the list of items that belong to a certain net.
* @param aNetCode is the net code.
* @param aTypes allows one to filter by item types.
2017-03-22 13:43:10 +00:00
*/
const std::vector<BOARD_CONNECTED_ITEM*> GetNetItems( int aNetCode,
2017-03-22 13:43:10 +00:00
const KICAD_T aTypes[] ) const;
void BlockRatsnestItems( const std::vector<BOARD_ITEM*>& aItems );
std::shared_ptr<CN_CONNECTIVITY_ALGO> GetConnectivityAlgo() const
{
2017-06-23 11:56:28 +00:00
return m_connAlgo;
}
Fix issues with zone filling connectivity locking Two issues found with the locking system used to prevent access to stale connectivity data during the zone fill process: 1) a std::mutex has undefined behavior if you try to use it to guard against access from the same thread. Because of the use of wx event loops (and coroutines) it is entirely possible, and in some situations inevitable, that the same thread will try to redraw the ratsnest in the middle of zone refilling. 2) The mutex was only guarding the ZONE_FILLER::Fill method, but the callers of that method also do connectivity updates as part of the COMMIT::Push. Redrawing the ratsnest after the Fill but before the Push will result in stale connectivity pointers to zone filled areas. Fixed (1) by switching to a trivial spinlock implementation. Spinlocks would generally not be desirable if the contention for the connectivity data crossed thread boundaries, but at the moment I believe it's guaranteed that the reads and writes to connectivity that are guarded by this lock happen from the main UI thread. The writes are also quite rare compared to reads, and reads are generally fast, so I'm not really worried about the UI thread spinning for any real amount of time. Fixed (2) by moving the locking location up to the call sites of ZONE_FILLER::Fill. This issue was quite difficult to reproduce, but I found a fairly reliable way: It only happens (for me) on Windows, MSYS2 build, with wxWidgets 3.0 It also only happens if I restrict PcbNew to use 2 CPU cores. With those conditions, I can reproduce the issue described in #6471 by repeatedly editing a zone properties and changing its net. The crash is especially easy to trigger if you press some keys (such as 'e' for edit) while the progress dialog is displayed. It's easiest to do this in a debug build as the slower KiCad is running, the bigger the window is to trigger this bug. Fixes https://gitlab.com/kicad/code/kicad/-/issues/6471 Fixes https://gitlab.com/kicad/code/kicad/-/issues/7048
2021-01-18 17:24:07 +00:00
KISPINLOCK& GetLock()
{
return m_lock;
}
void MarkItemNetAsDirty( BOARD_ITEM* aItem );
void SetProgressReporter( PROGRESS_REPORTER* aReporter );
const std::map<int, wxString>& GetNetclassMap() const
{
return m_netclassMap;
}
void AddExclusion( const KIID& aBoardItemId1, const KIID& aBoardItemId2 );
void RemoveExclusion( const KIID& aBoardItemId1, const KIID& aBoardItemId2 );
#ifndef SWIG
const std::vector<CN_EDGE> GetRatsnestForItems( const std::vector<BOARD_ITEM*> aItems );
const std::vector<CN_EDGE> GetRatsnestForPad( const PAD* aPad );
2020-11-13 15:15:52 +00:00
const std::vector<CN_EDGE> GetRatsnestForComponent( FOOTPRINT* aComponent,
bool aSkipInternalConnections = false );
#endif
std::shared_ptr<FROM_TO_CACHE> GetFromToCache()
{
return m_fromToCache;
}
2017-03-22 13:43:10 +00:00
private:
void updateRatsnest();
void addRatsnestCluster( const std::shared_ptr<CN_CLUSTER>& aCluster );
2017-03-22 13:43:10 +00:00
2021-11-30 14:19:39 +00:00
private:
2017-03-22 13:43:10 +00:00
std::shared_ptr<CN_CONNECTIVITY_ALGO> m_connAlgo;
2021-11-30 14:19:39 +00:00
std::shared_ptr<FROM_TO_CACHE> m_fromToCache;
std::vector<RN_DYNAMIC_LINE> m_dynamicRatsnest;
std::vector<RN_NET*> m_nets;
2021-11-30 14:19:39 +00:00
/// Used to suppress ratsnest calculations on dynamic ratsnests
bool m_skipRatsnest = false;
/// Ratsnest lines that have been excluded in DRC
std::set<std::pair<KIID, KIID>> m_exclusions;
2021-11-30 14:19:39 +00:00
KISPINLOCK m_lock;
/// Map of netcode -> netclass the net is a member of; used for ratsnest painting
2021-11-30 14:19:39 +00:00
std::map<int, wxString> m_netclassMap;
PROGRESS_REPORTER* m_progressReporter;
2017-03-22 13:43:10 +00:00
};
#endif