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 }