#include <glibmm/threads.h>
#include <glibmm/timer.h>
#include <glibmm/dispatcher.h>
#include <iostream>
#include <memory>

// Class for worker thread
class Worker
{
private:
  bool kill_;
  int& msg_cnt_;
  Glib::Dispatcher& disp_;
  Glib::Threads::Thread* thread_;
private:
 void main()
  {
    while(!kill_) { // No need for mutexing as this should be atomic
      g_atomic_int_inc(&msg_cnt_); // add 1 to unprocessed message count
      disp_.emit(); // Emit rapidly
      Glib::usleep(10000); // us
    }
  }
public:
  Worker(Glib::Dispatcher& d, int& msg_cnt)
  : kill_(false),
    msg_cnt_(msg_cnt),
    disp_(d),
    thread_(Glib::Threads::Thread::create(sigc::mem_fun(*this, &Worker::main)))
  {
  }

  ~Worker()
  {
    kill_ = true; // No need for mutexing as this should be atomic
    thread_->join();

    std::cout << "Worker::~Worker(): msg_cnt_: " << msg_cnt_ << std::endl;
    if (msg_cnt_ != 0) {
      std::cout << "  Warning: msg_cnt_ not zero!" << std::endl;
    }
  }
};

// This class owns the Worker object and its dispatcher
class WorkerOwner : public sigc::trackable
{
private:
  int msg_cnt_;
  Glib::Dispatcher dispatcher_;
  Worker worker_;
  char x[1 << 24]; // create huge array in order to force the compiler to really
                   // destroy this object on delete (otherwise we might not get
                   // a real segfault)
private:
  void handler()
  {
    g_atomic_int_dec_and_test(&msg_cnt_); // decrement unprocessed message count
  }
public:
  WorkerOwner()
  : msg_cnt_(0),
     worker_(dispatcher_, msg_cnt_)
  {
    dispatcher_.connect(sigc::mem_fun(*this, &WorkerOwner::handler));
	}
  ~WorkerOwner()
  {
  }
};

std::auto_ptr<WorkerOwner> wrk[2];
Glib::RefPtr<Glib::MainLoop> loop;

// We want do destroy the owner of the Worker object at some point,
// here simulated by timeout

void workerowner_destroyer()
{
  wrk[0].reset(); // causes deletion of the WorkerOwner object
}

// This simulates load in the main thread which causes dispatcher
// events to accumulate in the pipe (it is sufficient in the real application
// for events to accumulate for a short while and in the meanwhile destroy
// the dispatcher object)
bool timeout()
{
  for (int i = 0; i < 10000000; i++); // some load
    //std::cout << std::dec << i << std::endl;
  return true;
}

void quit_program()
{
  loop->quit();
}

int main(int argc, char* argv[])
{
  loop = Glib::MainLoop::create(false);

  wrk[0].reset(new WorkerOwner());
  wrk[1].reset(new WorkerOwner());

  Glib::signal_timeout().connect(sigc::ptr_fun(&timeout), 10); // ms
  Glib::signal_timeout().connect_once(sigc::ptr_fun(&workerowner_destroyer), 500);
  Glib::signal_timeout().connect_once(sigc::ptr_fun(&quit_program), 2000);

  loop->run();

  return 0;
}
