Re: program design



hi Joe, I'm fairily new to gtk programming as well. But I also have separate threads on my project doing different things. When they need to update the GUI, I use the following mechanism to talk to the GUI thread:

http://www.gtkmm.org/gtkmm2/docs/reference/html/classGlib_1_1Dispatcher.html


It's not trivial to tell you the truth, so I wrapped this in my (yet another) thread class. I'm sending attached so you can see/use -- look for the gui_callback method. Btw, the code is LGPL licensed.

hope it helps :)

- jan

ps.: if someone knows an easier way to do this, let me know ;)


----- Original Message ----
From: Joe Van Dyk <joevandyk gmail com>
To: gtkmm-list gnome org
Sent: Monday, June 5, 2006 6:06:35 PM
Subject: Re: program design

On 6/5/06, Hargobind Khalsa <khalsah gmail com> wrote:
> Take a look at this:
> http://gtkmm.org/docs/gtkmm-2.4/docs/tutorial/html/ch18.html
> I think that may be what you are looking for, no threads required.

I don't see how that helps me.  I want my Update() function to update
the GUI each time it is called.

Joe

p.s. Is it possible to make the default reply-to address gtkmm-list gnome org?

> On 6/5/06, Joe Van Dyk <joevandyk gmail com> wrote:
> >
>  Hi,
>
> Imagine you have a program with an Update() function.  This Update()
> function is called X times per second a non-gtkmm-related function.
>
> Also imagine that you have a gtkmm GUI.  Every time Update() is
> called, the GUI should be updated with information that's available
> from inside the Update() function.
>
> How would you set this up?  Would you have the Gtk stuff happening in
> a separate thread?  I suppose you'd have to...  How would you tell the
> gtk thread to update itself?  ( I'm fairly new to multithreaded
> program design, and gtk in general)
>
> Thanks,
> Joe
> _______________________________________________
> gtkmm-list mailing list
>  gtkmm-list gnome org
> http://mail.gnome.org/mailman/listinfo/gtkmm-list
>
>
_______________________________________________
gtkmm-list mailing list
gtkmm-list gnome org
http://mail.gnome.org/mailman/listinfo/gtkmm-list


#ifndef _FU_THREAD_
#define _FU_THREAD_

#include <pthread.h>
#include <stdexcept>

#include <boost/shared_ptr.hpp>
#include <boost/thread/thread.hpp>

#include <gtkmm.h>
#include <libgnomemm.h>
#include <libglademm.h>


namespace FU {
	/**
	* @brief FlickrUp thread model: 
	* 
	* Typical thread class with the following extras:
	* 
	* <ul>
	* <li><code>cancel()</code> actually kills the thread;</li>
	* <li>Utility method to call the given slot on the GUI thread. It returns after the slot returns from the GUI thread.</li>
	* </ul>
	*/
	class Thread 
	{
	public:
		/**
		 * Start thread infrastructure: needs to be called before entering the main loop and
		 * before the start of any threads
		 **/
		static void init_threads();
	
		/**
		* Constructor
		**/
		Thread();
	
		/**
		* Destructor
		**/
		virtual ~Thread();
	
		/**
		* Start running the thread: called by who is creating the thread
		**/
		void start();
	
		/**
		* Force cancel of thread: should be safe. 
		**/
		void cancel();
	
		/**
		* Wait until thread is finished
		**/
		void join();
	
		/**
		* Callback from the GUI: the slot will be called from the GUI thread, and the
		* current thread will be locked until the slot returns.
		*
		* @param slot called from the GUI thread, can be used to update the interface
		**/
		template < typename T > void gui_callback( const T &slot );
	
		
	protected:
		/**
		* Method that is called when the thread is run. Can be finished with a simple return
		* or throwing an exception.
		**/
		virtual void run() = 0;
		
		/**
		* Allow temporary block of thread cancelation, used to guarantee atomic actions.
		* Remember to unblock soon after.
		**/
		void block_cancel();
	
		/**
		* Unblock of thread cancelation, used after block_cancel()
		**/
		void unblock_cancel();
	
	private:
		bool running, ready, to_end;
		pthread_t pthread;
		boost::shared_ptr< boost::thread > thread;
	
		class Runnable {
		public:
			Runnable( Thread *_t ) : t(_t) {}
			Thread *t;
			void operator() ();
		} runnable;
		friend class Runnable;
	
		static void sigusr_handler(void *);
	
		
		/* Start GUI callback system, to be called before entering the main loop */
		static void gui_start();
		
		/* Dispatcher lock: get hold of this before using the dispatcher */
		static boost::mutex mutex_dispatcher;
	
		/* Application main dispatcher: use emit() to initiate */
		static boost::shared_ptr< Glib::Dispatcher > dispatcher;
	
		/* Dispatcher callback */
		static sigc::signal<void> dispatcher_callback;
	
		/* Dispatcher receiver on gui thread: runs client's slot and releases lock */
		static void dispatcher_receiver();
		
		/* Dispatcher callback return mutex locker */
		static void dispatcher_callback_lock();		
		
		
	public:
		/**
		* Class thrown when thread is to be cancelled: if you catch this rethrow it later.
		**/
		class Cancel {};
	
	};


	//===========================================================
	//
	// Implementation
	//
	//===========================================================
	template < typename T > void Thread::gui_callback( const T &slot )
	{
		boost::mutex::scoped_lock lock( mutex_dispatcher );
				
		// sanity check
		if ( ! dispatcher ) throw std::runtime_error( "Thread:gui_callback(): dispatcher object not created" );
	
		// Can't be cancelled during call to UI: risk losing lock
		block_cancel();
		sigc::connection conn = dispatcher_callback.connect( slot );
		// std::cout << "registered callback" << std::endl;
		dispatcher->emit();
		//std::cout << "emitted to dispatcher" << std::endl;
		dispatcher_callback_lock(); // waits for the GUI to return
		//std::cout << "reacquired lock from dispatcher, method returned" << std::endl;
		conn.disconnect();
		//std::cout << "unregistered callback" << std::endl;
		unblock_cancel();
		// Allow blocks again
	
		//std::cout << "releasing dispatcher" << std::endl;
	}

} // namespace FU

#endif
//
// C++ Implementation: Thread
//
// Description: 
//
//
// Author: Jan Pfeifer <janpf yahoo-inc com>, (C) 2006
//
// Copyright: See COPYING file that comes with this distribution
//
//
#include "FlickrUp.hh"
#include "Thread.hh"

#include <signal.h>
#include <pthread.h>

using namespace std;
using namespace boost;


///////////////////////////////////////////////////////////////////////////////
//
// Threads initialization: handle signal
//
///////////////////////////////////////////////////////////////////////////////
const int canceling_signal = SIGUSR1;


void sig_usr_handler( int intr )
{
	// cerr << "Handler called!!" << endl;
	throw FU::Thread::Cancel();
	//pthread_exit(0);
}

// usr_mask is used by all threads
sigset_t usr_mask;

void FU::Thread::init_threads() 
{
	//cout << "Setting handlers" << endl;

	// Set handler
	struct sigaction sa_usr;
	sa_usr.sa_handler = sig_usr_handler;
	sa_usr.sa_flags = 0;
	sigemptyset( &sa_usr.sa_mask );
	sigaddset( &sa_usr.sa_mask, canceling_signal );
	sa_usr.sa_restorer = 0;
	if ( sigaction( canceling_signal, &sa_usr, 0 ) ) {
		throw runtime_error( "Couldn't set sigaction()" );
	}
	
	// Unmasking signal
	sigemptyset( &usr_mask );
	sigaddset( &usr_mask, canceling_signal );
	sigprocmask(  SIG_UNBLOCK, &usr_mask, 0 );
	
	// Start gui callback mechanism
	Thread::gui_start();
}





///////////////////////////////////////////////////////////////////////////////
//
// GUI callback mechanism: based on GLib::Dispatcher
//
///////////////////////////////////////////////////////////////////////////////

/* Dispatcher lock: get hold of this before using the dispatcher */
boost::mutex FU::Thread::mutex_dispatcher;

/* Application main dispatcher: use emit() to initiate */
boost::shared_ptr< Glib::Dispatcher > FU::Thread::dispatcher;

/* Dispatcher callback */
sigc::signal<void> FU::Thread::dispatcher_callback;


/* Mutex for callback: used to synchronize the running path from
	the client's thread to the gui thread and back */
boost::mutex dispatcher_callback_mutex;

/* Locker on the dispatcher mutex */
boost::shared_ptr< boost::mutex::scoped_lock > dispatcher_callback_locker;

/* Dispatcher callback return mutex locker */
void FU::Thread::dispatcher_callback_lock()
{
	// creates a temporary lock: will only return when main lock has been released
	shared_ptr< mutex::scoped_lock > tmp = shared_ptr< mutex::scoped_lock >
			( new mutex::scoped_lock( dispatcher_callback_mutex ) );
	
	// make it the main lock
	dispatcher_callback_locker = tmp;
}

/* Dispatcher receiver on gui thread: runs client's slot and releases lock */
void FU::Thread::dispatcher_receiver()
{
	// cout << "- emiting callback" << endl;
	dispatcher_callback.emit();
	// cout << "- releasing callback lock" << endl;
	dispatcher_callback_locker.reset();
	
}


void FU::Thread::gui_start()
{
	// Register dispatcher
	dispatcher = shared_ptr<Glib::Dispatcher>( new Glib::Dispatcher );
	dispatcher_callback_lock();
	dispatcher->connect( sigc::ptr_fun( FU::Thread::dispatcher_receiver ) );
}



///////////////////////////////////////////////////////////////////////////////
//
// Implement Thread class
//
///////////////////////////////////////////////////////////////////////////////

FU::Thread::Thread() :
	running(false), ready(false), to_end( false ),
	pthread(0), thread(), runnable( this )
{ }

FU::Thread::~Thread()
{
	if ( running ) {
		cancel();
	}
	if ( pthread ) join();
	pthread = 0;
}

void FU::Thread::start()
{
	// pthread_t p = pthread_self();
	// cout << "Original thread: " << p << endl;
	boost::thread *t;
	t = new boost::thread( runnable );
	thread = shared_ptr< boost::thread >( t );
}

void FU::Thread::Runnable::operator() ()
{
	try {
		// It is already running
		t->running = true;
		t->pthread = pthread_self();
	
		// Set mask appropriately
		t->unblock_cancel();
	
		// Ready: after this point, requests will signal (pthread_kill) to end
		t->ready = true;
	
		// Check for end (if it was asked before we set ready)
		if ( t->to_end ) return;
	
		t->run();
	} catch ( FU::Thread::Cancel & ) {
		// Ok, normal end
	} catch ( exception &e ) {
		cerr << "Exception made thread exit: " << e.what() << endl;
	} catch (...) {
		cerr << "Unknown exception made thread exit" << endl;
	}
	t->ready = false;
	t->running = false;
}

void FU::Thread::cancel()
{
	to_end = true;
	if ( pthread ) pthread_kill( pthread, canceling_signal);
	// cout << "+ canceling thread: " << pthread << endl;
}

void FU::Thread::block_cancel()
{
	pthread_sigmask( SIG_BLOCK,  &usr_mask, 0 );
}


void FU::Thread::unblock_cancel()
{
	pthread_sigmask( SIG_UNBLOCK,  &usr_mask, 0 );
}

void FU::Thread::join()
{
	// cout << "+ joining thread" << endl;
	thread->join();
	thread.reset();
	//cout << "+ thread joined" << endl;
}



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