Search code examples
c++multithreadingpocopoco-libraries

Waking-up two threads with a Poco:Condition


using Poco 1.9.0-1 from MSYS2 under Windows 7 (64 bits).

I have one thread, signaling a Poco:Condition for three times, with a sleep of 300ms.

I have two threads using two diferent classes EvListenerA and EvListenerB, that extends from Poco::Runnable, and they are waiting the same Poco::Condition to show a message with std::cout.

With the first and second signals, there are no problems, but when third signal is launched, only the thread EvListenerA capture it correctly.

This is the code:

/*
 * main.cpp
 *
 *  Created on: 6 jun. 2019
 *      Author: ccortiz
 */

#include <Poco/Thread.h>
#include <Poco/Runnable.h>
#include <Poco/Condition.h>
#include <iostream>
using namespace std;

Poco::Condition condicion;
Poco::Mutex     mutex;

class GenEvents:public Poco::Runnable{
public:
    void run(){
        cout << "Launching GenEvents!" << endl;

        for (Poco::UInt32 i=0; i<3; i++){
            cout << "[GenEvents] Event_" << i << endl;
            condicion.broadcast();
            Poco::Thread::sleep(300); //Wait 300ms.
        }
        cout << "Ending GenEvents!" << endl;
    }
};

class EvListenerA:public Poco::Runnable{
public:
    void run(){
        cout << "Launching EvListenerA!" << endl;

        for (Poco::UInt32 i=0; i<3; i++){
            condicion.wait(mutex);
            cout << "   [EvListenerA] Receiving Event_" << i << endl;
        }
        cout << "Ending EvListenerA!" << endl;
    }
};

class EvListenerB:public Poco::Runnable{
public:
    void run(){
        cout << "Launching EvListenerB!" << endl;

        for (Poco::UInt32 i=0; i<3; i++){
            condicion.wait(mutex);
            cout << "   [EvListenerB] Receiving Event_" << i << endl;
        }
        cout << "Ending EvListenerB!" << endl;
    }
};

int main(void){
    Poco::Thread th1; //Hilo que genera 3 eventos.
    Poco::Thread th2; //Hilo que espera 3 eventos.
    Poco::Thread th3; //Hilo que espera 3 eventos.

    GenEvents  genEvents;  //Objeto que implementa el hilo generador de eventos.
    EvListenerA evListenerA; //Objeto que implementa el hilo receptor de eventos.
    EvListenerB evListenerB; //Objeto que implementa el hilo receptor de eventos.

    th2.start(evListenerA);
    th3.start(evListenerB);
    Poco::Thread::sleep(500); //Espera de medio segundo.

    th1.start(genEvents);

    th1.join();
    th2.join();
    th3.join();
}

This is the program output:

Launching EvListenerB!
Launching EvListenerA!
Launching GenEvents!
[GenEvents] Event_0
   [EvListenerB] Receiving Event_0
   [EvListenerA] Receiving Event_0
[GenEvents] Event_1
   [EvListenerA] Receiving Event_1
   [EvListenerB] Receiving Event_1
[GenEvents] Event_2
   [EvListenerA] Receiving Event_2
Ending EvListenerA!
Ending GenEvents!

Why I don't have my "[EvListenerB] Receiving Event_2" in the output?

What happens with EvListenerB and Event_2?

Any idea? Thanks


Solution

  • Hmm, for me this is undefined behaviour. Reference states clearly that Condition is used together with Mutex or FastMutex. When wait is called mutex must be locked! - it is missing in your code.

    Quotes from reference:

    A Condition object is always used in conjunction with a Mutex (or FastMutex) object.

    and

    Unlocks the mutex (which must be locked upon calling wait()) and waits for the given time until the Condition is signalled.

    The given mutex will be locked again upon successfully leaving the function, even in case of an exception.

    So if you want to see descired output you have to call lock/unlock onto mutex:

        for (Poco::UInt32 i=0; i<3; i++)
        {
            mutex.lock();  // <--- lock mutex
            condicion.wait(mutex);
            cout << "   [EvListenerA] Receiving Event_" << i << endl;
            mutex.unlock(); // <--- unlock
        }
    

    do the same change for EvListenerA and EvListenerB classes.

    First you are locking mutex. Then wait is called, it unlocks mutex and we are waiting until condition is signaled, then wait returns and mutex is locked again (before returning from wait). Before leaving out of scope of for loop iteration unlock is called.