logo top
Main Page   Widgets   glibmm Namespaces   Book  

thread/dispatcher.cc

A Glib::Dispatcher example.

00001 /*
00002  * Glib::Dispatcher example -- cross thread signalling
00003  * by Daniel Elstner  <daniel.kitta@gmail.com>
00004  *
00005  * modified to only use glibmm
00006  * by J. Abelardo Gutierrez <jabelardo@cantv.net>
00007  *
00008  * Copyright (c) 2002-2003  Free Software Foundation
00009  */
00010 
00011 #include <glibmm.h>
00012 
00013 #include <algorithm>
00014 #include <functional>
00015 #include <iostream>
00016 #include <vector>
00017 
00018 namespace
00019 {
00020 
00021 /*
00022  * Note that it does not make sense for this class to inherit from
00023  * sigc::trackable, as doing so would only give a false sense of security.
00024  * Once the thread launch has been triggered, the object has to stay alive
00025  * until the thread has been joined again.  The code running in the thread
00026  * assumes the existence of the object.  If it is destroyed earlier, the
00027  * program will crash, with sigc::trackable or without it.
00028  */
00029 class ThreadProgress
00030 {
00031 public:
00032   explicit ThreadProgress(int id);
00033   virtual ~ThreadProgress();
00034 
00035   int  id() const;
00036   void launch();
00037   void join();
00038   bool unfinished() const;
00039 
00040   sigc::signal<void>& signal_finished();
00041 
00042 private:
00043   enum { ITERATIONS = 100 };
00044 
00045   // Note that the thread does not write to the member data at all.  It only
00046   // reads signal_increment_, which is only written to before the thread is
00047   // lauched.  Therefore, no locking is required.
00048   Glib::Thread*       thread_;
00049   int                 id_;
00050   unsigned int        progress_;
00051   Glib::Dispatcher    signal_increment_;
00052   sigc::signal<void>  signal_finished_;
00053 
00054   void progress_increment();
00055   void thread_function();
00056 };
00057 
00058 class Application : public sigc::trackable
00059 {
00060 public:
00061   Application();
00062   virtual ~Application();
00063 
00064   void run();
00065 
00066 private:
00067   Glib::RefPtr<Glib::MainLoop>  main_loop_;
00068   std::vector<ThreadProgress*>  progress_threads_;
00069 
00070   void launch_threads();
00071   void on_progress_finished(ThreadProgress* thread_progress);
00072 };
00073 
00074 template <class T>
00075 class DeletePtr : public std::unary_function<void, T>
00076 {
00077 public:
00078   void operator()(T ptr) const { delete ptr; }
00079 };
00080 
00081 ThreadProgress::ThreadProgress(int id)
00082 :
00083   thread_   (0),
00084   id_       (id),
00085   progress_ (0)
00086 {
00087   // Connect to the cross-thread signal.
00088   signal_increment_.connect(sigc::mem_fun(*this, &ThreadProgress::progress_increment));
00089 }
00090 
00091 ThreadProgress::~ThreadProgress()
00092 {
00093   // It is an error if the thread is still running at this point.
00094   g_return_if_fail(thread_ == 0);
00095 }
00096 
00097 int ThreadProgress::id() const
00098 {
00099   return id_;
00100 }
00101 
00102 void ThreadProgress::launch()
00103 {
00104   // Create a joinable thread.
00105   thread_ = Glib::Thread::create(sigc::mem_fun(*this, &ThreadProgress::thread_function), true);
00106 }
00107 
00108 void ThreadProgress::join()
00109 {
00110   thread_->join();
00111   thread_ = 0;
00112 }
00113 
00114 bool ThreadProgress::unfinished() const
00115 {
00116   return (progress_ < ITERATIONS);
00117 }
00118 
00119 sigc::signal<void>& ThreadProgress::signal_finished()
00120 {
00121   return signal_finished_;
00122 }
00123 
00124 void ThreadProgress::progress_increment()
00125 {
00126   ++progress_;
00127   std::cout << "Thread " << id_ << ": " << progress_ << '%' << std::endl;
00128 
00129   if (progress_ >= ITERATIONS)
00130     signal_finished_();
00131 }
00132 
00133 void ThreadProgress::thread_function()
00134 {
00135   Glib::Rand rand;
00136 
00137   for (int i = 0; i < ITERATIONS; ++i)
00138   {
00139     Glib::usleep(rand.get_int_range(2000, 20000));
00140 
00141     // Tell the main thread to increment the progress value.
00142     signal_increment_();
00143   }
00144 }
00145 
00146 Application::Application()
00147 :
00148   main_loop_        (Glib::MainLoop::create()),
00149   progress_threads_ (5)
00150 {
00151   // Note that unless you're targetting an embedded platform, you can assume
00152   // exceptions to be enabled.  The #ifdef is only here to make the example
00153   // compile in either case; you may ignore it otherwise.
00154 #ifdef GLIBMM_EXCEPTIONS_ENABLED
00155   try
00156 #endif
00157   {
00158     for (std::vector<ThreadProgress*>::size_type i = 0; i < progress_threads_.size(); ++i)
00159     {
00160       ThreadProgress *const progress = new ThreadProgress(i + 1);
00161       progress_threads_[i] = progress;
00162 
00163       progress->signal_finished().connect(
00164           sigc::bind<1>(sigc::mem_fun(*this, &Application::on_progress_finished), progress));
00165     }
00166   }
00167 #ifdef GLIBMM_EXCEPTIONS_ENABLED
00168   catch (...)
00169   {
00170     // In your own code, you should preferably use a smart pointer
00171     // to ensure exception safety.
00172     std::for_each(progress_threads_.begin(), progress_threads_.end(),
00173                   DeletePtr<ThreadProgress*>());
00174     throw;
00175   }
00176 #endif
00177 }
00178 
00179 Application::~Application()
00180 {
00181   std::for_each(progress_threads_.begin(), progress_threads_.end(),
00182                 DeletePtr<ThreadProgress*>());
00183 }
00184 
00185 void Application::run()
00186 {
00187   // Install a one-shot idle handler to launch the threads.
00188   Glib::signal_idle().connect(
00189       sigc::bind_return(sigc::mem_fun(*this, &Application::launch_threads), false));
00190 
00191   main_loop_->run();
00192 }
00193 
00194 void Application::launch_threads()
00195 {
00196   std::cout << "Launching " << progress_threads_.size() << " threads:" << std::endl;
00197 
00198   std::for_each(progress_threads_.begin(), progress_threads_.end(),
00199                 std::mem_fun(&ThreadProgress::launch));
00200 }
00201 
00202 void Application::on_progress_finished(ThreadProgress* thread_progress)
00203 {
00204   thread_progress->join();
00205 
00206   std::cout << "Thread " << thread_progress->id() << ": finished." << std::endl;
00207 
00208   // Quit if it was the last thread to be joined.
00209   if (std::find_if(progress_threads_.begin(), progress_threads_.end(),
00210                    std::mem_fun(&ThreadProgress::unfinished)) == progress_threads_.end())
00211   {
00212     main_loop_->quit();
00213   }
00214 }
00215 
00216 } // anonymous namespace
00217 
00218 int main(int, char**)
00219 {
00220   Glib::thread_init();
00221 
00222   Application application;
00223   application.run();
00224 
00225   return 0;
00226 }

Generated for glibmm 2.4 by Doxygen 1.5.1 © 1997-2001