glibmm 2.4: thread/dispatcher.cc

Main Page Widgets Namespaces Book

# thread/dispatcher.cc

A Glib::Dispatcher example.

    /*
     * Glib::Dispatcher example -- cross thread signalling
     * by Daniel Elstner  <daniel.elstner@gmx.net>
     *
     * modified to only use glibmm
     * by J. Abelardo Gutierrez <jabelardo@cantv.net>
     *
     * Copyright (c) 2002-2003  Free Software Foundation
     */

    #include <glibmm.h>

    #include <algorithm>
    #include <functional>
    #include <iostream>
    #include <list>
    #include <memory>

    namespace
    {

    class ThreadProgress
    {
    public:
      explicit ThreadProgress(int id);
      virtual ~ThreadProgress();

      void launch();
      void join();

      sigc::signal<void>& signal_finished();
      int id() const;

      virtual void reference() const { ++ref_count_; }
      virtual void unreference() const { if (!(--ref_count_)) delete this; }

    private:
      Glib::Thread*       thread_;
      int                 id_;
      unsigned int        progress_;
      Glib::Dispatcher    signal_increment_;
      sigc::signal<void>  signal_finished_;

      void progress_increment();
      void thread_function();

      mutable int ref_count_;

    };

    class Application : public sigc::trackable
    {
    public:
      Application();
      virtual ~Application();

      void launch_threads();
      void run();

    private:
      Glib::RefPtr<Glib::MainLoop>  main_loop_;
      std::list<ThreadProgress*>    progress_list_;
      std::list<Glib::RefPtr<ThreadProgress> > progress_ref_list_;

      void on_progress_finished(ThreadProgress* thread_progress);
    };

    ThreadProgress::ThreadProgress(int id)
    :
      thread_   (0),
      id_       (id),
      progress_ (0),
      ref_count_(0)
    {
      // Increment the reference count
      reference();
      // Connect to the cross-thread signal.
      signal_increment_.connect(sigc::mem_fun(*this, &ThreadProgress::progress_increment));
    }

    ThreadProgress::~ThreadProgress()
    {}

    void ThreadProgress::launch()
    {
      // Create a joinable thread.
      thread_ = Glib::Thread::create(sigc::mem_fun(*this, &ThreadProgress::thread_function), true);
    }

    void ThreadProgress::join()
    {
      thread_->join();
    }

    sigc::signal<void>& ThreadProgress::signal_finished()
    {
      return signal_finished_;
    }

    int ThreadProgress::id() const
    {
      return id_;
    }

    void ThreadProgress::progress_increment()
    {
      // Use an integer because floating point arithmetic is inaccurate --
      // we want to finish *exactly* after the 100th increment.
      ++progress_;

      std::cout << "Thread " << id_ << ": " << progress_ << '%' << std::endl;

      if(progress_ >= 100)
        signal_finished_();
    }

    void ThreadProgress::thread_function()
    {
      Glib::Rand rand;
      int usecs = 5000;

      for(int i = 0; i < 100; ++i)
      {
        usecs = rand.get_int_range(std::max(0, usecs - 1000 - i), std::min(20000, usecs + 1000 + i));
        Glib::usleep(usecs);

        // Tell the main thread to increment the progress value.
        signal_increment_();
      }
    }

    Application::Application()
    :
      main_loop_ (Glib::MainLoop::create())
    {
      std::cout << "Thread Dispatcher Example." << std::endl;

      for(int i = 1; i <= 5; ++i)
      {
        ThreadProgress* progress=new ThreadProgress(i);
        progress_list_.push_back(progress);
        progress_ref_list_.push_back(Glib::RefPtr<ThreadProgress>(progress));

        progress->signal_finished().connect(
            sigc::bind<1>(sigc::mem_fun(*this, &Application::on_progress_finished), progress));
      }
    }

    Application::~Application()
    {
    }

    void Application::launch_threads()
    {
      std::for_each(progress_list_.begin(), progress_list_.end(),
                    std::mem_fun(&ThreadProgress::launch));
    }

    void Application::run()
    {
      main_loop_->run();
    }

    void Application::on_progress_finished(ThreadProgress* thread_progress)
    {
      {
        progress_list_.remove(thread_progress);
        thread_progress->join();

        std::cout << "Thread " << thread_progress->id()
                  << ": finished." << std::endl;
      }

      if(progress_list_.empty())
        main_loop_->quit();
    }

    } // anonymous namespace

    int main(int, char**)
    {
      Glib::thread_init();

      Application application;

      // Install a one-shot idle handler to launch the threads
      Glib::signal_idle().connect(
          sigc::bind_return(sigc::mem_fun(application, &Application::launch_threads), false));

      application.run();

      return 0;
    }