Hello,

I'm attaching an old emails i've posted to the openwengo list discussing implementation of thread safe cross thread signalling,

I think it'll be easy to adapt this suff on QT / Glib cross thread event passing


Thanks
Vadim

--- Begin Message ---
Hello,

I've looked into Event family classes,  and is seems kind of hairy and
messy.

On order to do thread safe events we need today to:

1) Declare a nonsafe event
2) attach a non-safe handler
3) decale a safe event
4) attach a safe handler

The non safe handler have to create a ThreadEvent message and
  post it to the QT queue.


So i've experimented a little bit with boost signals
and managed to create a thread safe signal class.

basicly you  do:

  mtsignal<void (std::string name,  int age)>  sig;

in one thread you do:
  sig.connect(&handler);

and in another:
  sig("vadim", 49);


and the internal implementation
  will create the message and pass it to the QT queue

Actually it even can check if handler thread is == caller thread in do
direct call
in this case as i do in attached example

Enjoy and
Thanks
Vadim

P.S.
I build the example as following:
g++ -g  -o tsig2 -I/usr/local/include/boost-1_33_1/ -L /usr/local/lib
-lboost_signals-gcc-d  tsig2.cpp



#include <boost/signals.hpp>
#include <boost/bind.hpp>
#include <iostream>

using namespace boost;
using namespace std;

// Thread id
int gTID = 1000;

struct XComm
{
  typedef int TargetID;

  struct mess_base
  {
    virtual void callback() = 0;
  };

  template<class Slot> struct mess : public mess_base
  {
    const Slot slot;
    mess(const Slot& s) : slot(s) { }
    virtual void callback() { slot(); }
  };


  template<typename T> static void send(TargetID tid, const T& slot) 
  {
    if (tid == get_tid())
      {
	cout << __FUNCTION__ << ": direct  call to: " << tid  << endl;
	slot();
	return;
      }
    else
      {
	cout << __FUNCTION__ << ": message sent from: " << get_tid() << " to: " << tid  << endl;
	mess<T>  *m = new mess<T>(slot);
	m->callback();
	delete m;
      }
  }

  static TargetID get_tid() { return gTID; }

};

// multihread functor
template<typename Sig, typename Comm>
struct mtfunction : public boost::function<Sig>
{
  typedef function<Sig> base_type;
  typedef typename base_type::result_type result_type;
  typename Comm::TargetID  tid;


  mtfunction() : base_type(), tid(Comm::get_tid()) { }

  template<class T>
  mtfunction(T f) : base_type(f), tid(Comm::get_tid()) { }

  mtfunction(const mtfunction& other) : base_type((const base_type &)other), tid(other.tid) { }

  mtfunction& operator=(const mtfunction& other)
  {
    base_type::operator=(other);
    tid = other.tid;
    return *this;
  }

  static void caller0(base_type *fun) {  (*fun)(); } 
  
  template<class T1>
  static void caller1(base_type *fun, T1 a1) {  (*fun)(a1); } 



  template<class T1, class T2>
  static void caller2(base_type *fun, T1 a1, T2 a2) {  (*fun)(a1,a2); } 


  template<class T1, class T2, class T3>
  static void caller3(base_type *fun, T1 a1, T2 a2, T3 a3) {  (*fun)(a1,a2,a3); } 

  template<class T1, class T2, class T3,  class T4>
  static void caller4(base_type *fun, T1 a1, T2 a2, T3 a3, T4 a4) {  (*fun)(a1,a2,a3,a4); } 

  template<class T1, class T2, class T3, class T4, class T5>
  static void caller5(base_type *fun, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5) {  (*fun)(a1,a2,a3,a4,a5); } 


  template<class T1, class T2, class T3, class T4, class T5, class T6>
  static void caller6(base_type *fun, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6) {  (*fun)(a1,a2,a3,a4,a5,a6); } 



  result_type operator()() const { Comm::send(tid, boost::bind(&caller0, (base_type *)this)); }


  template<class T1>
  result_type operator()(T1 a1) const { Comm::send(tid, boost::bind(&caller1<T1>, (base_type *)this, a1)); }
  

  template<class T1, class T2>
  result_type operator()(T1 a1, T2 a2) const { Comm::send(tid, boost::bind(&caller2<T1,T2>, (base_type *)this, a1, a2)); }



  template<class T1, class T2, class T3>
  result_type operator()(T1 a1, T2 a2, T3 a3) const { Comm::send(tid, boost::bind(&caller3<T1,T2,T3>, (base_type *)this, a1, a2, a3)); }


  template<class T1, class T2, class T3, class T4>
  result_type operator()(T1 a1, T2 a2, T3 a3, T4 a4) const { Comm::send(tid, boost::bind(&caller4<T1,T2,T3,T4>, (base_type *)this, a1, a2, a3, a4)); }


  template<class T1, class T2, class T3, class T4, class T5>
  result_type operator()(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5) const 
  { 
    Comm::send(tid, boost::bind(&caller5<T1,T2,T3,T4,T5>, (base_type *)this, a1, a2, a3, a4, a5)); 
  }


  template<class T1, class T2, class T3, class T4, class T5 , class T6>
  result_type operator()(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6) const 
  { 
    Comm::send(tid, boost::bind(&caller6<T1,T2,T3,T4,T5,T6>, (base_type *)this, a1, a2, a3, a4, a5, a6)); 
  }



};



// multithreaded signals
template<
    typename Signature, // function type R (T1, T2, ..., TN)
    typename Combiner = last_value<typename function_traits<Signature>::result_type>,
    typename Group = int,
    typename GroupCompare = std::less<Group>
  >
  class mtsignal :
    public signal<Signature, 
		  Combiner,
		  Group,
		  GroupCompare,
		  mtfunction<Signature, XComm> >
  {
    typedef signal<Signature,
		   Combiner,
		   Group,
		   GroupCompare,
		   mtfunction<Signature, XComm> > base_type;

  public:
    explicit mtsignal(const Combiner& combiner = Combiner(),
                    const GroupCompare& group_compare = GroupCompare()) :
      base_type(combiner, group_compare)
    {
    }
  };



mtsignal<int (int a)>  sig1;
mtsignal<void (string data)> sig2;


int s1_handler(int a)
{
  cout << "s1_handler:: " << a << endl;
  return 0;
}

int s2_rhandler(const string& a)
{
  cout << "s2_rhandler:: " << a << endl;
  return 0;
}


int s2_handler(string a)
{
  cout << "s2_handler:: " << a << endl;
  return 0;
}



main(int argc, char *argv[])
{

  sig1.connect(&s1_handler);

  cout << "sig1  connected" << endl;

  sig1(33);

  gTID++; // simulate thread switch

  sig1(55);

  cout << "sig1  done" << endl;

  sig2.connect(&s2_rhandler);

  gTID++;

  sig2.connect(&s2_handler);


  sig2("abcd");



}


_______________________________________________
Wengophone-devel mailing list
[email protected]
http://dev.openwengo.com/mailman/listinfo/wengophone-devel

--- End Message ---
--- Begin Message ---


Tanguy Krotoff wrote:

Vadim Lebedev wrote:

Vadim Lebedev wrote:

Hello,

I've looked into Event family classes,  and is seems kind of hairy and
messy.

On order to do thread safe events we need today to:

1) Declare a nonsafe event
2) attach a non-safe handler
3) decale a safe event
4) attach a safe handler

The non safe handler have to create a ThreadEvent message and
  post it to the QT queue.


So i've experimented a little bit with boost signals
and managed to create a thread safe signal class.

basicly you  do:

  mtsignal<void (std::string name,  int age)>  sig;

in one thread you do:
  sig.connect(&handler);

and in another:
  sig("vadim", 49);


and the internal implementation
  will create the message and pass it to the QT queue

Actually it even can check if handler thread is == caller thread in do
direct call
in this case as i do in attached example

Enjoy and
Thanks
Vadim

P.S.
I build the example as following:
g++ -g  -o tsig2 -I/usr/local/include/boost-1_33_1/ -L /usr/local/lib
-lboost_signals-gcc-d  tsig2.cpp


Your implementation is very interesting: very simple and clear.
But how do you manage to call QApplication::postEvent()?


Suppose we  replace the  XComm class  by following:

// Threading parameter is specific for each platform (Win32, posix, etc)
// need to provide  thread_id typedef and static get_current_thread_method

template<class Threading>
classs QtComm
{
public:

 typedef Threading::thread_id TargetID;



 template<class Slot> struct mess : public QCustomEvent
 {
   const Slot slot;
   mess(const Slot& s) : slot(s) { }
   virtual void callback() { slot(); }
 };


template<typename T> static void send(TargetID tid, const T& slot) {
   if (tid == get_tid())
     {
        // cout << __FUNCTION__ << ": direct  call to: " << tid  << endl;
        slot();
        return;
     }
   else
     {
        // cout << __FUNCTION__ << ": message sent from: " << get_tid() << " to: " << 
tid  << endl;
        mess<T>  *m = new mess<T>(slot);
        QApplication::postEvent(m);
     }
 }

 static TargetID get_tid() { return Threading::get_current_thread(); }

};


it seems that Event class and my proposal both suffers from common

deficiency/bug
The registration/activation of event handlers themselves is not thread/safe. I mean a thread A will try to register/unregister event handler just at the moment
when thread B tries to raise the event


I'm not sure to understand. connect() and notify() (e.g operator()) use the same mutex, actions are atomic in this case.

Unless i'm looking in the wring place: http://dev.openwengo.com/trac/openwengo/trac.cgi/browser/owutil/trunk/event/Event.h
i don't see any mutexes there


Thanks
Vadim


_______________________________________________
Wengophone-devel mailing list
[email protected]
http://dev.openwengo.com/mailman/listinfo/wengophone-devel



--- End Message ---
_______________________________________________
QuteCom-dev mailing list
[email protected]
http://lists.qutecom.org/mailman/listinfo/qutecom-dev

Reply via email to