Re: program design
- From: Jan Pfeifer <pfjan yahoo com br>
- To: gtkmm-list gnome org
- Subject: Re: program design
- Date: Tue, 6 Jun 2006 00:02:38 +0200 (CEST)
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]