fd7379e39cb6d89a84677ddaaabcc1b6923bb12e
[ardour.git] / libs / glibmm2 / examples / thread / dispatcher2.cc
1 /*
2  * original Glib::Dispatcher example -- cross thread signalling
3  * by Daniel Elstner  <daniel.elstner@gmx.net>
4  * 
5  * Modified by Stephan Puchegger <stephan.puchegger@ap.univie.ac.at>
6  * to contain 2 mainloops in 2 different threads, that communicate 
7  * via cross thread signalling in both directions. The timer thread
8  * sends the UI thread a cross thread signal every second, which in turn
9  * updates the label stating how many seconds have passed since the start
10  * of the program.
11  *
12  * Modified by J. Abelardo Gutierrez <jabelardo@cantv.net>
13  * to cast all gtkmm out and make it glimm only
14  *
15  * Note:  This example is special stuff that's seldomly needed by the
16  * vast majority of applications.  Don't bother working out what this
17  * code does unless you know for sure you need 2 main loops running in
18  * 2 distinct main contexts.
19  *
20  * Copyright (c) 2002-2003  Free Software Foundation
21  */
22
23 #include <glibmm.h>
24 #include <sstream>
25 #include <iostream>
26
27
28 namespace
29 {
30 Glib::RefPtr<Glib::MainLoop> main_loop;
31
32 class ThreadTimer : public sigc::trackable
33 {
34 public:
35   ThreadTimer();
36   ~ThreadTimer();
37
38   void launch();
39   void signal_finished_emit();
40   void print() const;
41
42   typedef sigc::signal<void> type_signal_end;
43   static type_signal_end& signal_end();
44
45 private:
46   unsigned int      time_;
47   Glib::Dispatcher  signal_increment_;  
48   Glib::Dispatcher* signal_finished_ptr_;
49
50   Glib::Mutex       startup_mutex_;
51   Glib::Cond        startup_cond_;
52   Glib::Thread*     thread_;
53   
54   static type_signal_end signal_end_;
55
56   void timer_increment();
57   bool timeout_handler();
58   static void finished_handler(Glib::RefPtr<Glib::MainLoop> mainloop);
59   void thread_function();
60 };
61
62 //TODO: Rename to avoid confusion with Glib::Dispatcher. murrayc
63 class Dispatcher : public sigc::trackable
64 {
65 public:
66   Dispatcher();
67
68   void launch_thread();
69   void end();
70
71 private:
72   ThreadTimer* timer_;
73 };
74
75 ThreadTimer::ThreadTimer() 
76 :
77   time_ (0),
78   // Create a new dispatcher that is attached to the default main context,
79   signal_increment_ (),
80   // This pointer will be initialized later by the 2nd thread.
81   signal_finished_ptr_ (NULL)
82 {
83   // Connect the cross-thread signal.
84   signal_increment_.connect(sigc::mem_fun(*this, &ThreadTimer::timer_increment));
85 }
86
87 ThreadTimer::~ThreadTimer()
88 {}
89
90 void ThreadTimer::launch()
91 {
92   // Unfortunately, the thread creation has to be fully synchronized in
93   // order to access the Dispatcher object instantiated by the 2nd thread.
94   // So, let's do some kind of hand-shake using a mutex and a condition
95   // variable.
96   Glib::Mutex::Lock lock (startup_mutex_);
97
98   // Create a joinable thread -- it needs to be joined, otherwise it's a memory leak.
99   thread_ = Glib::Thread::create(
100       sigc::mem_fun(*this, &ThreadTimer::thread_function), true);
101
102   // Wait for the 2nd thread's startup notification.
103   while(signal_finished_ptr_ == NULL)
104     startup_cond_.wait(startup_mutex_);
105 }
106
107 void ThreadTimer::signal_finished_emit()
108 {
109   // Cause the 2nd thread's main loop to quit.
110   signal_finished_ptr_->emit();
111
112   // wait for the thread to join
113   if(thread_ != NULL)
114     thread_->join();
115
116   signal_finished_ptr_ = NULL;
117 }
118
119 void ThreadTimer::print() const
120 {
121   std::cout << time_ << " seconds since start" << std::endl;
122 }
123
124 sigc::signal< void >& ThreadTimer::signal_end()
125 {
126   return signal_end_;
127 }
128
129 void ThreadTimer::timer_increment()
130 {
131   // another second has passed since the start of the program
132   ++time_;
133   print();
134
135   if(time_ >= 10)
136     signal_finished_emit();
137 }
138
139 // static
140 void ThreadTimer::finished_handler(Glib::RefPtr<Glib::MainLoop> mainloop)
141 {
142   // quit the timer thread mainloop
143   mainloop->quit();
144   std::cout << "timer thread mainloop finished" << std::endl;
145   ThreadTimer::signal_end().emit();
146 }
147
148 bool ThreadTimer::timeout_handler()
149 {
150   // inform the printing thread that another second has passed
151   signal_increment_();
152
153   // this timer should stay alive
154   return true;
155 }
156
157 void ThreadTimer::thread_function()
158 {
159   // create a new Main Context
160   Glib::RefPtr<Glib::MainContext> context = Glib::MainContext::create();
161   // create a new Main Loop
162   Glib::RefPtr<Glib::MainLoop> mainloop = Glib::MainLoop::create(context, true);
163
164   // attach a timeout handler, that is called every second, to the
165   // newly created MainContext
166   context->signal_timeout().connect(sigc::mem_fun(*this, &ThreadTimer::timeout_handler), 1000);
167
168   // We need to lock while creating the Dispatcher instance,
169   // in order to ensure memory visibility.
170   Glib::Mutex::Lock lock (startup_mutex_);
171
172   // create a new dispatcher, that is connected to the newly
173   // created MainContext
174   Glib::Dispatcher signal_finished (context);
175   
176   signal_finished.connect(sigc::bind(sigc::ptr_fun(&ThreadTimer::finished_handler), mainloop));
177
178   signal_finished_ptr_ = &signal_finished;
179
180   // Tell the launcher thread that everything is in place now.
181   startup_cond_.signal();
182   lock.release();
183
184   // start the mainloop
185   mainloop->run();
186 }
187
188 // initialize static member:
189 ThreadTimer::type_signal_end ThreadTimer::signal_end_;
190
191 Dispatcher::Dispatcher()
192
193   timer_ (NULL)
194 {
195   std::cout << "Thread Dispatcher Example #2" << std::endl;
196
197   timer_ = new ThreadTimer();
198   timer_->signal_end().connect(sigc::mem_fun(*this, &Dispatcher::end));
199   timer_->print();
200 }
201
202 void Dispatcher::launch_thread()
203 {
204   // launch the timer thread
205   timer_->launch();
206 }
207
208 void Dispatcher::end()
209 {
210   // quit the main mainloop
211   main_loop->quit();
212 }
213
214 } // anonymous namespace
215
216
217 int main(int, char**)
218 {
219   Glib::thread_init();
220   main_loop = Glib::MainLoop::create();
221
222   Dispatcher dispatcher;
223
224   // Install a one-shot idle handler to launch the threads
225   Glib::signal_idle().connect(
226       sigc::bind_return(sigc::mem_fun(dispatcher, &Dispatcher::launch_thread), false));
227
228   main_loop->run();
229
230   return 0;
231 }
232