26

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.

Fire Lancer
  • 29,364
  • 31
  • 116
  • 182
  • I think your Windows-specific code is using a mutex under the hood. It just abstracts that away. – rmeador Nov 04 '09 at 23:26
  • possible duplicate of [pthread-like windows manual-reset event](http://stackoverflow.com/questions/178114/pthread-like-windows-manual-reset-event) – jww Feb 11 '14 at 13:36
  • [this question](http://stackoverflow.com/questions/4692717/win32-reset-event-like-synchronization-class-with-boost-c) also has nice info – Loomchild Jan 17 '17 at 10:44

10 Answers10

17

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.

deft_code
  • 57,255
  • 29
  • 141
  • 224
10

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
  • 13,510
  • 9
  • 44
  • 50
  • 1
    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 Nov 04 '09 at 22:47
  • 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). – deft_code Nov 04 '09 at 23:25
  • How do you specify the timeout in case of a non-INFINITE wait? – jww Feb 11 '14 at 13:34
  • 2
    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. – gsf May 28 '14 at 17:38
8

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);
}
oofafu
  • 81
  • 1
  • 2
  • 1
    promises/futures are one shot only, which means you need to use a new one every time you wish to signal the other thread – deltanine Feb 07 '19 at 22:18
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?

Masood Khaari
  • 2,911
  • 2
  • 23
  • 40
4

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.

Mahmoud Al-Qudsi
  • 28,357
  • 12
  • 85
  • 125
  • 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. – Michaël Roy Sep 29 '17 at 14:45
2

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();
}
Robert
  • 2,330
  • 29
  • 47
chub
  • 767
  • 6
  • 11
0

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
  • 11,888
  • 3
  • 47
  • 79
0

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);
    }
}
Tim Sylvester
  • 22,897
  • 2
  • 80
  • 94
bua
  • 4,761
  • 1
  • 26
  • 32
  • 1
    I don't think the boost signal/slot mechanism has any kind of wait mechanism – the_mandrill Nov 04 '09 at 22:47
  • 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 Nov 04 '09 at 23:01
  • @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. – baltazar Jan 13 '12 at 13:41
0

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.

Abe Miessler
  • 82,532
  • 99
  • 305
  • 486
Jean Davy
  • 2,062
  • 1
  • 19
  • 24
-1

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
  • 2,683
  • 28
  • 28