Cross-Platform equivalent to windows events

18,433

Solution 1

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

Solution 2

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.

Solution 3

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

Solution 4

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?

Solution 5

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.

Share:
18,433

Related videos on Youtube

Carmen
Author by

Carmen

Updated on April 17, 2022

Comments

  • Carmen
    Carmen about 2 years

    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.

  • Carmen
    Carmen over 14 years
    That works in some cases, however barrier lacks a timeout value that is being used in a couple of places.
  • Carmen
    Carmen over 14 years
    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).
  • the_mandrill
    the_mandrill over 14 years
    I don't think the boost signal/slot mechanism has any kind of wait mechanism
  • bua
    bua over 14 years
    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.
  • deft_code
    deft_code over 14 years
    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).
  • baltazar
    baltazar over 12 years
    @bua Implementation of waitForReadyRead method is at least one reason to wait. In this case you must wait for data and you should not block QT events.
  • jww
    jww over 10 years
    How do you specify the timeout in case of a non-INFINITE wait?
  • gsf
    gsf almost 10 years
    there are two major issues with this: 1. There is a race condition between starting the working thread and calling wait in the condition. If the thread starts really fast notify_one could be called before the wait and the main thread to hang. 2. Using stack variable may result in crash, because the exiting from foo() frees the condition, and it might be still in use inside the notify_one call. The proposed barrier solution is in fact safer - even that it might be considered overkill for the purpose.
  • Michaël Roy
    Michaël Roy over 6 years
    Thanks!! I have been looking for a portable WFMO solution for ages. It's a shame the c++ standard doesn't carry anything close to it, AFAIK, boost doesn't either. I definitely have some immediate use for your library.
  • deltanine
    deltanine over 5 years
    promises/futures are one shot only, which means you need to use a new one every time you wish to signal the other thread
  • ihdv
    ihdv about 3 years
    May I ask what is bCall here? And what headers do you include?