9b67b0238d
Resolve URI expansion on the main thread before dispatching async library enumeration tasks. The getUri() call accesses PROJECT text variables and SETTINGS_MANAGER::Prj() which are not thread-safe. When a project is unloaded while worker threads are still running, they would access freed or modified data in PROJECT::TextVarResolver, causing either an access violation in (KICAD-14DS) or an assertion failure when the project list is empty (KICAD-11Q9). Pre-computing the expanded URI on the main thread and passing it to enumerateLibrary() as a parameter eliminates the data race. Resolves KICAD-14DS Resolves KICAD-11Q9
176 lines
7.2 KiB
C++
176 lines
7.2 KiB
C++
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
|
|
* @author Jon Evans <jon@craftyjon.com>
|
|
*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
|
|
#ifndef DESIGN_BLOCK_LIBRARY_ADAPTER_H
|
|
#define DESIGN_BLOCK_LIBRARY_ADAPTER_H
|
|
|
|
#include <kicommon.h>
|
|
#include <lib_id.h>
|
|
#include <core/leak_at_exit.h>
|
|
#include <libraries/library_manager.h>
|
|
|
|
class DESIGN_BLOCK;
|
|
class DESIGN_BLOCK_IO;
|
|
class PROJECT;
|
|
|
|
|
|
class KICOMMON_API DESIGN_BLOCK_LIBRARY_ADAPTER : public LIBRARY_MANAGER_ADAPTER
|
|
{
|
|
public:
|
|
DESIGN_BLOCK_LIBRARY_ADAPTER( LIBRARY_MANAGER& aManager );
|
|
|
|
LIBRARY_TABLE_TYPE Type() const override { return LIBRARY_TABLE_TYPE::DESIGN_BLOCK; }
|
|
|
|
static wxString GlobalPathEnvVariableName();
|
|
|
|
std::optional<LIB_STATUS> LoadOne( LIB_DATA* aLib ) override;
|
|
|
|
std::optional<LIB_STATUS> LoadOne( const wxString& nickname );
|
|
|
|
/// @return all the design blocks in the given library, if it exists and is loaded (or an empty list)
|
|
std::vector<DESIGN_BLOCK*> GetDesignBlocks( const wxString& aNickname );
|
|
|
|
/// @return all the names of design blocks in the given library, if it exists and is loaded (or an empty list)
|
|
std::vector<wxString> GetDesignBlockNames( const wxString& aNickname );
|
|
|
|
/**
|
|
* Load a design block having @a aDesignBlockName from the library given by @a aNickname.
|
|
*
|
|
* @param aNickname is a locator for the "library", it is a "name" in #LIB_TABLE_ROW.
|
|
* @param aDesignBlockName is the name of the design block to load.
|
|
* @param aKeepUUID = true to keep initial items UUID, false to set new UUID
|
|
* normally true if loaded in the design block editor, false
|
|
* if loaded in the board editor. Used only in kicad_plugin
|
|
* @return the design block if found caller owns it, else NULL if not found.
|
|
*
|
|
* @throw IO_ERROR if the library cannot be found or read. No exception
|
|
* is thrown in the case where aDesignBlockName cannot be found.
|
|
*/
|
|
DESIGN_BLOCK* LoadDesignBlock( const wxString& aNickname, const wxString& aDesignBlockName,
|
|
bool aKeepUUID = false );
|
|
|
|
/**
|
|
* Indicates whether or not the given design block already exists in the given library.
|
|
*/
|
|
bool DesignBlockExists( const wxString& aNickname, const wxString& aDesignBlockName );
|
|
|
|
/**
|
|
* A version of #DesignBlockLoad() for use after #DesignBlockEnumerate() for more efficient
|
|
* cache management.
|
|
*
|
|
* The return value is const to allow it to return a reference to a cached item.
|
|
*/
|
|
// TODO(JE) library tables -- do we still need this?
|
|
const DESIGN_BLOCK* GetEnumeratedDesignBlock( const wxString& aNickname, const wxString& aDesignBlockName );
|
|
|
|
/**
|
|
* The set of return values from DesignBlockSave() below.
|
|
*/
|
|
enum SAVE_T
|
|
{
|
|
SAVE_OK,
|
|
SAVE_SKIPPED,
|
|
};
|
|
|
|
/**
|
|
* Write @a aDesignBlock to an existing library given by @a aNickname.
|
|
*
|
|
* If a design block by the same name already exists, it is replaced.
|
|
*
|
|
* @param aNickname is a locator for the "library", it is a "name" in #LIB_TABLE_ROW.
|
|
* @param aDesignBlock is what to store in the library. The caller continues to own the
|
|
* design block after this call.
|
|
* @param aOverwrite when true means overwrite any existing design block by the same name,
|
|
* else if false means skip the write and return SAVE_SKIPPED.
|
|
* @return #SAVE_OK or #SAVE_SKIPPED. If error saving, then #IO_ERROR is thrown.
|
|
*
|
|
* @throw IO_ERROR if there is a problem saving.
|
|
*/
|
|
SAVE_T SaveDesignBlock( const wxString& aNickname, const DESIGN_BLOCK* aDesignBlock,
|
|
bool aOverwrite = true );
|
|
|
|
/**
|
|
* Delete the @a aDesignBlockName from the library given by @a aNickname.
|
|
*
|
|
* @param aNickname is a locator for the "library", it is a "name" in #LIB_TABLE_ROW.
|
|
* @param aDesignBlockName is the name of a design block to delete from the specified library.
|
|
*
|
|
* @throw IO_ERROR if there is a problem finding the design block or the library, or deleting
|
|
* it.
|
|
*/
|
|
void DeleteDesignBlock( const wxString& aNickname, const wxString& aDesignBlockName );
|
|
|
|
/**
|
|
* Return true if the library given by @a aNickname is writable.
|
|
*
|
|
* Often system libraries are read only because of where they are installed.
|
|
*
|
|
* @throw IO_ERROR if no library at aLibraryPath exists.
|
|
*/
|
|
bool IsDesignBlockLibWritable( const wxString& aNickname );
|
|
|
|
/**
|
|
* Load a design block having @a aDesignBlockId with possibly an empty nickname.
|
|
*
|
|
* @param aDesignBlockId the [nickname] and name of the design block to load.
|
|
* @param aKeepUUID = true to keep initial items UUID, false to set new UUID
|
|
* normally true if loaded in the design block editor, false
|
|
* if loaded in the board editor
|
|
* used only in kicad_plugin
|
|
* @return the #DESIGN_BLOCK if found caller owns it, else NULL if not found.
|
|
*
|
|
* @throw IO_ERROR if the library cannot be found or read. No exception is
|
|
* thrown in the case where \a aDesignBlockName cannot be found.
|
|
* @throw PARSE_ERROR if @a aDesignBlockId is not parsed OK.
|
|
*/
|
|
DESIGN_BLOCK* DesignBlockLoadWithOptionalNickname( const LIB_ID& aDesignBlockId,
|
|
bool aKeepUUID = false );
|
|
|
|
protected:
|
|
|
|
std::map<wxString, LIB_DATA>& globalLibs() override { return GlobalLibraries.Get(); }
|
|
std::map<wxString, LIB_DATA>& globalLibs() const override { return GlobalLibraries.Get(); }
|
|
std::shared_mutex& globalLibsMutex() override { return GlobalLibraryMutex; }
|
|
std::shared_mutex& globalLibsMutex() const override { return GlobalLibraryMutex; }
|
|
|
|
void enumerateLibrary( LIB_DATA* aLib, const wxString& aUri ) override;
|
|
|
|
LIBRARY_RESULT<IO_BASE*> createPlugin( const LIBRARY_TABLE_ROW* row ) override;
|
|
|
|
IO_BASE* plugin( const LIB_DATA* aRow ) override;
|
|
|
|
private:
|
|
|
|
/// Helper to cast the ABC plugin in the LIB_DATA* to a concrete plugin
|
|
static DESIGN_BLOCK_IO* dbplugin( const LIB_DATA* aRow );
|
|
|
|
// The global libraries, potentially shared between multiple different open
|
|
// projects, each of which has their own instance of this adapter class.
|
|
// Wrapped in LEAK_AT_EXIT to skip destruction at program exit for faster shutdown.
|
|
static LEAK_AT_EXIT<std::map<wxString, LIB_DATA>> GlobalLibraries;
|
|
|
|
static std::shared_mutex GlobalLibraryMutex;
|
|
};
|
|
|
|
|
|
|
|
#endif //DESIGN_BLOCK_LIBRARY_ADAPTER_H
|