summaryrefslogtreecommitdiff
path: root/examples/thread
diff options
context:
space:
mode:
Diffstat (limited to 'examples/thread')
-rw-r--r--examples/thread/.cvsignore6
-rw-r--r--examples/thread/Makefile.am19
-rw-r--r--examples/thread/dispatcher.cc192
-rw-r--r--examples/thread/dispatcher2.cc254
-rw-r--r--examples/thread/thread.cc114
-rw-r--r--examples/thread/threadpool.cc47
6 files changed, 632 insertions, 0 deletions
diff --git a/examples/thread/.cvsignore b/examples/thread/.cvsignore
new file mode 100644
index 00000000..09983072
--- /dev/null
+++ b/examples/thread/.cvsignore
@@ -0,0 +1,6 @@
+Makefile.in
+Makefile
+dispatcher
+dispatcher2
+thread
+threadpool
diff --git a/examples/thread/Makefile.am b/examples/thread/Makefile.am
new file mode 100644
index 00000000..43605570
--- /dev/null
+++ b/examples/thread/Makefile.am
@@ -0,0 +1,19 @@
+include $(top_srcdir)/examples/Makefile.am_fragment
+
+INCLUDES += $(GTHREAD_CFLAGS)
+
+## Build the executable, but don't install it.
+noinst_PROGRAMS = dispatcher dispatcher2 thread threadpool
+
+dispatcher_SOURCES = dispatcher.cc
+dispatcher_LDFLAGS = $(GTHREAD_LIBS)
+
+dispatcher2_SOURCES = dispatcher2.cc
+dispatcher2_LDFLAGS = $(GTHREAD_LIBS)
+
+thread_SOURCES = thread.cc
+thread_LDFLAGS = $(GTHREAD_LIBS)
+
+threadpool_SOURCES = threadpool.cc
+threadpool_LDFLAGS = $(GTHREAD_LIBS)
+
diff --git a/examples/thread/dispatcher.cc b/examples/thread/dispatcher.cc
new file mode 100644
index 00000000..9cf22dd5
--- /dev/null
+++ b/examples/thread/dispatcher.cc
@@ -0,0 +1,192 @@
+/*
+ * Glib::Dispatcher example -- cross thread signalling
+ * by Daniel Elstner <daniel.elstner@gmx.net>
+ *
+ * Copyright (c) 2002 Free Software Foundation
+ */
+
+#include <sigc++/class_slot.h>
+#include <glibmm.h>
+#include <gtkmm/box.h>
+#include <gtkmm/button.h>
+#include <gtkmm/buttonbox.h>
+#include <gtkmm/main.h>
+#include <gtkmm/progressbar.h>
+#include <gtkmm/stock.h>
+#include <gtkmm/window.h>
+
+#include <algorithm>
+#include <functional>
+#include <list>
+
+
+namespace
+{
+
+class ThreadProgress : public Gtk::ProgressBar
+{
+public:
+ ThreadProgress();
+ virtual ~ThreadProgress();
+
+ void launch();
+ SigC::Signal0<void>& signal_finished();
+
+private:
+ unsigned int progress_;
+ Glib::Dispatcher signal_increment_;
+ SigC::Signal0<void> signal_finished_;
+
+ void progress_increment();
+ void thread_function();
+};
+
+class MainWindow : public Gtk::Window
+{
+public:
+ MainWindow();
+ virtual ~MainWindow();
+
+ void launch_threads();
+
+protected:
+ virtual bool on_delete_event(GdkEventAny* event);
+
+private:
+ std::list<ThreadProgress*> progress_bars_;
+ Gtk::Button* close_button_;
+
+ void on_progress_finished(ThreadProgress* progress);
+};
+
+
+ThreadProgress::ThreadProgress()
+:
+ progress_ (0)
+{
+ // Connect to the cross-thread signal.
+ signal_increment_.connect(SigC::slot(*this, &ThreadProgress::progress_increment));
+}
+
+ThreadProgress::~ThreadProgress()
+{}
+
+void ThreadProgress::launch()
+{
+ // Create a non-joinable thread -- it's deleted automatically on thread exit.
+ Glib::Thread::create(SigC::slot_class(*this, &ThreadProgress::thread_function), false);
+}
+
+SigC::Signal0<void>& ThreadProgress::signal_finished()
+{
+ return signal_finished_;
+}
+
+void ThreadProgress::progress_increment()
+{
+ // Use an integer because floating point arithmetic is inaccurate --
+ // we want to finish *exactly* after the 1000th increment.
+ ++progress_;
+
+ const double fraction = double(progress_) / 1000.0;
+ set_fraction(std::min(fraction, 1.0));
+
+ if(progress_ >= 1000)
+ signal_finished_();
+}
+
+void ThreadProgress::thread_function()
+{
+ Glib::Rand rand;
+ int usecs = 5000;
+
+ for(int i = 0; i < 1000; ++i)
+ {
+ usecs = rand.get_int_range(std::max(0, usecs - 1000 - i), std::min(20000, usecs + 1000 + i));
+ Glib::usleep(usecs);
+
+ // Tell the GUI thread to increment the progress bar value.
+ signal_increment_();
+ }
+}
+
+
+MainWindow::MainWindow()
+:
+ close_button_ (0)
+{
+ set_title("Thread Dispatcher Example");
+
+ Gtk::VBox *const vbox = new Gtk::VBox(false, 10);
+ add(*Gtk::manage(vbox));
+ vbox->set_border_width(10);
+
+ for(int i = 0; i < 5; ++i)
+ {
+ ThreadProgress *const progress = new ThreadProgress();
+ vbox->pack_start(*Gtk::manage(progress), Gtk::PACK_SHRINK);
+ progress_bars_.push_back(progress);
+
+ progress->signal_finished().connect(
+ SigC::bind(SigC::slot(*this, &MainWindow::on_progress_finished), progress));
+ }
+
+ Gtk::ButtonBox *const button_box = new Gtk::HButtonBox();
+ vbox->pack_end(*Gtk::manage(button_box), Gtk::PACK_SHRINK);
+
+ close_button_ = new Gtk::Button(Gtk::Stock::CLOSE);
+ button_box->pack_start(*Gtk::manage(close_button_), Gtk::PACK_SHRINK);
+ close_button_->set_flags(Gtk::CAN_DEFAULT);
+ close_button_->grab_default();
+ close_button_->set_sensitive(false);
+ close_button_->signal_clicked().connect(SigC::slot(*this, &Gtk::Widget::hide));
+
+ show_all_children();
+ set_default_size(300, -1);
+}
+
+MainWindow::~MainWindow()
+{}
+
+void MainWindow::launch_threads()
+{
+ std::for_each(
+ progress_bars_.begin(), progress_bars_.end(),
+ std::mem_fun(&ThreadProgress::launch));
+}
+
+bool MainWindow::on_delete_event(GdkEventAny*)
+{
+ // Don't allow closing the window before all threads finished.
+ return !progress_bars_.empty();
+}
+
+void MainWindow::on_progress_finished(ThreadProgress* progress)
+{
+ progress_bars_.remove(progress);
+
+ // Enable the close button when all threads finished.
+ if(progress_bars_.empty())
+ close_button_->set_sensitive(true);
+}
+
+} // anonymous namespace
+
+
+int main(int argc, char** argv)
+{
+ Glib::thread_init();
+ Gtk::Main main_instance (&argc, &argv);
+
+ MainWindow window;
+
+ // Install a one-shot idle handler to launch the threads
+ // right after the main window has been displayed.
+ Glib::signal_idle().connect(
+ SigC::bind_return(SigC::slot(window, &MainWindow::launch_threads), false));
+
+ Gtk::Main::run(window);
+
+ return 0;
+}
+
diff --git a/examples/thread/dispatcher2.cc b/examples/thread/dispatcher2.cc
new file mode 100644
index 00000000..2bb72b90
--- /dev/null
+++ b/examples/thread/dispatcher2.cc
@@ -0,0 +1,254 @@
+/*
+ * original Glib::Dispatcher example -- cross thread signalling
+ * by Daniel Elstner <daniel.elstner@gmx.net>
+ *
+ * Modified by Stephan Puchegger <stephan.puchegger@ap.univie.ac.at>
+ * to contain 2 mainloops in 2 different threads, that communicate
+ * via cross thread signalling in both directions. The timer thread
+ * sends the UI thread a cross thread signal every second, which in turn
+ * updates the label stating how many seconds have passed since the start
+ * of the program.
+ *
+ * Note: This example is special stuff that's seldomly needed by the
+ * vast majority of applications. Don't bother working out what this
+ * code does unless you know for sure you need 2 main loops running in
+ * 2 distinct main contexts.
+ *
+ * Copyright (c) 2002 Free Software Foundation
+ */
+
+#include <sigc++/class_slot.h>
+#include <glibmm.h>
+#include <gtkmm/box.h>
+#include <gtkmm/button.h>
+#include <gtkmm/buttonbox.h>
+#include <gtkmm/main.h>
+#include <gtkmm/label.h>
+#include <gtkmm/stock.h>
+#include <gtkmm/window.h>
+#include <sstream>
+
+
+namespace
+{
+
+class ThreadTimer : public Gtk::Label
+{
+public:
+ ThreadTimer();
+ ~ThreadTimer();
+
+ Glib::Thread* launch();
+ void signal_finished_emit();
+
+private:
+ unsigned int time_;
+ Glib::Dispatcher signal_increment_;
+ Glib::Dispatcher* signal_finished_ptr_;
+
+ Glib::Mutex startup_mutex_;
+ Glib::Cond startup_cond_;
+
+ void timer_increment();
+ bool timeout_handler();
+ static void finished_handler(Glib::RefPtr<Glib::MainLoop> mainloop);
+ void thread_function();
+};
+
+class MainWindow : public Gtk::Window
+{
+public:
+ MainWindow();
+
+ void launch_thread();
+
+protected:
+ bool on_delete_event(GdkEventAny* event);
+ void on_button_clicked();
+
+private:
+ Glib::Thread* producer_;
+
+ ThreadTimer* timer_label_;
+ Gtk::Button* close_button_;
+};
+
+
+ThreadTimer::ThreadTimer()
+:
+ time_ (0),
+ // Create a new dispatcher that is attached to the default main context,
+ // which is the one Gtk::Main is using.
+ signal_increment_ (),
+ // This pointer will be initialized later by the 2nd thread.
+ signal_finished_ptr_ (0)
+{
+ // Connect the cross-thread signal.
+ signal_increment_.connect(SigC::slot(*this, &ThreadTimer::timer_increment));
+}
+
+ThreadTimer::~ThreadTimer()
+{}
+
+Glib::Thread* ThreadTimer::launch()
+{
+ // Unfortunately, the thread creation has to be fully synchronized in
+ // order to access the Dispatcher object instantiated by the 2nd thread.
+ // So, let's do some kind of hand-shake using a mutex and a condition
+ // variable.
+ Glib::Mutex::Lock lock (startup_mutex_);
+
+ // Create a joinable thread -- it needs to be joined, otherwise it's a memory leak.
+ Glib::Thread *const thread = Glib::Thread::create(
+ SigC::slot_class(*this, &ThreadTimer::thread_function), true);
+
+ // Wait for the 2nd thread's startup notification.
+ while(signal_finished_ptr_ == 0)
+ startup_cond_.wait(startup_mutex_);
+
+ return thread;
+}
+
+void ThreadTimer::signal_finished_emit()
+{
+ // Cause the 2nd thread's main loop to quit.
+ signal_finished_ptr_->emit();
+ signal_finished_ptr_ = 0;
+}
+
+void ThreadTimer::timer_increment()
+{
+ // another second has passed since the start of the program
+ ++time_;
+
+ std::ostringstream out;
+ out << time_ << " seconds since start";
+
+ set_text(out.str());
+}
+
+// static
+void ThreadTimer::finished_handler(Glib::RefPtr<Glib::MainLoop> mainloop)
+{
+ // quit the timer thread mainloop
+ mainloop->quit();
+}
+
+bool ThreadTimer::timeout_handler()
+{
+ // inform the UI thread that another second has passed
+ signal_increment_();
+
+ // this timer should stay alive
+ return true;
+}
+
+void ThreadTimer::thread_function()
+{
+ // create a new Main Context
+ Glib::RefPtr<Glib::MainContext> context = Glib::MainContext::create();
+ // create a new Main Loop
+ Glib::RefPtr<Glib::MainLoop> mainloop = Glib::MainLoop::create(context, true);
+
+ // attach a timeout handler, that is called every second, to the
+ // newly created MainContext
+ context->signal_timeout().connect(SigC::slot(*this, &ThreadTimer::timeout_handler), 1000);
+
+ // We need to lock while creating the Dispatcher instance,
+ // in order to ensure memory visibility.
+ Glib::Mutex::Lock lock (startup_mutex_);
+
+ // create a new dispatcher, that is connected to the newly
+ // created MainContext
+ Glib::Dispatcher signal_finished (context);
+ signal_finished.connect(SigC::bind(SigC::slot(&ThreadTimer::finished_handler), mainloop));
+
+ signal_finished_ptr_ = &signal_finished;
+
+ // Tell the launcher thread that everything is in place now.
+ startup_cond_.signal();
+ lock.release();
+
+ // start the mainloop
+ mainloop->run();
+}
+
+
+MainWindow::MainWindow()
+:
+ producer_ (0),
+ timer_label_ (0),
+ close_button_ (0)
+{
+ set_title("Thread Dispatcher Example #2");
+
+ Gtk::VBox *const vbox = new Gtk::VBox(false, 10);
+ add(*Gtk::manage(vbox));
+ vbox->set_border_width(10);
+
+ timer_label_ = new ThreadTimer();
+ vbox->pack_start(*Gtk::manage(timer_label_), Gtk::PACK_SHRINK);
+ timer_label_->set_text("0 seconds since start");
+
+ Gtk::ButtonBox *const button_box = new Gtk::HButtonBox();
+ vbox->pack_end(*Gtk::manage(button_box), Gtk::PACK_SHRINK);
+
+ close_button_ = new Gtk::Button(Gtk::Stock::CLOSE);
+ button_box->pack_start(*Gtk::manage(close_button_), Gtk::PACK_SHRINK);
+ close_button_->set_flags(Gtk::CAN_DEFAULT);
+ close_button_->grab_default();
+ close_button_->signal_clicked().connect(SigC::slot(*this, &MainWindow::on_button_clicked));
+
+ show_all_children();
+ set_default_size(300, -1);
+}
+
+void MainWindow::launch_thread()
+{
+ // launch the timer thread
+ producer_ = timer_label_->launch();
+}
+
+bool MainWindow::on_delete_event(GdkEventAny*)
+{
+ // signal the timer thread to stop
+ timer_label_->signal_finished_emit();
+
+ // wait for the timer thread to join
+ producer_->join();
+
+ return false;
+}
+
+void MainWindow::on_button_clicked()
+{
+ // signal the timer thread to stop
+ timer_label_->signal_finished_emit();
+
+ // wait for the timer thread to join
+ producer_->join();
+
+ // hide toplevel, thus stop the UI thread main loop
+ hide();
+}
+
+} // anonymous namespace
+
+
+int main(int argc, char** argv)
+{
+ Glib::thread_init();
+ Gtk::Main main_instance (&argc, &argv);
+
+ MainWindow window;
+
+ // Install a one-shot idle handler to launch the threads
+ // right after the main window has been displayed.
+ Glib::signal_idle().connect(
+ SigC::bind_return(SigC::slot(window, &MainWindow::launch_thread), false));
+
+ Gtk::Main::run(window);
+
+ return 0;
+}
+
diff --git a/examples/thread/thread.cc b/examples/thread/thread.cc
new file mode 100644
index 00000000..aaddff92
--- /dev/null
+++ b/examples/thread/thread.cc
@@ -0,0 +1,114 @@
+
+#include <iostream>
+#include <queue>
+#include <sigc++/class_slot.h>
+#include <glibmm/random.h>
+#include <glibmm/thread.h>
+#include <glibmm/timer.h>
+
+
+namespace
+{
+
+class MessageQueue
+{
+public:
+ MessageQueue();
+ ~MessageQueue();
+
+ void producer();
+ void consumer();
+
+private:
+ Glib::Mutex mutex_;
+ Glib::Cond cond_push_;
+ Glib::Cond cond_pop_;
+ std::queue<int> queue_;
+};
+
+
+MessageQueue::MessageQueue()
+{}
+
+MessageQueue::~MessageQueue()
+{}
+
+void MessageQueue::producer()
+{
+ Glib::Rand rand (1234);
+
+ for(int i = 0; i < 200; ++i)
+ {
+ {
+ Glib::Mutex::Lock lock (mutex_);
+
+ while(queue_.size() >= 64)
+ cond_pop_.wait(mutex_);
+
+ queue_.push(i);
+ std::cout << '*';
+ std::cout.flush();
+
+ cond_push_.signal();
+ }
+
+ if(rand.get_bool())
+ continue;
+
+ Glib::usleep(rand.get_int_range(0, 100000));
+ }
+}
+
+void MessageQueue::consumer()
+{
+ Glib::Rand rand (4567);
+
+ for(;;)
+ {
+ {
+ Glib::Mutex::Lock lock (mutex_);
+
+ while(queue_.empty())
+ cond_push_.wait(mutex_);
+
+ const int i = queue_.front();
+ queue_.pop();
+ std::cout << "\x08 \x08";
+ std::cout.flush();
+
+ cond_pop_.signal();
+
+ if(i >= 199)
+ break;
+ }
+
+ if(rand.get_bool())
+ continue;
+
+ Glib::usleep(rand.get_int_range(10000, 200000));
+ }
+}
+
+}
+
+
+int main(int, char**)
+{
+ Glib::thread_init();
+
+ MessageQueue queue;
+
+ Glib::Thread *const producer = Glib::Thread::create(
+ SigC::slot_class(queue, &MessageQueue::producer), true);
+
+ Glib::Thread *const consumer = Glib::Thread::create(
+ SigC::slot_class(queue, &MessageQueue::consumer), true);
+
+ producer->join();
+ consumer->join();
+
+ std::cout << std::endl;
+
+ return 0;
+}
+
diff --git a/examples/thread/threadpool.cc b/examples/thread/threadpool.cc
new file mode 100644
index 00000000..721f7f2f
--- /dev/null
+++ b/examples/thread/threadpool.cc
@@ -0,0 +1,47 @@
+
+#include <iostream>
+#include <glibmm/random.h>
+#include <glibmm/thread.h>
+#include <glibmm/threadpool.h>
+#include <glibmm/timer.h>
+
+
+namespace
+{
+
+Glib::StaticMutex mutex = GLIBMM_STATIC_MUTEX_INIT;
+
+void print_char(char c)
+{
+ Glib::Rand rand;
+
+ for(int i = 0; i < 100; ++i)
+ {
+ {
+ Glib::Mutex::Lock lock (mutex);
+ std::cout << c;
+ std::cout.flush();
+ }
+ Glib::usleep(rand.get_int_range(10000, 100000));
+ }
+}
+
+} // anonymous namespace
+
+
+int main(int, char**)
+{
+ Glib::thread_init();
+
+ Glib::ThreadPool pool (10);
+
+ for(char c = 'a'; c <= 'z'; ++c)
+ pool.push(SigC::bind(SigC::slot(&print_char), c));
+
+ pool.shutdown();
+
+ std::cout << std::endl;
+
+ return 0;
+}
+