Gracefully shutdown tools when frames are closed
If the tools are not gracefully exited, then the stack variables are never destroyed, so variable lifetime issues can occur. Fixes https://gitlab.com/kicad/code/kicad/issues/1753
This commit is contained in:
parent
1e6840ea8f
commit
b1240b5b1e
|
@ -55,17 +55,18 @@ struct TOOL_MANAGER::TOOL_STATE
|
||||||
|
|
||||||
TOOL_STATE( const TOOL_STATE& aState )
|
TOOL_STATE( const TOOL_STATE& aState )
|
||||||
{
|
{
|
||||||
theTool = aState.theTool;
|
theTool = aState.theTool;
|
||||||
idle = aState.idle;
|
idle = aState.idle;
|
||||||
pendingWait = aState.pendingWait;
|
shutdown = aState.shutdown;
|
||||||
|
pendingWait = aState.pendingWait;
|
||||||
pendingContextMenu = aState.pendingContextMenu;
|
pendingContextMenu = aState.pendingContextMenu;
|
||||||
contextMenu = aState.contextMenu;
|
contextMenu = aState.contextMenu;
|
||||||
contextMenuTrigger = aState.contextMenuTrigger;
|
contextMenuTrigger = aState.contextMenuTrigger;
|
||||||
cofunc = aState.cofunc;
|
cofunc = aState.cofunc;
|
||||||
wakeupEvent = aState.wakeupEvent;
|
wakeupEvent = aState.wakeupEvent;
|
||||||
waitEvents = aState.waitEvents;
|
waitEvents = aState.waitEvents;
|
||||||
transitions = aState.transitions;
|
transitions = aState.transitions;
|
||||||
vcSettings = aState.vcSettings;
|
vcSettings = aState.vcSettings;
|
||||||
// do not copy stateStack
|
// do not copy stateStack
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,6 +82,9 @@ struct TOOL_MANAGER::TOOL_STATE
|
||||||
/// Is the tool active (pending execution) or disabled at the moment
|
/// Is the tool active (pending execution) or disabled at the moment
|
||||||
bool idle;
|
bool idle;
|
||||||
|
|
||||||
|
/// Should the tool shutdown during next execution
|
||||||
|
bool shutdown;
|
||||||
|
|
||||||
/// Flag defining if the tool is waiting for any event (i.e. if it
|
/// Flag defining if the tool is waiting for any event (i.e. if it
|
||||||
/// issued a Wait() call).
|
/// issued a Wait() call).
|
||||||
bool pendingWait;
|
bool pendingWait;
|
||||||
|
@ -112,17 +116,18 @@ struct TOOL_MANAGER::TOOL_STATE
|
||||||
|
|
||||||
TOOL_STATE& operator=( const TOOL_STATE& aState )
|
TOOL_STATE& operator=( const TOOL_STATE& aState )
|
||||||
{
|
{
|
||||||
theTool = aState.theTool;
|
theTool = aState.theTool;
|
||||||
idle = aState.idle;
|
idle = aState.idle;
|
||||||
pendingWait = aState.pendingWait;
|
shutdown = aState.shutdown;
|
||||||
|
pendingWait = aState.pendingWait;
|
||||||
pendingContextMenu = aState.pendingContextMenu;
|
pendingContextMenu = aState.pendingContextMenu;
|
||||||
contextMenu = aState.contextMenu;
|
contextMenu = aState.contextMenu;
|
||||||
contextMenuTrigger = aState.contextMenuTrigger;
|
contextMenuTrigger = aState.contextMenuTrigger;
|
||||||
cofunc = aState.cofunc;
|
cofunc = aState.cofunc;
|
||||||
wakeupEvent = aState.wakeupEvent;
|
wakeupEvent = aState.wakeupEvent;
|
||||||
waitEvents = aState.waitEvents;
|
waitEvents = aState.waitEvents;
|
||||||
transitions = aState.transitions;
|
transitions = aState.transitions;
|
||||||
vcSettings = aState.vcSettings;
|
vcSettings = aState.vcSettings;
|
||||||
// do not copy stateStack
|
// do not copy stateStack
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
@ -179,11 +184,12 @@ private:
|
||||||
///> Restores the initial state.
|
///> Restores the initial state.
|
||||||
void clear()
|
void clear()
|
||||||
{
|
{
|
||||||
idle = true;
|
idle = true;
|
||||||
pendingWait = false;
|
shutdown = false;
|
||||||
|
pendingWait = false;
|
||||||
pendingContextMenu = false;
|
pendingContextMenu = false;
|
||||||
cofunc = NULL;
|
cofunc = NULL;
|
||||||
contextMenu = NULL;
|
contextMenu = NULL;
|
||||||
contextMenuTrigger = CMENU_OFF;
|
contextMenuTrigger = CMENU_OFF;
|
||||||
vcSettings.Reset();
|
vcSettings.Reset();
|
||||||
transitions.clear();
|
transitions.clear();
|
||||||
|
@ -429,6 +435,74 @@ bool TOOL_MANAGER::runTool( TOOL_BASE* aTool )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void TOOL_MANAGER::ShutdownAllTools()
|
||||||
|
{
|
||||||
|
// Create a temporary list of tools to iterate over since when the tools shutdown
|
||||||
|
// they remove themselves from the list automatically (invalidating the iterator)
|
||||||
|
ID_LIST tmpList = m_activeTools;
|
||||||
|
|
||||||
|
for( auto id : tmpList )
|
||||||
|
{
|
||||||
|
ShutdownTool( id );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void TOOL_MANAGER::ShutdownTool( TOOL_ID aToolId )
|
||||||
|
{
|
||||||
|
TOOL_BASE* tool = FindTool( aToolId );
|
||||||
|
|
||||||
|
if( tool && tool->GetType() == INTERACTIVE )
|
||||||
|
ShutdownTool( tool );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void TOOL_MANAGER::ShutdownTool( const std::string& aToolName )
|
||||||
|
{
|
||||||
|
TOOL_BASE* tool = FindTool( aToolName );
|
||||||
|
|
||||||
|
if( tool && tool->GetType() == INTERACTIVE )
|
||||||
|
ShutdownTool( tool );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void TOOL_MANAGER::ShutdownTool( TOOL_BASE* aTool )
|
||||||
|
{
|
||||||
|
wxASSERT( aTool != NULL );
|
||||||
|
|
||||||
|
TOOL_ID id = aTool->GetId();
|
||||||
|
|
||||||
|
if( isActive( aTool ) )
|
||||||
|
{
|
||||||
|
auto it = std::find( m_activeTools.begin(), m_activeTools.end(), id );
|
||||||
|
|
||||||
|
TOOL_STATE* st = m_toolIdIndex[*it];
|
||||||
|
|
||||||
|
// the tool state handler is waiting for events (i.e. called Wait() method)
|
||||||
|
if( st && st->pendingWait )
|
||||||
|
{
|
||||||
|
// Wake up the tool and tell it to shutdown
|
||||||
|
st->shutdown = true;
|
||||||
|
st->pendingWait = false;
|
||||||
|
st->waitEvents.clear();
|
||||||
|
|
||||||
|
if( st->cofunc )
|
||||||
|
{
|
||||||
|
wxLogTrace( kicadTraceToolStack,
|
||||||
|
"TOOL_MANAGER::ShutdownTool is shutting down tool %s",
|
||||||
|
st->theTool->GetName() );
|
||||||
|
|
||||||
|
setActiveState( st );
|
||||||
|
bool end = !st->cofunc->Resume();
|
||||||
|
|
||||||
|
if( end )
|
||||||
|
it = finishTool( st );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
TOOL_BASE* TOOL_MANAGER::FindTool( int aId ) const
|
TOOL_BASE* TOOL_MANAGER::FindTool( int aId ) const
|
||||||
{
|
{
|
||||||
std::map<TOOL_ID, TOOL_STATE*>::const_iterator it = m_toolIdIndex.find( aId );
|
std::map<TOOL_ID, TOOL_STATE*>::const_iterator it = m_toolIdIndex.find( aId );
|
||||||
|
@ -560,7 +634,11 @@ TOOL_EVENT* TOOL_MANAGER::ScheduleWait( TOOL_BASE* aTool, const TOOL_EVENT_LIST&
|
||||||
// switch context back to event dispatcher loop
|
// switch context back to event dispatcher loop
|
||||||
st->cofunc->KiYield();
|
st->cofunc->KiYield();
|
||||||
|
|
||||||
return &st->wakeupEvent;
|
// If the tool should shutdown, it gets a null event to break the loop
|
||||||
|
if( st->shutdown )
|
||||||
|
return nullptr;
|
||||||
|
else
|
||||||
|
return &st->wakeupEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -185,9 +185,9 @@ CVPCB_MAINFRAME::CVPCB_MAINFRAME( KIWAY* aKiway, wxWindow* aParent ) :
|
||||||
|
|
||||||
CVPCB_MAINFRAME::~CVPCB_MAINFRAME()
|
CVPCB_MAINFRAME::~CVPCB_MAINFRAME()
|
||||||
{
|
{
|
||||||
// Be sure a active tool (if exists) is deactivated:
|
// Shutdown all running tools
|
||||||
if( m_toolManager )
|
if( m_toolManager )
|
||||||
m_toolManager->DeactivateTool();
|
m_toolManager->ShutdownAllTools();
|
||||||
|
|
||||||
// Clean up the tool infrastructure
|
// Clean up the tool infrastructure
|
||||||
delete m_actions;
|
delete m_actions;
|
||||||
|
|
|
@ -152,16 +152,16 @@ DISPLAY_FOOTPRINTS_FRAME::DISPLAY_FOOTPRINTS_FRAME( KIWAY* aKiway, wxWindow* aPa
|
||||||
|
|
||||||
DISPLAY_FOOTPRINTS_FRAME::~DISPLAY_FOOTPRINTS_FRAME()
|
DISPLAY_FOOTPRINTS_FRAME::~DISPLAY_FOOTPRINTS_FRAME()
|
||||||
{
|
{
|
||||||
|
// Shutdown all running tools
|
||||||
|
if( m_toolManager )
|
||||||
|
m_toolManager->ShutdownAllTools();
|
||||||
|
|
||||||
GetBoard()->DeleteAllModules();
|
GetBoard()->DeleteAllModules();
|
||||||
GetCanvas()->StopDrawing();
|
GetCanvas()->StopDrawing();
|
||||||
GetCanvas()->GetView()->Clear();
|
GetCanvas()->GetView()->Clear();
|
||||||
// Be sure any event cannot be fired after frame deletion:
|
// Be sure any event cannot be fired after frame deletion:
|
||||||
GetCanvas()->SetEvtHandlerEnabled( false );
|
GetCanvas()->SetEvtHandlerEnabled( false );
|
||||||
|
|
||||||
// Be sure a active tool (if exists) is deactivated:
|
|
||||||
if( m_toolManager )
|
|
||||||
m_toolManager->DeactivateTool();
|
|
||||||
|
|
||||||
delete GetScreen();
|
delete GetScreen();
|
||||||
SetScreen( NULL ); // Be sure there is no double deletion
|
SetScreen( NULL ); // Be sure there is no double deletion
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,9 +89,6 @@ int CVPCB_CONTROL::Main( const TOOL_EVENT& aEvent )
|
||||||
evt->SetPassEvent();
|
evt->SetPassEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
// This tool is supposed to be active forever
|
|
||||||
wxASSERT( false );
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -92,9 +92,6 @@ int CVPCB_FOOTPRINT_VIEWER_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
|
||||||
evt->SetPassEvent();
|
evt->SetPassEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
// This tool is supposed to be active forever
|
|
||||||
wxASSERT( false );
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -211,6 +211,10 @@ LIB_VIEW_FRAME::LIB_VIEW_FRAME( KIWAY* aKiway, wxWindow* aParent, FRAME_T aFrame
|
||||||
|
|
||||||
LIB_VIEW_FRAME::~LIB_VIEW_FRAME()
|
LIB_VIEW_FRAME::~LIB_VIEW_FRAME()
|
||||||
{
|
{
|
||||||
|
// Shutdown all running tools
|
||||||
|
if( m_toolManager )
|
||||||
|
m_toolManager->ShutdownAllTools();
|
||||||
|
|
||||||
if( m_previewItem )
|
if( m_previewItem )
|
||||||
GetCanvas()->GetView()->Remove( m_previewItem );
|
GetCanvas()->GetView()->Remove( m_previewItem );
|
||||||
}
|
}
|
||||||
|
|
|
@ -196,6 +196,10 @@ LIB_EDIT_FRAME::LIB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
|
||||||
|
|
||||||
LIB_EDIT_FRAME::~LIB_EDIT_FRAME()
|
LIB_EDIT_FRAME::~LIB_EDIT_FRAME()
|
||||||
{
|
{
|
||||||
|
// Shutdown all running tools
|
||||||
|
if( m_toolManager )
|
||||||
|
m_toolManager->ShutdownAllTools();
|
||||||
|
|
||||||
// current screen is destroyed in EDA_DRAW_FRAME
|
// current screen is destroyed in EDA_DRAW_FRAME
|
||||||
SetScreen( m_dummyScreen );
|
SetScreen( m_dummyScreen );
|
||||||
|
|
||||||
|
|
|
@ -311,6 +311,10 @@ SCH_EDIT_FRAME::SCH_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ):
|
||||||
|
|
||||||
SCH_EDIT_FRAME::~SCH_EDIT_FRAME()
|
SCH_EDIT_FRAME::~SCH_EDIT_FRAME()
|
||||||
{
|
{
|
||||||
|
// Shutdown all running tools
|
||||||
|
if( m_toolManager )
|
||||||
|
m_toolManager->ShutdownAllTools();
|
||||||
|
|
||||||
delete m_item_to_repeat; // we own the cloned object, see this->SaveCopyForRepeatItem()
|
delete m_item_to_repeat; // we own the cloned object, see this->SaveCopyForRepeatItem()
|
||||||
|
|
||||||
SetScreen( NULL );
|
SetScreen( NULL );
|
||||||
|
|
|
@ -403,9 +403,6 @@ int EE_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
|
||||||
evt->SetPassEvent();
|
evt->SetPassEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
// This tool is supposed to be active forever
|
|
||||||
assert( false );
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -222,6 +222,10 @@ GERBVIEW_FRAME::GERBVIEW_FRAME( KIWAY* aKiway, wxWindow* aParent ):
|
||||||
|
|
||||||
GERBVIEW_FRAME::~GERBVIEW_FRAME()
|
GERBVIEW_FRAME::~GERBVIEW_FRAME()
|
||||||
{
|
{
|
||||||
|
// Shutdown all running tools
|
||||||
|
if( m_toolManager )
|
||||||
|
m_toolManager->ShutdownAllTools();
|
||||||
|
|
||||||
GetCanvas()->GetView()->Clear();
|
GetCanvas()->GetView()->Clear();
|
||||||
|
|
||||||
GetGerberLayout()->GetImagesList()->DeleteAllImages();
|
GetGerberLayout()->GetImagesList()->DeleteAllImages();
|
||||||
|
|
|
@ -236,9 +236,6 @@ int GERBVIEW_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
|
||||||
evt->SetPassEvent();
|
evt->SetPassEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
// This tool is supposed to be active forever
|
|
||||||
assert( false );
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -94,6 +94,36 @@ public:
|
||||||
*/
|
*/
|
||||||
bool InvokeTool( const std::string& aToolName );
|
bool InvokeTool( const std::string& aToolName );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shutdown all tools with a currently registered event loop in this tool manager
|
||||||
|
* by waking them up with a null event.
|
||||||
|
*/
|
||||||
|
void ShutdownAllTools();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shutdown the specified tool by waking it up with a null event to terminate
|
||||||
|
* the processing loop.
|
||||||
|
*
|
||||||
|
* @param aTool is the tool to shutdown
|
||||||
|
*/
|
||||||
|
void ShutdownTool( TOOL_BASE* aTool );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shutdown the specified tool by waking it up with a null event to terminate
|
||||||
|
* the processing loop.
|
||||||
|
*
|
||||||
|
* @param aToolId is the ID of the tool to shutdown
|
||||||
|
*/
|
||||||
|
void ShutdownTool( TOOL_ID aToolId );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shutdown the specified tool by waking it up with a null event to terminate
|
||||||
|
* the processing loop.
|
||||||
|
*
|
||||||
|
* @param aToolName is name of the tool to shutdown
|
||||||
|
*/
|
||||||
|
void ShutdownTool( const std::string& aToolName );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function RunAction()
|
* Function RunAction()
|
||||||
* Runs the specified action. The common format for action names is "application.ToolName.Action".
|
* Runs the specified action. The common format for action names is "application.ToolName.Action".
|
||||||
|
|
|
@ -166,9 +166,9 @@ KICAD_MANAGER_FRAME::KICAD_MANAGER_FRAME( wxWindow* parent, const wxString& titl
|
||||||
|
|
||||||
KICAD_MANAGER_FRAME::~KICAD_MANAGER_FRAME()
|
KICAD_MANAGER_FRAME::~KICAD_MANAGER_FRAME()
|
||||||
{
|
{
|
||||||
// Ensure there are no active tools
|
// Shutdown all running tools
|
||||||
if( m_toolManager )
|
if( m_toolManager )
|
||||||
m_toolManager->DeactivateTool();
|
m_toolManager->ShutdownAllTools();
|
||||||
|
|
||||||
delete m_actions;
|
delete m_actions;
|
||||||
delete m_toolManager;
|
delete m_toolManager;
|
||||||
|
|
|
@ -200,6 +200,10 @@ PL_EDITOR_FRAME::PL_EDITOR_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
|
||||||
|
|
||||||
PL_EDITOR_FRAME::~PL_EDITOR_FRAME()
|
PL_EDITOR_FRAME::~PL_EDITOR_FRAME()
|
||||||
{
|
{
|
||||||
|
// Shutdown all running tools
|
||||||
|
if( m_toolManager )
|
||||||
|
m_toolManager->ShutdownAllTools();
|
||||||
|
|
||||||
// Since the file menu contains file history menus, we must ensure that the menu
|
// Since the file menu contains file history menus, we must ensure that the menu
|
||||||
// destructor is called before the file history objects are deleted since their destructor
|
// destructor is called before the file history objects are deleted since their destructor
|
||||||
// unregisters the menu from the history.
|
// unregisters the menu from the history.
|
||||||
|
|
|
@ -193,9 +193,6 @@ int PL_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
|
||||||
evt->SetPassEvent();
|
evt->SetPassEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
// This tool is supposed to be active forever
|
|
||||||
assert( false );
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -233,6 +233,10 @@ FOOTPRINT_EDIT_FRAME::FOOTPRINT_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent,
|
||||||
|
|
||||||
FOOTPRINT_EDIT_FRAME::~FOOTPRINT_EDIT_FRAME()
|
FOOTPRINT_EDIT_FRAME::~FOOTPRINT_EDIT_FRAME()
|
||||||
{
|
{
|
||||||
|
// Shutdown all running tools
|
||||||
|
if( m_toolManager )
|
||||||
|
m_toolManager->ShutdownAllTools();
|
||||||
|
|
||||||
// save the footprint in the PROJECT
|
// save the footprint in the PROJECT
|
||||||
retainLastFootprint();
|
retainLastFootprint();
|
||||||
|
|
||||||
|
|
|
@ -271,6 +271,10 @@ FOOTPRINT_VIEWER_FRAME::FOOTPRINT_VIEWER_FRAME( KIWAY* aKiway, wxWindow* aParent
|
||||||
|
|
||||||
FOOTPRINT_VIEWER_FRAME::~FOOTPRINT_VIEWER_FRAME()
|
FOOTPRINT_VIEWER_FRAME::~FOOTPRINT_VIEWER_FRAME()
|
||||||
{
|
{
|
||||||
|
// Shutdown all running tools
|
||||||
|
if( m_toolManager )
|
||||||
|
m_toolManager->ShutdownAllTools();
|
||||||
|
|
||||||
GetCanvas()->StopDrawing();
|
GetCanvas()->StopDrawing();
|
||||||
GetCanvas()->GetView()->Clear();
|
GetCanvas()->GetView()->Clear();
|
||||||
// Be sure any event cannot be fired after frame deletion:
|
// Be sure any event cannot be fired after frame deletion:
|
||||||
|
|
|
@ -339,6 +339,10 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
|
||||||
|
|
||||||
PCB_EDIT_FRAME::~PCB_EDIT_FRAME()
|
PCB_EDIT_FRAME::~PCB_EDIT_FRAME()
|
||||||
{
|
{
|
||||||
|
// Shutdown all running tools
|
||||||
|
if( m_toolManager )
|
||||||
|
m_toolManager->ShutdownAllTools();
|
||||||
|
|
||||||
// Since the file menu contains file history menus, we must ensure that the menu
|
// Since the file menu contains file history menus, we must ensure that the menu
|
||||||
// destructor is called before the file history objects are deleted since their destructor
|
// destructor is called before the file history objects are deleted since their destructor
|
||||||
// unregisters the menu from the history.
|
// unregisters the menu from the history.
|
||||||
|
|
|
@ -287,9 +287,6 @@ int SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
|
||||||
evt->SetPassEvent();
|
evt->SetPassEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
// This tool is supposed to be active forever
|
|
||||||
assert( false );
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue