开发者

Cross-Platform equivalent to windows events

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.


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.


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();
}


You could use a promise and a future, from boost thread:

#include <boost\thread.hpp>

boost::promise<bool> prom;

void foo()
{
    auto future = prom.get_future();
    auto result = future.wait_for(boost::chrono::milliseconds(1000));
    // we get here if (a) 1 second passes or (b) bar sets the promise value
    if (result==boost::future_status::ready) 
    { 
        /* bar set the promise value */ 
    }
    if (result==boost::future_status::timeout)
    {
        /* 1 second passed without bar setting promise value */ 
    }
}

void bar()
{
    prom.set_value(true);
}


Since comments are closed for me, I had to post my comments to previous posts as an answer. But actually I'm not answering.

1) There's a problem with @Alan 's solution. The sample code he provided works well. But it is different from Windows Events functionality. When a Windows Event object is set, any number of subsequent calls to WaitForSingleObject immediately returns, showing that the object is in signaled state. But with boost's mutex/condition solution, bar() has to notify the condition for every foo() calls that need it. This makes situation a lot harder for 'cross-platform'ing Windows Event functionality. notify_all() also can't help.

Of course this is somehow solved in @deft_code's sample code by using a boolean variable. (Although it suffers itself from race condition problem. Consider if SetEvent(...) is called dead after while(!evt->m_bool) and before evt->m_cond.wait(lock) from within a separate thread. A deadlock will occur. This can however be solved by using some race condition management techniques to make the two statements while() and wait() atomic.) But it has its own shortcoming:

2) There's also a problem with @deft_code 's code in making use of boost mutex/condition/bool combination:

Event objects in Windows can be named which enables them to be used for inter-process synchronizations. For example, process A can create a named event and set it like this: SetEvent(hFileIsReady). Afterward, whatever number of processes waiting for this event to be set (thereby calling WaitForSingleObject(hFileIsReady)) will immediately continue their normal execution until the event is again reset within process A by ResetEvent(hFileIsReady).

But the combination mutex/condition/bool can't afford such a functionality. Of course, we can use boost named_condition and named_mutex. However, what about the boolean variable which we have to check before waiting?


For anyone involved in or working on porting multi-threaded native Windows C/C++ code to Linux/Mac, we've authored an open source (MIT-licensed) library that implements both manual and auto-reset WIN32 events on top of pthreads, including a complete implementation of WaitForSingleObject and WaitForMultipleObjects, making it the only WFMO port I know of available on Linux/Mac.

pevents is available on GitHub and has been fairly battle-tested and is in use by some big names; there is also a boost port of pevents floating around somewhere.

Using pevents will make porting code from Windows significantly easier as the underlying paradigms are dramatically different between Windows and posix platforms - though I'd encourage anyone writing multi-platform code to use an existing cross-platform multithreading library like boost in the first place.


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(2);

  boost::thread t(&foo, &b);
  b.wait();
  std::cout << "after foo done" <<  std::endl;
  t.join();
}


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.)


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);
    }
}


From Boost.Thread version 1.47 documentation:

The classes condition_variable and condition_variable_any provide a mechanism for one thread to wait for notification from another thread that a particular condition has become true.


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜