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 }