Fix project lock both working and not working.

Turns out project locks would get created and only stick around for a short duration as the default move constructor was letting the og object free the lock.
But if you crashed kicad or managed something weird, you can get a lock file with no override prompt which also creates confusion as some stuff gets set read only.

So lets just fix this up.

- Implement move constructor on lockfile so that the old object will no longer free the lockfile
- Move lock ownership to the project itself, just slightly less weird to handle for the lock override
- Implement the override lock prompt
This commit is contained in:
Mark Roszko 2025-06-01 18:21:38 -04:00 committed by Marek Roszko
parent e615fcfe90
commit 9d32d208b8
6 changed files with 80 additions and 15 deletions

View File

@ -33,6 +33,7 @@
#include <string_utils.h>
#include <kiface_ids.h>
#include <kiway.h>
#include <lockfile.h>
#include <macros.h>
#include <project.h>
#include <project/project_file.h>
@ -42,6 +43,7 @@
#include <settings/settings_manager.h>
#include <title_block.h>
PROJECT::PROJECT() :
m_readOnly( false ),
m_textVarsTicker( 0 ),
@ -460,3 +462,15 @@ DESIGN_BLOCK_LIB_TABLE* PROJECT::DesignBlockLibs()
return tbl;
}
void PROJECT::SetProjectLock( LOCKFILE* aLockFile )
{
m_project_lock.reset( aLockFile );
}
LOCKFILE* PROJECT::GetProjectLock() const
{
return m_project_lock.get();
}

View File

@ -939,13 +939,11 @@ bool SETTINGS_MANAGER::LoadProject( const wxString& aFullPath, bool aSetActive )
if( m_projects.count( fullPath ) )
return true;
bool readOnly = false;
LOCKFILE lockFile( fullPath );
if( !lockFile.Valid() )
{
wxLogTrace( traceSettings, wxT( "Project %s is locked; opening read-only" ), fullPath );
readOnly = true;
}
// No MDI yet
@ -986,10 +984,10 @@ bool SETTINGS_MANAGER::LoadProject( const wxString& aFullPath, bool aSetActive )
if( success )
{
project->SetReadOnly( readOnly || project->GetProjectFile().IsReadOnly() );
project->SetReadOnly( !lockFile.Valid() || project->GetProjectFile().IsReadOnly() );
if( lockFile && aSetActive )
m_project_lock.reset( new LOCKFILE( std::move( lockFile ) ) );
project->SetProjectLock( new LOCKFILE( std::move( lockFile ) ) );
}
m_projects_list.push_back( std::move( project ) );
@ -1048,9 +1046,6 @@ bool SETTINGS_MANAGER::UnloadProject( PROJECT* aProject, bool aSave )
// Remove the reference in the environment to the previous project
wxSetEnv( PROJECT_VAR_NAME, wxS( "" ) );
// Release lock on the file, in case we had one
m_project_lock = nullptr;
if( m_kiway )
m_kiway->ProjectChanged();
}

View File

@ -127,6 +127,20 @@ public:
}
}
LOCKFILE( LOCKFILE&& other ) noexcept :
m_originalFile( std::move( other.m_originalFile ) ),
m_lockFilename( std::move( other.m_lockFilename ) ),
m_username( std::move( other.m_username ) ),
m_hostname( std::move( other.m_hostname ) ),
m_fileCreated( other.m_fileCreated ),
m_status( other.m_status ),
m_removeOnRelease( other.m_removeOnRelease ),
m_errorMsg( std::move( other.m_errorMsg ) )
{
// Disable unlock in the moved-from object
other.m_fileCreated = false;
}
~LOCKFILE()
{
UnlockFile();

View File

@ -52,6 +52,7 @@ class SYMBOL_LIB_TABLE;
class FILENAME_RESOLVER;
class PROJECT_FILE;
class PROJECT_LOCAL_SETTINGS;
class LOCKFILE;
/**
@ -217,9 +218,9 @@ public:
{
DOC_PATH,
SCH_LIB_PATH,
SCH_LIB_SELECT, // eeschema/selpart.cpp
SCH_LIB_SELECT, // eeschema/selpart.cpp
SCH_LIBEDIT_CUR_LIB,
SCH_LIBEDIT_CUR_SYMBOL, // eeschema/libeditframe.cpp
SCH_LIBEDIT_CUR_SYMBOL, // eeschema/libeditframe.cpp
VIEWER_3D_PATH,
VIEWER_3D_FILTER_INDEX,
@ -270,11 +271,11 @@ public:
/**
* Clear the _ELEMs and RSTRINGs.
*/
void Clear() // inline not virtual
void Clear() // inline not virtual
{
elemsClear();
for( unsigned i = 0; i<RSTRING_COUNT; ++i )
for( unsigned i = 0; i < RSTRING_COUNT; ++i )
SetRString( RSTRING_T( i ), wxEmptyString );
}
@ -297,6 +298,10 @@ public:
*/
virtual DESIGN_BLOCK_LIB_TABLE* DesignBlockLibs();
void SetProjectLock( LOCKFILE* aLockFile );
LOCKFILE* GetProjectLock() const;
private:
friend class SETTINGS_MANAGER; // so that SM can set project path
friend class TEST_NETLISTS_FIXTURE; // TODO(JE) make this not required
@ -366,6 +371,9 @@ private:
/// @see this::Elem() and enum ELEM_T.
std::array<_ELEM*,static_cast<unsigned int>( PROJECT::ELEM::COUNT )> m_elems;
/// Lock
std::unique_ptr<LOCKFILE> m_project_lock;
};

View File

@ -531,9 +531,6 @@ private:
/// Loaded project files, mapped according to project full name.
std::map<wxString, PROJECT_FILE*> m_project_files;
/// Lock for loaded project (expand to multiple once we support MDI).
std::unique_ptr<LOCKFILE> m_project_lock;
static wxString backupDateTimeFormat;
};

View File

@ -48,6 +48,7 @@
#include <kiway.h>
#include <kiway_express.h>
#include <launch_ext.h>
#include <lockfile.h>
#include <notifications_manager.h>
#include <reporter.h>
#include <project/project_local_settings.h>
@ -68,6 +69,7 @@
#include <wx/filedlg.h>
#include <wx/dnd.h>
#include <wx/process.h>
#include <wx/snglinst.h>
#include <atomic>
#include <update_manager.h>
#include <jobs/jobset.h>
@ -1035,7 +1037,42 @@ void KICAD_MANAGER_FRAME::CommonSettingsChanged( int aFlags )
void KICAD_MANAGER_FRAME::ProjectChanged()
{
wxString file = GetProjectFileName();
wxString file = GetProjectFileName();
// empty file string means no project loaded
if( !Prj().IsNullProject() &&
Prj().GetProjectLock() == nullptr )
{
LOCKFILE lockFile( file );
if( !lockFile.Valid() && lockFile.IsLockedByMe() )
{
// If we cannot acquire the lock but we appear to be the one who
// locked it, check to see if there is another KiCad instance running.
// If there is not, then we can override the lock. This could happen if
// KiCad crashed or was interrupted
if( !Pgm().SingleInstance()->IsAnotherRunning() )
{
lockFile.OverrideLock();
}
}
if( !lockFile.Valid() )
{
wxString msg;
msg.Printf( _( "Project '%s' is already open by '%s' at '%s'." ), file, lockFile.GetUsername(),
lockFile.GetHostname() );
if( AskOverrideLock( this, msg ) )
{
lockFile.OverrideLock();
}
}
Prj().SetReadOnly( !lockFile.Valid() || Prj().GetProjectFile().IsReadOnly() );
Prj().SetProjectLock( new LOCKFILE( std::move( lockFile ) ) );
}
wxString title;
if( !file.IsEmpty() )