views:

570

answers:

6

I'm trying to port some Windows code to Linux, ideally through platform-independent libraries (eg boost), however I'm not sure how to port this bit of event code.

The bit of code involves two threads (lets call them A and B). A wants to do something that only B can, so it sends B a message, then waits for B to say its done. In windows this looks something like:

void foo();//thread a calls this
void bar(HANDLE evt);

void foo()
{
    HANDLE evt = CreateEvent(0,FALSE,FALSE,0);
    bCall(boost::bind(&bar, evt));
    WaitForSingleObject(evt,INFINITE);
    CloseHandle(evt);
}
void bar(HANDLE evt)
{
    doSomething();
    SetEvent(evt);
}

I looked at the boost::thread library, but it didnt seem to have anything that does this, the closes I could see was the boost::condition_variable, but it appears that is means in conjunction with a mutex, which is not the case here.

+1  A: 

you can use boost thread barrier

#include <boost/thread/thread.hpp>
#include <boost/thread/barrier.hpp>
#include <iostream>

void foo(boost::barrier* b)
{
  std::cout << "foo done" << std::endl;
  b->wait();
}


int main()
{
  std::cout << "start foo" << std::endl;
  boost::barrier* b = new boost::barrier(2);

  boost::thread t(&foo, b);
  b->wait();
  std::cout << "after foo done" <<  std::endl;
  t.join();
}
chub
That works in some cases, however barrier lacks a timeout value that is being used in a couple of places.
Fire Lancer
A: 

Under Posix compliant systems you can use Posix IPC. It is used for inter-process / inter-thread messaging. If I remember correctly there is a cygwin port available.

Elmar Weber
A: 

I've done (or seen) all of the following at various times for things like this:

Use a mutex + a condition variable.

Use a pipe, having foo create the pipe and pass the write end of it to bar. Bar then writes into the pipe when bar is done. (This even works multi-process).

Have foo poll on a boolean (yea, this is a bad idea.)

Michael Kohne
A: 

It looks like You're looking for signal-slot mechanizm.

You can find one in:

boost and Qt

both crossplatform.

Qt example:

 #include <QObject>

 class Counter : public QObject
 {
     Q_OBJECT

 public:
     Counter() { m_value = 0; }

     int value() const { return m_value; }

 public slots:
     void setValue(int value);

 signals:
     void valueChanged(int newValue);

 private:
     int m_value;
 };

     Counter a, b;
     QObject::connect(&a, SIGNAL(valueChanged(int)),
                      &b, SLOT(setValue(int)));

     a.setValue(12);     // a.value() == 12, b.value() == 12
     b.setValue(48);     // a.value() == 12, b.value() == 48

void Counter::setValue(int value) { if (value != m_value) { m_value = value; emit valueChanged(value); } }

bua
I don't think the boost signal/slot mechanism has any kind of wait mechanism
the_mandrill
But with signal/slot I don't see a reason to wait()?If something is ready, just publish the message, any interested client would proceed apropriate action.Simple observer desing pattern.
bua
+4  A: 

I think a good, cross-platform equivalent to win32 events is boost::condition, so your code could look something like this:

void foo()
{
    boost::mutex mtxWait; 
    boost::condition cndSignal;

    bCall(boost::bind(&bar, mtxWait, cndSignal));

    boost::mutex::scoped_lock mtxWaitLock(mtxWait);
    cndSignal.wait(mtxWait); // you could also use cndSignal.timed_wait() here
}

void bar(boost::mutex& mtxWait, boost::condition& cndSignal)
{
    doSomething();
    cndSignal.notify_one();
}
Alan
Is the mutex really required for this though? I certainly dot like the fact it means that B could potentially block in bar(if thread A's time slice expired between mtxWaitLock and cndSignal.wait).
Fire Lancer
the second mutex lock is not needed. You don't need to hold the mutex to call notify (there are situations when you would need to).
caspin
yes you're right - I'll modify the code
Alan
+1  A: 

All these answers are too complex, come on people it isn't that hard.

namespace porting
{
   class Event;
   typedef Event* Event_handle;
   static const unsigned k_INFINITE = 0xFFFFFFFF;

   class Event
   {
      friend Event_handle CreateEvent( void );
      friend void CloseHandle( Event_handle evt );
      friend void SetEvent( Event_handle evt );
      friend void WaitForSingleObject( Event_handle evt, unsigned timeout );

      Event( void ) : m_bool(false) { }

      bool m_bool;
      boost::mutex m_mutex;
      boost::condition m_condition;
   };

   Event_handle CreateEvent( void )
   { return new Event; }

   void CloseHandle( Event_handle evt )
   { delete evt; }

   void SetEvent( Event_handle evt )
   {
      evt->m_bool = true;
      evt->m_cond.notify_all();
   }

   void WaitForSingleObject( Event_handle evt, unsigned timeout )
   {
      boost::scoped_lock lock( evt->m_mutex );
      if( timeout == k_INFINITE )
      {
         while( !evt->m_bool )
         {
            evt->m_cond.wait( lock );
         }
      }
      else
      {
         //slightly more complex code for timeouts
      }
   }

}// porting

void foo()
{
   porting::Event_handle evt = porting::CreateEvent();
   bCall( boost::bind(&bar, evt ) );
   porting::WaitForSingleObject( evt, porting::k_INFINITE );
   porting::CloseHandle(evt);
}

void bar( porting::Event_handle evt )
{
   doSomething();
   porting::SetEvent(evt);
}

There is probably a bit more to do to get this fully working as I'm not familiar with the semantics of WaitForSingleObject (what happens if two threads call it at the same time, what happens if the same thread calls it twice). However, the solution will look very much like this.

caspin