1

(I hate having to put a title like this. but I just couldn't find anything better)

I have two classes with two threads. first one detects motion between two frames:

void Detector::run(){

isActive = true;  

// will run forever
while (isActive){

        //code to detect motion for every frame
        //.........................................

        if(isThereMotion)
        {
            if(number_of_sequence>0){

                theRecorder.setRecording(true);                    
                theRecorder.setup();

               // cout << " motion was detected" << endl;

            }
            number_of_sequence++;
        }
        else 
        {
            number_of_sequence = 0;
            theRecorder.setRecording(false);
            // cout << " there was no motion" << endl;
            cvWaitKey (DELAY);

        }
    }
}

second one will record a video when started:

void Recorder::setup(){

    if (!hasStarted){
        this->start();
       }
}

void Recorder::run(){    

     theVideoWriter.open(filename, CV_FOURCC('X','V','I','D'), 20, Size(1980,1080), true);


if (recording){

    while(recording){
    //++++++++++++++++++++++++++++++++++++++++++++++++
    cout <<  recording << endl;
    hasStarted=true;
    webcamRecorder.read(matRecorder); // read a new frame from video
    theVideoWriter.write(matRecorder); //writer the frame into the file

    }

   }
else{
    hasStarted=false;
    cout << "no recording??" << endl;
    changeFilemamePlusOne();
    }
  hasStarted=false;
  cout << "finished recording" << endl;
  theVideoWriter.release();

}

The boolean recording gets changed by the function:

void Recorder::setRecording(bool x){
    recording = x;
}

The goal is to start the recording once motion was detected while preventing the program from starting the recording twice.

The really strange problem, which honestly doesn't make any sense in my head, is that the code will only work if I cout the boolean recording ( marked with the "++++++"). Else recording never changes to false and the code in the else statment never gets called.

Does anyone have an idea on why this is happening. I'm still just begining with c++ but this problem seems really strange to me..

isADon
  • 3,433
  • 11
  • 35
  • 49
  • 2
    Did you try [`std::atomic`](http://en.cppreference.com/w/cpp/atomic/atomic) type for `isActive` and `recording` variables already? – πάντα ῥεῖ Jul 10 '14 at 19:08
  • This is definitely a race condition. The cout introduces enough of a delay to change the execution order. – Alex Reinking Jul 10 '14 at 19:09
  • @AlexReinking _'This is definitely a race condition ...'_ Of course it is. Question is how to come over it :P ... – πάντα ῥεῖ Jul 10 '14 at 19:13
  • How is `recording` declared? If it's just a `bool`, then your problems are entirely expected. – Alan Stokes Jul 10 '14 at 19:13
  • If you're just beginning with C++, why are you using threads at all? – Alan Stokes Jul 10 '14 at 19:13
  • 3
    And before anyone comes up with `volatile`: **NO!** – πάντα ῥεῖ Jul 10 '14 at 19:14
  • yes recoding is a bool. Can you tell me why this is wrong? – isADon Jul 10 '14 at 19:16
  • 1
    @isADon because if you don't declare the variable as `atomic ` the compiler can assume that no other thread will change the value of the field and cache it (this is a nice and simple explanation and totally wrong in the details but it's a good start). – Voo Jul 10 '14 at 19:20
  • @isADon What threading library are you using or what threading standard are you coding to? What does it say about concurrent access to a `bool`? (Most threading libraries state that if a primitive type is accessed in one thread while another thread is, or might be, modifying it, the result is unpredictable. **Thus, you must ensure your code can never do this.**) – David Schwartz Jul 10 '14 at 19:24
  • @DavidSchwartz i've been using QT's QThread for simplicity. It won't allow me the use unless I put flags in the QMake. Is this safe or is there a better way doing this using the QT libraries? – isADon Jul 10 '14 at 19:30
  • 1
    @isADon You can just use [QMutex](http://qt-project.org/doc/qt-5/threads-synchronizing.html) or any of the other synchronization mechanisms QT provides. (Unfortunately, this turns every read into a write of shared data -- the mutex itself -- which hurts performance a bit, but it usually doesn't matter.) – David Schwartz Jul 10 '14 at 19:35
  • @DavidSchwartz going to have to read in to that since I have no idea how to work with QMutex. Thanks for all the help – isADon Jul 10 '14 at 19:38
  • I'm wondering if a state variable might be better here than a bool. The states would be: | no motion + not recording | (nothing to do), | motion + not recording | (start recording), | motion + recording | (nothing to do), | no motion + recording | (stop recording). All of state logic would be handled in the first thread only. The only inter thread communication would be to start or stop recording. – rcgldr Jul 10 '14 at 20:44

3 Answers3

2

I suppose your variables isThereMotion and recording are simple class members of type bool.

Concurrent access to these members isn't thread safe by default, and you'll face race conditions, and all kinds of weird behaviors.

I'd recommend to declare these member variables like this (as long you can make use of the latest standard):

class Detector {
    // ...
    std::atomic<bool> isThereMotion;
};

class Recorder {
    // ...
    std::atomic<bool> hasStarted;
};

etc.

The reason behind the scenes is, that even reading/writing a simple boolean value splits up into several assembler instructions applied to the CPU, and those may be scheduled off in the middle for a thread execution path change of the process. Using std::atomic<> provides something like a critical section for read/write operations on this variable automatically.


In short: Make everything, that is purposed to be accessed concurrently from different threads, an atomic value, or use an appropriate synchronization mechanism like a std::mutex.

If you can't use the latest c++ standard, you can perhaps workaround using boost::thread to keep your code portable.


NOTE:
As from your comments, your question seems to be specific for the Qt framework, there's a number of mechanisms you can use for synchronization as e.g. the mentioned QMutex.


Why volatile doesn't help in multithreaded environments?

volatile prevents the compiler to optimize away actual read access just by assumptions of values set formerly in a sequential manner. It doesn't prevent threads to be interrupted in actually retrieving or writing values there.

volatile should be used for reading from addresses that can be changed independently of the sequential or threading execution model (e.g. bus addressed peripheral HW registers, where the HW changes values actively, e.g. a FPGA reporting current data throughput at a register inteface).

See more details about this misconception here:
Why is volatile not considered useful in multithreaded C or C++ programming?

Community
  • 1
  • 1
πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
  • this worked almost flawlessly. Had to add some flags to my QMake since QT won't allow me just using but after that the bool gets updated correctly. thanks a ton! – isADon Jul 10 '14 at 19:40
  • [Dekker's algorithm](http://en.wikipedia.org/wiki/Dekker%27s_algorithm) could be used with volatile to synchronize threads, but the wait loop for each thread will be cpu bound unless something like a Sleep() can be used in the wait loop. – rcgldr Jul 11 '14 at 03:08
0

You could use a pool of nodes with pointers to frame buffers as part of a linked list fifo messaging system using mutex and semaphore to coordinate the threads. A message for each frame to be recorded would be sent to the recording thread (appended to it's list and a semaphore released), otherwise the node would be returned (appended) back to the main thread's list.

Example code using Windows based synchronization to copy a file. The main thread reads into buffers, the spawned thread writes from buffers it receives. The setup code is lengthy, but the actual messaging functions and the two thread functions are simple and small.

mtcopy.zip

rcgldr
  • 27,407
  • 3
  • 36
  • 61
-2

Could be a liveness issue. The compiler could be re-ordering instructions or hoisting isActive out of the loop. Try marking it as volatile.

From MSDN docs:

Objects that are declared as volatile are not used in certain optimizations because their values can change at any time. The system always reads the current value of a volatile object when it is requested, even if a previous instruction asked for a value from the same object. Also, the value of the object is written immediately on assignment.

Simple example:

#include <iostream>
using namespace std;
int main() {
    bool active = true;
    while(active) {
      cout << "Still active" << endl;
    }
}

Assemble it: g++ -S test.cpp -o test1.a

Add volatile to active as in volatile bool active = true

Assemble it again g++ -S test.cpp -o test2.a and look at the difference diff test1.a test2.a

<   testb   $1, -5(%rbp)
---
>   movb    -5(%rbp), %al
>   testb   $1, %al

Notice the first one doesn't even bother to read the value of active before testing it since the loop body never modifies it. The second version does.

joshbodily
  • 1,038
  • 8
  • 17
  • 1
    What did I say? Did you read my comment? Using `volatile` is blatantly wrong advice here! – πάντα ῥεῖ Jul 10 '14 at 19:35
  • To explain a bit more: The docs you mentioned aren't wrong actually. The problem lies elsewhere. `volatile` prevents the compiler to optimize away actual read access just by assumptions of values set formerly in a sequential manner. It doesn't prevent threads to be interrupted in actually retrieving or writing values there. Such needs to be done with thread synchronization mechanisms as I mentioned in my answer. – πάντα ῥεῖ Jul 10 '14 at 19:47
  • Care to elaborate, other than saying 'NO'? – joshbodily Jul 10 '14 at 19:47
  • 1
    volatile is **NOT** for multithreading, and the reason has nothing to do with optimizations. The value of the `bool` can change between the time the `movb` is executed, and the time the `testb` is executed. – Rob K Jul 10 '14 at 19:48
  • Thanks for clarifying. I'm not saying it's the best or most elegant solution, I'm just trying to help him understand liveness issues as I think that is the root of the problem here – joshbodily Jul 10 '14 at 19:49
  • @joshbodily _'as I think that is the root of the problem here'_ As I explained, it's not. What you mention refers to some orthogonal problem. – πάντα ῥεῖ Jul 10 '14 at 19:55
  • Why volatile is wrong: http://stackoverflow.com/questions/2484980/why-is-volatile-not-considered-useful-in-multithreaded-c-or-c-programming – Rob K Jul 10 '14 at 19:56
  • @πάνταῥεῖ Nobody answered his question as to 'why' this is happening. volatile was just the easiest way to explain the concept of liveness b/c you can easily see the problem in the generated assembly. You explained the how, I explained the why. – joshbodily Jul 10 '14 at 19:57
  • @joshbodily _'volatile was just the easiest way ...'_ But is **wrong** and misleading here, sorry! – πάντα ῥεῖ Jul 10 '14 at 20:03
  • Right, but it's the easiest way to explain the concept of liveness, which *is* the actual issue. I wanted to explain in a SSCCE, volatile was the easiest way to explain the concept of liveness b/c it's clear as day in the assembly. It might not be immediately obvious to a novice programmer that a variable can be changed, but the new value is never used. I don't think the accepted answer adequately explains what is happening, there is just hand waving with 'all kinds of weird behavior.' I thought SO was for educating people, not for getting others to do your programming for you. – joshbodily Jul 10 '14 at 20:14
  • @joshbodily _'I don't think the accepted answer adequately explains ...'_ That's going to be in a bit awkward direction now :P ... Can't you simply accept where you've been **wrong** with an answer? – πάντα ῥεῖ Jul 10 '14 at 20:23
  • I don't see how this is awkward, I just think the OP will always have a niggling doubt about why `isRecording` is modified, but the new value never "takes." I thought my answer was the simplest way to illustrate that compilers will often optimize reads away. A lot of seemingly daunting concepts can be illustrated by code that is not "production-ready" shall we say. – joshbodily Jul 10 '14 at 20:30
  • @joshbodily _'that compilers will often optimize reads away'_ You still didn't get the point. It's not about reads getting optimized away. Also your _SSCCE_ doesn't refer to any [tag:multithreading] behavior (as it was clearly tagged for the Q). **`volatile` doesn't guarantee atomic, thread safe operations, period!** (And the OP seems to have well gotten and adopted my answer as it seems BTW). _"I don't think the accepted answer adequately explains ..."_ Would you mind to elaborate, what's actually missing please? – πάντα ῥεῖ Jul 10 '14 at 20:41