Commit 1a5e9dae authored by Sebastian Eichelbaum's avatar Sebastian Eichelbaum
Browse files

[CHANGE] - extended module deletion. Now the item is deleted after the module has finished

parent f8bb790f
......@@ -25,12 +25,13 @@
#include <iostream>
#include "WConditionOneShot.h"
#include "WCondition.h"
#include "WLogger.h"
#include "WThreadedRunner.h"
WThreadedRunner::WThreadedRunner():
m_shutdownFlag( new WConditionOneShot, false )
m_shutdownFlag( new WConditionOneShot(), false )
{
// initialize members
}
......@@ -61,7 +62,6 @@ void WThreadedRunner::wait( bool requestFinish )
m_thread.join();
}
void WThreadedRunner::requestStop()
{
// first notify
......
......@@ -75,6 +75,11 @@ public:
*/
void wait( bool requestFinish = false );
/**
* This method's purpose is to request a stop without waiting for it.
*/
virtual void requestStop();
protected:
/**
......@@ -88,11 +93,6 @@ protected:
*/
virtual void notifyStop();
/**
* This method's purpose is to request a stop without waiting for it.
*/
virtual void requestStop();
/**
* Thread instance.
*/
......
......@@ -51,6 +51,8 @@
#include "events/WModuleCrashEvent.h"
#include "events/WUpdateTextureSorterEvent.h"
WMainWindow* WQt4Gui::m_mainWindow = NULL;
WQt4Gui::WQt4Gui( int argc, char** argv )
: WGUI( argc, argv )
{
......@@ -104,6 +106,11 @@ void WQt4Gui::moduleError( boost::shared_ptr< WModule > module, const WException
QCoreApplication::postEvent( m_mainWindow, new WModuleCrashEvent( module, exception.what() ) );
}
WMainWindow* WQt4Gui::getMainWindow()
{
return m_mainWindow;
}
int WQt4Gui::run()
{
bool parsingSuccessful = parseOptions();
......
......@@ -131,6 +131,13 @@ public:
*/
virtual void closeCustomWidget( std::string title );
/**
* Returns the current main window instance or NULL if not existent.
*
* \return the main window instance.
*/
static WMainWindow* getMainWindow();
protected:
/**
......@@ -146,7 +153,7 @@ private:
/**
* Main window containing all needed widgets.
*/
WMainWindow* m_mainWindow;
static WMainWindow* m_mainWindow;
/**
* Graphics Engine instance.
......
......@@ -44,6 +44,7 @@
#include "../events/WRoiAssocEvent.h"
#include "../events/WRoiRemoveEvent.h"
#include "../events/WModuleReadyEvent.h"
#include "../events/WModuleDeleteEvent.h"
#include "../events/WEventTypes.h"
#include "../guiElements/WQtApplyModulePushButton.h"
#include "../WMainWindow.h"
......@@ -199,6 +200,35 @@ bool WQtDatasetBrowser::event( QEvent* event )
return true;
}
// a module tree item should be deleted
if ( event->type() == WQT_MODULE_REMOVE_EVENT )
{
WModuleDeleteEvent* e = dynamic_cast< WModuleDeleteEvent* >( event ); // NOLINT
if ( !e )
{
// this should never happen, since the type is set to WQT_Ready_EVENT.
WLogger::getLogger()->addLogMessage( "Event is not an WModuleRemoveEvent although its type claims it. Ignoring event.",
"DatasetBrowser", LL_WARNING );
return true;
}
// grab the module reference and print some info
boost::shared_ptr< WModule > module = e->getTreeItem()->getModule();
WLogger::getLogger()->addLogMessage( "Removing module \"" + module->getName() + "\" from Tree.", "DatasetBrowser", LL_DEBUG );
// remove it from the tree and free last ref count
m_moduleTreeWidget->deleteItem( e->getTreeItem() );
// ref count != 1?
if ( module.use_count() != 1 )
{
wlog::warn( "DatasetBrowser" ) << "Removed module has strange usage count: " << module.use_count() << ". Should be 1 here. " <<
"Module reference is held by someone else.";
}
return true;
}
// a module changed its state to "ready" -> activate it in dataset browser
if ( event->type() == WQT_READY_EVENT )
{
......@@ -209,6 +239,8 @@ bool WQtDatasetBrowser::event( QEvent* event )
// this should never happen, since the type is set to WQT_Ready_EVENT.
WLogger::getLogger()->addLogMessage( "Event is not an WModueReadyEvent although its type claims it. Ignoring event.",
"DatasetBrowser", LL_WARNING );
return true;
}
WLogger::getLogger()->addLogMessage( "Activating module " + e->getModule()->getName() + " in dataset browser.",
......@@ -640,17 +672,17 @@ boost::shared_ptr< WRMROIRepresentation > WQtDatasetBrowser::getFirstRoiInSelect
void WQtDatasetBrowser::deleteModuleTreeItem()
{
// TODO(ebaum): test whether the item is a module/dataset
if ( m_moduleTreeWidget->selectedItems().count() > 0 )
{
boost::shared_ptr< WModule > module = dynamic_cast< WQtTreeItem* >( m_moduleTreeWidget->selectedItems().at( 0 ) )->getModule();
m_moduleTreeWidget->deleteItem( m_moduleTreeWidget->selectedItems().at( 0 ) ) ;
// instruct the kernel to remove module
WKernel::getRunningKernel()->getRootContainer()->remove( module );
// DEBUG:
int count = module.use_count();
std::cout << "COUNT DMTI " << count << std::endl;
if ( ( m_moduleTreeWidget->selectedItems().at( 0 )->type() == MODULE ) ||
( m_moduleTreeWidget->selectedItems().at( 0 )->type() == DATASET ) )
{
// instead of deleting the tree item directly -> inform the tree item and let it do the job:
static_cast< WQtTreeItem* >( m_moduleTreeWidget->selectedItems().at( 0 ) )->deleteSelf();
// select another item
m_moduleTreeWidget->setCurrentItem( m_moduleTreeWidget->topLevelItem( 0 ) );
}
}
}
......
......@@ -25,17 +25,27 @@
#include <set>
#include <string>
#include <QtGui/QApplication>
#include "../../../kernel/WKernel.h"
#include "../../../common/WProgressCombiner.h"
#include "../../../common/WProgress.h"
#include "../../../kernel/WModuleInputConnector.h"
#include "../../../kernel/WModuleOutputConnector.h"
#include "../events/WModuleDeleteEvent.h"
#include "../WQt4Gui.h"
#include "../WMainWindow.h"
#include "WTreeItemTypes.h"
#include "WQtTreeItem.h"
WQtTreeItem::WQtTreeItem( QTreeWidgetItem * parent, WTreeItemType type, boost::shared_ptr< WModule > module ) :
QTreeWidgetItem( parent, type )
QTreeWidgetItem( parent, type ),
m_deleteInProgress( false ),
m_needPostDeleteEvent( true )
{
m_module = module;
m_name = module->getName();
......@@ -148,7 +158,33 @@ void WQtTreeItem::updateState()
setText( 0, m_name.c_str() );
}
// if the user requested it to be deleted: disable and color it
if ( m_deleteInProgress )
{
setForeground( 0, QBrush( QColor::fromRgb( 255, 0, 0 ) ) );
setDisabled( true );
}
// is finished?
if ( !m_module->isRunning().get() && m_needPostDeleteEvent )
{
m_needPostDeleteEvent = false; // this ensures the event is only posted once
QCoreApplication::postEvent( WQt4Gui::getMainWindow()->getDatasetBrowser(), new WModuleDeleteEvent( this ) );
}
// update tooltip
updateTooltip( progress );
}
void WQtTreeItem::deleteSelf()
{
// instruct the kernel to remove module
WKernel::getRunningKernel()->getRootContainer()->remove( m_module );
// update tree item state
m_deleteInProgress = true;
// instruct the module to finish
m_module->requestStop();
}
......@@ -50,7 +50,7 @@ public:
* \param module The represented module
* \param type the type used for the treeitem. Used to identify the items.
*/
WQtTreeItem( QTreeWidgetItem * parent, WTreeItemType type, boost::shared_ptr< WModule > module );
WQtTreeItem( QTreeWidgetItem* parent, WTreeItemType type, boost::shared_ptr< WModule > module );
/**
* Destructor.
......@@ -62,7 +62,7 @@ public:
*
* \return the pointer to the module associated with this item.
*/
boost::shared_ptr< WModule >getModule();
boost::shared_ptr< WModule > getModule();
/**
* Returns the name used for this tree item.
......@@ -71,6 +71,11 @@ public:
*/
std::string getName();
/**
* Initiates the item to delete itself from the tree. It also removes the underlying module to be deleted from the module graph.
*/
virtual void deleteSelf();
public slots:
/**
......@@ -102,6 +107,16 @@ protected:
*/
void updateTooltip( std::string progress );
/**
* True if the treeitem and the module gets deleted currently.
*/
bool m_deleteInProgress;
/**
* True if no delete event has been posted yet.
*/
bool m_needPostDeleteEvent;
private:
/**
......
......@@ -54,4 +54,7 @@
// when a roi got removed
#define WQT_ROI_REMOVE_EVENT QEvent::User + 7
// when a module got deleted
#define WQT_MODULE_REMOVE_EVENT QEvent::User + 8
#endif // WEVENTTYPES_H
//---------------------------------------------------------------------------
//
// Project: OpenWalnut ( http://www.openwalnut.org )
//
// Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
// For more information see http://www.openwalnut.org/copying
//
// This file is part of OpenWalnut.
//
// OpenWalnut is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// OpenWalnut 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
//
//---------------------------------------------------------------------------
#include "WEventTypes.h"
#include "WModuleDeleteEvent.h"
WModuleDeleteEvent::WModuleDeleteEvent( WQtTreeItem* treeItem )
: QEvent( static_cast< QEvent::Type >( WQT_MODULE_REMOVE_EVENT ) ),
m_item( treeItem )
{
// initialize members
}
WModuleDeleteEvent::~WModuleDeleteEvent()
{
// cleanup
}
WQtTreeItem* WModuleDeleteEvent::getTreeItem()
{
return m_item;
}
//---------------------------------------------------------------------------
//
// Project: OpenWalnut ( http://www.openwalnut.org )
//
// Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
// For more information see http://www.openwalnut.org/copying
//
// This file is part of OpenWalnut.
//
// OpenWalnut is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// OpenWalnut 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
//
//---------------------------------------------------------------------------
#ifndef WMODULEDELETEEVENT_H
#define WMODULEDELETEEVENT_H
#include <QtCore/QEvent>
#include "../datasetbrowser/WQtTreeItem.h"
/**
* Event signalling a module item should be deleted.
*/
class WModuleDeleteEvent: public QEvent
{
public:
/**
* Creates a new event instance denoting that the specified module got deleted in the root container.
*
* \param treeItem the tree item that switched its state.
*/
explicit WModuleDeleteEvent( WQtTreeItem* treeItem );
/**
* Destructor.
*/
virtual ~WModuleDeleteEvent();
/**
* Getter for the tree item that got outdated.
*
* \return the tree item
*/
WQtTreeItem* getTreeItem();
protected:
/**
* The tree item sent this event.
*/
WQtTreeItem* m_item;
private:
};
#endif // WMODULEDELETEEVENT_H
......@@ -60,6 +60,7 @@ WModule::WModule():
m_isReady( new WConditionOneShot(), false ),
m_isCrashed( new WConditionOneShot(), false ),
m_isReadyOrCrashed( new WConditionSet(), false ),
m_isRunning( new WCondition(), false ),
m_readyProgress( boost::shared_ptr< WProgress >( new WProgress( "Initializing Module" ) ) ),
m_moduleState()
{
......@@ -102,7 +103,7 @@ void WModule::addConnector( boost::shared_ptr< WModuleOutputConnector > con )
m_outputConnectors.insert( con );
}
void WModule::disconnectAll()
void WModule::disconnect()
{
// remove connections and their signals
for( std::set<boost::shared_ptr< WModuleInputConnector > >::iterator listIter = m_inputConnectors.begin();
......@@ -123,7 +124,7 @@ void WModule::removeConnectors()
m_isUsable( m_initialized() && m_isAssociated() );
// remove connections and their signals, this is flat removal. The module container can do deep removal
disconnectAll();
disconnect();
// clean up list
// this should delete the connector since nobody else *should* have another shared_ptr to them
......@@ -302,6 +303,11 @@ const WBoolFlag& WModule::isReadyOrCrashed() const
return m_isReadyOrCrashed;
}
const WBoolFlag& WModule::isRunning() const
{
return m_isRunning;
}
void WModule::notifyConnectionEstablished( boost::shared_ptr< WModuleConnector > /*here*/,
boost::shared_ptr< WModuleConnector > /*there*/ )
{
......@@ -366,6 +372,7 @@ void WModule::threadMain()
WLogger::getLogger()->addLogMessage( "Starting module main method.", "Module (" + getName() + ")", LL_INFO );
// call main thread function
m_isRunning( true );
moduleMain();
}
catch( const WException& e )
......@@ -392,6 +399,8 @@ void WModule::threadMain()
// hopefully, all waiting threads use isReadyOrCrashed to wait.
m_isCrashed( true );
}
m_isRunning( false );
}
wlog::WStreamedLogger WModule::infoLog() const
......
......@@ -173,6 +173,13 @@ public:
*/
const WBoolFlag& isReadyOrCrashed() const;
/**
* Returns a flag denoting whether the thread currently is running or nor. It is also useful to get a callback whenever a module stops.
*
* \return the flag
*/
const WBoolFlag& isRunning() const;
/**
* The container this module is associated with.
*
......@@ -232,6 +239,12 @@ public:
*/
virtual MODULE_TYPE getType() const;
/**
* Completely disconnects all connected connectors of this module. This is useful to isolate a module (for deletion, removal from a container
* and so on.)
*/
void disconnect();
protected:
/**
......@@ -301,11 +314,6 @@ protected:
*/
void removeConnectors();
/**
* Completely disconnects all connected connectors of this module.
*/
void disconnectAll();
/**
* Callback for m_active. Overwrite this in your modules to handle m_active changes separately.
*/
......@@ -440,6 +448,11 @@ protected:
*/
WBoolFlag m_isReadyOrCrashed;
/**
* True if the module currently is running.
*/
WBoolFlag m_isRunning;
/**
* Progress indicator for the "ready" state.
*/
......
......@@ -51,7 +51,8 @@ WModuleContainer::WModuleContainer( std::string name, std::string description ):
m_moduleAccess( m_modules.getAccessObject() ),
m_name( name ),
m_description( description ),
m_crashIfModuleCrashes( true )
m_crashIfModuleCrashes( true ),
m_moduleSubscriptionsAccess( m_moduleSubscriptions.getAccessObject() )
{
WLogger::getLogger()->addLogMessage( "Constructing module container." , "ModuleContainer (" + getName() + ")", LL_INFO );
// initialize members
......@@ -110,17 +111,21 @@ void WModuleContainer::add( boost::shared_ptr< WModule > module, bool run )
LL_INFO );
// now module->isUsable() is true
// -> so run it
// Connect the error handler and all default handlers:
m_moduleSubscriptionsAccess->beginWrite();
// connect the containers signal handler explicitly
t_ModuleErrorSignalHandlerType func = boost::bind( &WModuleContainer::moduleError, this, _1, _2 );
module->subscribeSignal( WM_ERROR, func );
boost::signals2::connection signalCon = module->subscribeSignal( WM_ERROR, func );
m_moduleSubscriptionsAccess->get().insert( ModuleSubscription( module, signalCon ) );
// connect default ready/error notifiers
boost::shared_lock<boost::shared_mutex> slock = boost::shared_lock<boost::shared_mutex>( m_errorNotifiersLock );
for ( std::list< t_ModuleErrorSignalHandlerType >::iterator iter = m_errorNotifiers.begin(); iter != m_errorNotifiers.end(); ++iter)
{
module->subscribeSignal( WM_ERROR, ( *iter ) );
signalCon = module->subscribeSignal( WM_ERROR, ( *iter ) );
m_moduleSubscriptionsAccess->get().insert( ModuleSubscription( module, signalCon ) );
}
slock = boost::shared_lock<boost::shared_mutex>( m_associatedNotifiersLock );
for ( std::list< t_ModuleGenericSignalHandlerType >::iterator iter = m_associatedNotifiers.begin(); iter != m_associatedNotifiers.end(); ++iter)
......@@ -131,9 +136,11 @@ void WModuleContainer::add( boost::shared_ptr< WModule > module, bool run )
slock = boost::shared_lock<boost::shared_mutex>( m_readyNotifiersLock );
for ( std::list< t_ModuleGenericSignalHandlerType >::iterator iter = m_readyNotifiers.begin(); iter != m_readyNotifiers.end(); ++iter)
{
module->subscribeSignal( WM_READY, ( *iter ) );
signalCon = module->subscribeSignal( WM_READY, ( *iter ) );
m_moduleSubscriptionsAccess->get().insert( ModuleSubscription( module, signalCon ) );
}
slock.unlock();
m_moduleSubscriptionsAccess->endWrite();
// add the modules progress to local progress combiner
m_progress->addSubProgress( module->getRootProgressCombiner() );
......@@ -157,15 +164,25 @@ void WModuleContainer::remove( boost::shared_ptr< WModule > module )
return;
}
// stop module
WLogger::getLogger()->addLogMessage( "Waiting for module \"" + module->getName() + "\" to finish." , "ModuleContainer (" + getName() + ")",
LL_DEBUG );
module->wait( true );
// remove connections inside this container
module->disconnect();
// remove progress combiner
m_progress->removeSubProgress( module->getRootProgressCombiner() );
// remove signal subscriptions
// remove signal subscriptions to this containers default notifiers
m_moduleSubscriptionsAccess->beginWrite();
// find all subscriptions for this module
std::pair< ModuleSubscriptionsIterator, ModuleSubscriptionsIterator > subscriptions = m_moduleSubscriptionsAccess->get().equal_range( module );
for( ModuleSubscriptionsIterator it = subscriptions.first; it != subscriptions.second; ++it )
{
// disconnect subscription.
( *it ).second.disconnect();
}
// erase them
m_moduleSubscriptionsAccess->get().erase( subscriptions.first, subscriptions.second );
m_moduleSubscriptionsAccess->endWrite();
// get write lock
m_moduleAccess->beginWrite();
......@@ -173,7 +190,6 @@ void WModuleContainer::remove( boost::shared_ptr< WModule > module )
m_moduleAccess->endWrite();
module->setAssociatedContainer( boost::shared_ptr< WModuleContainer >() );
}
WModuleContainer::DataModuleListType WModuleContainer::getDataModules()
......
......@@ -27,6 +27,7 @@
#include <list>
#include <set>
#include <map>
#include <vector>
#include <string>
......@@ -107,7 +108,10 @@ public:
virtual void add( boost::shared_ptr< WModule > module, bool run = true );
/**
* Remove the given module from this container if it is associated with it. TODO(ebaum): deep removal? flat removal?
* Remove the given module from this container if it is associated with it. It only provides flat removal. It does not remove depending
* modules. Please be aware that this method does NOT stop the module. It just removes it from the container. If you release the shared
* pointer after removing from the container, the instance gets freed although it still might run. To also wait for the module to quit, use
* module->wait( true ).
*
* \param module the module to remove.
*/
......@@ -325,6 +329,49 @@ protected:
bool m_crashIfModuleCrashes;
private:
// the following typedefs are for convenience; to help accessing the container in a thread safe way.
/**
* A type for mapping a module to all its subscriptions
*/
typedef std::pair< boost::shared_ptr< WModule >, boost::signals2::connection > ModuleSubscription;
/**