[gparted/psusi/refactor: 3/19] Add async version of FileSystem::execute_command()



commit ca74ac5f7627876922021cbddc35d16b0fc51f1f
Author: Phillip Susi <psusi ubuntu com>
Date:   Sun Feb 12 19:03:30 2012 -0500

    Add async version of FileSystem::execute_command()
    
    The new function allows the filesystems to execute their external commands
    asynchronously.  The stdout and stderr of the child process are connected
    to pipes that are monitored by a new class PipeCapture, which gets notified
    when the pipes are readable and reads them into the output and error
    ustrings similar to the old execute_command().  Once the pipes have reached
    eof, and the child process has exited, a callback is invoked to indicate
    completion.  The callback is passed a value between 0.0 and 1.0 to indicate
    how much progress has been made, and returns true if cancellation is
    requested.  For now, only a value of 1.0 is used and the return value is
    ignored.
    
    The old execute_command has been implemented using the new async version
    and then blocking the thread on a mutex until completion.

 include/FileSystem.h |   53 ++++++++++++-
 src/FileSystem.cc    |  201 +++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 241 insertions(+), 13 deletions(-)
---
diff --git a/include/FileSystem.h b/include/FileSystem.h
index 2202402..400a2ee 100644
--- a/include/FileSystem.h
+++ b/include/FileSystem.h
@@ -28,36 +28,74 @@
 namespace GParted
 {
 
+// captures output pipe of subprocess into a ustring and emits a progress signal
+// with the value 1.0 when eof is reached
+
+class PipeCapture
+{
+	Glib::ustring &buff;
+	Glib::RefPtr<Glib::IOChannel> channel;
+	sigc::connection connection;
+	bool OnReadable( Glib::IOCondition condition );
+	sigc::slot<bool, double> progressslot;
+public:
+	PipeCapture( int fd, Glib::ustring &buffer, sigc::slot<bool, double> slot );
+	~PipeCapture();
+};
+
 class FileSystem
 {
 public:
 	FileSystem() ;
-	virtual ~FileSystem() {}
+	virtual ~FileSystem();
 
 	virtual const Glib::ustring get_custom_text( CUSTOM_TEXT ttype, int index = 0 ) ;
 	static const Glib::ustring get_generic_text( CUSTOM_TEXT ttype, int index = 0 ) ;
 
 	virtual FS get_filesystem_support() = 0 ;
 	virtual void set_used_sectors( Partition & partition ) = 0 ;
+	virtual void set_used_sectors( Partition & partition, sigc::slot<bool, double> slot );
 	virtual void read_label( Partition & partition ) = 0 ;
+	virtual void read_label( Partition & partition, sigc::slot<bool, double> slot );
 	virtual bool write_label( const Partition & partition, OperationDetail & operationdetail ) = 0 ;
+	virtual void write_label( const Partition & partition, OperationDetail & operationdetail,
+				  sigc::slot<bool, double> slot );
 	virtual void read_uuid( Partition & partition ) = 0 ;
+	virtual void read_uuid( Partition & partition, sigc::slot<bool, double> slot );
 	virtual bool write_uuid( const Partition & partition, OperationDetail & operationdetail ) = 0 ;
+	virtual void write_uuid( const Partition & partition, OperationDetail & operationdetail,
+				 sigc::slot<bool, double> slot );
 	virtual bool create( const Partition & new_partition, OperationDetail & operationdetail ) = 0 ;
+	virtual void create( const Partition & new_partition, OperationDetail & operationdetail,
+			     sigc::slot<bool, double> slot );
 	virtual bool resize( const Partition & partition_new,
 			     OperationDetail & operationdetail,
 			     bool fill_partition = false ) = 0 ;
+	virtual void resize( const Partition & partition_new, OperationDetail & operationdetail,
+			     bool fill_partition, sigc::slot<bool, double> slot );
 	virtual bool move( const Partition & partition_new
 	                 , const Partition & partition_old
 	                 , OperationDetail & operationdetail
 	                 ) = 0 ;
+	virtual void move( const Partition & partition_new, const Partition & partition_old,
+			   OperationDetail & operationdetail, sigc::slot<bool, double> slot );
 	virtual bool copy( const Glib::ustring & src_part_path,
 			   const Glib::ustring & dest_part_path,
 			   OperationDetail & operationdetail ) = 0 ;
+	virtual void copy( const Glib::ustring & src_part_path, const Glib::ustring & dest_part_path,
+			   OperationDetail & operationdetail, sigc::slot<bool, double> slot );
 	virtual bool check_repair( const Partition & partition, OperationDetail & operationdetail ) = 0 ;
-	
+	virtual void check_repair( const Partition & partition, OperationDetail & operationdetail,
+				   sigc::slot<bool, double> slot );
+	sigc::slot<bool, double> unlock_mutex;
+	sigc::slot<bool, double> set_success;
+	Glib::Mutex mutex;
+	bool success;
 protected:
 	int execute_command( const Glib::ustring & command, OperationDetail & operationdetail ) ;
+	void execute_command( const Glib::ustring & command, OperationDetail & operationdetail, sigc::slot<bool, double> progresscb );
+	void execute_command( const Glib::ustring & command, sigc::slot<bool, double> progresscb );
+	bool execute_command_progress( double progress );
 	int execute_command_timed( const Glib::ustring & command
 	                         , OperationDetail & operationdetail
 	                         , bool check_status = true ) ;
@@ -66,12 +104,19 @@ protected:
 
 	//those are used in several places..
 	Glib::ustring output, error ;
+	PipeCapture *outputcapture, *errorcapture;
+	sigc::slot<bool, double> slot;
 	Sector N, S ;
 	int exit_status ;
 	unsigned int index ;
-	
 private:
-
+	bool unlock_mutex_f( double progress );
+	bool set_success_f( double progress );
+	OperationDetail *cmdoperationdetail;
+	int pipecount;
+	sigc::slot<bool, double> progressslot;
+	void store_exit_status( GPid pid, int status );
+	bool running;
 };
 
 } //GParted
diff --git a/src/FileSystem.cc b/src/FileSystem.cc
index 8b02404..8f455e7 100644
--- a/src/FileSystem.cc
+++ b/src/FileSystem.cc
@@ -19,12 +19,72 @@
 #include "../include/FileSystem.h"
 
 #include <cerrno>
+#include <iostream>
 
 namespace GParted
 {
-	
+
+PipeCapture::PipeCapture( int fd, Glib::ustring &string, sigc::slot<bool, double> slot ) : buff( string ), progressslot( slot )
+{
+	// tie fd to string
+	// make channel
+	channel = Glib::IOChannel::create_from_fd( fd );
+	connection = Glib::signal_io().connect(
+		sigc::mem_fun( *this, &PipeCapture::OnReadable ),
+		fd,
+		Glib::IO_IN | Glib::IO_HUP | Glib::IO_ERR );
+}
+
+bool PipeCapture::OnReadable( Glib::IOCondition condition )
+{
+	// read from pipe and store in buff
+	Glib::ustring str;
+	Glib::IOStatus status = channel->read( str, 512 );
+	if (status == Glib::IO_STATUS_NORMAL)
+	{
+		buff += str;
+		return true;
+	}
+	if (status != Glib::IO_STATUS_EOF)
+		std::cerr << "Pipe IOChannel read failed" << std::endl;
+	// signal completion
+	progressslot( 1.0 );
+	return false;
+}
+
+PipeCapture::~PipeCapture()
+{
+	connection.disconnect();
+}
+
 FileSystem::FileSystem()
 {
+	unlock_mutex = sigc::mem_fun( *this, &FileSystem::unlock_mutex_f );
+	set_success = sigc::mem_fun( *this, &FileSystem::set_success_f );
+	mutex.lock();  // set initial state to locked
+}
+
+FileSystem::~FileSystem()
+{
+	mutex.unlock();
+}
+
+bool FileSystem::unlock_mutex_f( double progress )
+{
+	if( progress == 1.0 )
+	{
+		mutex.unlock();
+	}
+	return false;
+}
+
+bool FileSystem::set_success_f( double progress )
+{
+	if ( progress == 1.0 )
+	{
+		success = !exit_status;
+		return slot( 1.0 );
+	} else return false;
 }
 
 const Glib::ustring FileSystem::get_custom_text( CUSTOM_TEXT ttype, int index )
@@ -50,17 +110,73 @@ const Glib::ustring FileSystem::get_generic_text( CUSTOM_TEXT ttype, int index )
 
 int FileSystem::execute_command( const Glib::ustring & command, OperationDetail & operationdetail ) 
 {
-	operationdetail .add_child( OperationDetail( command, STATUS_NONE, FONT_BOLD_ITALIC ) ) ;
+	execute_command( command, operationdetail, unlock_mutex );
+	mutex.lock();  // wait for completion
+	return exit_status ;
+}
 
-	int exit_status = Utils::execute_command( "nice -n 19 " + command, output, error ) ;
+void FileSystem::store_exit_status( GPid pid, int status )
+{
+	exit_status = status;
+	running = false;
+	if (pipecount == 0) // pipes finished first
+		progressslot( 1.0 );
+	Glib::spawn_close_pid( pid );
+}
 
-	if ( ! output .empty() )
-		operationdetail .get_last_child() .add_child( OperationDetail( output, STATUS_NONE, FONT_ITALIC ) ) ;
-	
-	if ( ! error .empty() )
-		operationdetail .get_last_child() .add_child( OperationDetail( error, STATUS_NONE, FONT_ITALIC ) ) ;
+void FileSystem::execute_command( const Glib::ustring & command, sigc::slot<bool, double> progresscb )
+{
+	Glib::Pid pid;
+	// set up pipes for capture
+	int out, err;
+	// spawn external process
+	cmdoperationdetail = 0;
+	Glib::spawn_async_with_pipes(
+		std::string(),
+		Glib::shell_parse_argv( command ),
+		Glib::SPAWN_DO_NOT_REAP_CHILD | Glib::SPAWN_SEARCH_PATH,
+		sigc::slot< void >(),
+		&pid,
+		0,
+		&out,
+		&err );
+	running = true;
+	progressslot = progresscb;
+	Glib::signal_child_watch().connect( sigc::mem_fun( *this, &FileSystem::store_exit_status ), pid );
+	output.clear();
+	error.clear();
+	outputcapture = new PipeCapture( out, output, sigc::mem_fun( *this, &FileSystem::execute_command_progress) );
+	errorcapture = new PipeCapture( err, error, sigc::mem_fun( *this, &FileSystem::execute_command_progress) );
+	pipecount = 2;
+}
 
-	return exit_status ;
+void FileSystem::execute_command( const Glib::ustring & command, OperationDetail & operationdetail, sigc::slot<bool, double> progresscb )
+{
+	operationdetail .add_child( OperationDetail( command, STATUS_EXECUTE, FONT_BOLD_ITALIC ) ) ;
+	execute_command( command, progresscb );
+	cmdoperationdetail = &operationdetail;
+}
+
+bool FileSystem::execute_command_progress( double progress )
+{
+	if (progress < 1.0)
+		progressslot( progress );
+	// propogate up progress, clean up on completion
+	if (--pipecount)
+		return false; // wait for second pipe to eof
+	delete outputcapture;
+	delete errorcapture;
+	if ( cmdoperationdetail )
+	{
+		if ( !output.empty() )
+		cmdoperationdetail->get_last_child().add_child( OperationDetail( output, STATUS_NONE, FONT_ITALIC ) );
+
+		if ( !error.empty() )
+			cmdoperationdetail->get_last_child().add_child( OperationDetail( error, STATUS_NONE, FONT_ITALIC ) );
+	}
+	if ( !running ) // already got exit status
+		progressslot( progress );
+	return false;
 }
 
 //Time command, add results to operation detail and by default set success or failure
@@ -152,4 +268,71 @@ void FileSystem::rm_temp_dir( const Glib::ustring dir_name, OperationDetail & op
 	}
 }
 
+void FileSystem::set_used_sectors( Partition & partition, sigc::slot<bool, double> slot )
+{
+	success = false;
+	slot( 1.0 );
+}
+
+void FileSystem::read_label( Partition & partition, sigc::slot<bool, double> slot )
+{
+	success = false;
+	slot( 1.0 );
+}
+
+void FileSystem::write_label( const Partition & partition, OperationDetail & operationdetail,
+			      sigc::slot<bool, double> slot )
+{
+	success = false;
+	slot( 1.0 );
+}
+
+void FileSystem::read_uuid( Partition & partition, sigc::slot<bool, double> slot )
+{
+	success = false;
+	slot( 1.0 );
+}
+
+void FileSystem::write_uuid( const Partition & partition, OperationDetail & operationdetail,
+			     sigc::slot<bool, double> slot )
+{
+	success = false;
+	slot( 1.0 );
+}
+
+void FileSystem::create( const Partition & new_partition, OperationDetail & operationdetail,
+			 sigc::slot<bool, double> slot )
+{
+	success = false;
+	slot( 1.0 );
+}
+
+void FileSystem::resize( const Partition & partition_new, OperationDetail & operationdetail,
+			 bool fill_partition, sigc::slot<bool, double> slot )
+{
+	success = false;
+	slot( 1.0 );
+}
+
+void FileSystem::move( const Partition & partition_new, const Partition & partition_old,
+		       OperationDetail & operationdetail, sigc::slot<bool, double> slot )
+{
+	success = false;
+	slot( 1.0 );
+}
+
+void FileSystem::copy( const Glib::ustring & src_part_path, const Glib::ustring & dest_part_path,
+		       OperationDetail & operationdetail, sigc::slot<bool, double> slot )
+{
+	success = false;
+	slot( 1.0 );
+}
+
+void FileSystem::check_repair( const Partition & partition, OperationDetail & operationdetail,
+			       sigc::slot<bool, double> slot )
+{
+	success = false;
+	slot( 1.0 );
+}
+
 } //GParted



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]