Help me create a race condition

There’s a title you don’t see every day!

I’m venturing into multi-threading for the first time, but trying to do a bit of groundwork before I go to town with my main project. I want to build a test program that commits a race condition, so that I can study it and know how my IDE responds to them. But I can’t seem to get it to misbehave as it should.

Here is my program. The idea is that both the message thread and SpecialThread would access int i (through a reference) and cause a race condition. I put calls to i on different timer frequencies hoping that they would collide, but they don’t seem to. What am I doing wrong / right??

class MainComponent;

class SpecialThread : public Thread
{
public:
    SpecialThread(MainComponent& mc) : m(mc), Thread("specialThread") {}

    void run() override;
    MainComponent& m;
};


class MainComponent  : public Component,
                       public Timer
{
public:

    MainComponent() : myThread(*this)
    {
        setBounds(100, 100, 300, 400);
        startTimer(6);
        myThread.startThread();
    }

    ~MainComponent()
    {
        myThread.stopThread(0);
    }

    void timerCallback() override
    {
        int& ii = getInt();
        ++ii;
        DBG("message thread " << ii);
    }

    int& getInt()
    {
        return i;
    }

private:
    SpecialThread myThread;
    int i = 0;
};


void SpecialThread::run()
{
    while (true)
    {
        int& ii = m.getInt();
        --ii;
        DBG("special thread " << ii);
        wait(5);
    }
}

Bonus question: how do I exit the thread properly? I put myThread.stopThread(0); in the destructor, but it still asserts when I close the program.

With something simple like an int that you increment/decrement, you may need to do millions of accesses/arithmetic operations before you get an inconsistency, but if you make both the threads access and increment/decrement the variable as fast as possible, that wouldn’t take a long time to happen. You obviously shouldn’t in that case use just debug printouts to examine the variable, as that would just slow the execution down and clog your debug output.

Your thread doesn’t exit cleanly because you never get out of the infinite loop in it. You can use the Thread::threadShouldExit method in the thread loop to check if the thread should stop.

Here’s a test that fails very quickly :

void test_thread()
{
    int sharedvariable = 0;
    auto task = [&sharedvariable]() 
    {
        for (int i = 0; i < 1000000; ++i)
        {
            ++sharedvariable;
        }
    };
    std::thread th1(task);
    std::thread th2(task);
    th1.join();
    th2.join();
    std::cout << "variable = " << sharedvariable << "\n";
}

The end result will be different for every run, when it should always be 2000000. (The code can be trivially fixed by changing the shared int into std::atomic<int>.)

Thanks @Xenakios. I changed the program so that both threads are adding a character to a String. Once the String got up to about 7000 characters, it finally threw an assertion on this line in juce_String.cpp:

static CharPointerType makeUniqueWithByteSize (const CharPointerType text, size_t numBytes)

Can I safely assume that this was a race condition?

I didn’t realize that race conditions were so sparse. I suppose this is one of the things that makes them nasty–they seem a lot less predictable than dangling pointers, memory leaks, etc.

If the same code runs OK from a single thread, yes, it would be a race condition if you get the error when doing it from multiple threads. Race conditions don’t necessarily show up as fatal runtime errors/exceptions/assertions, though. You may simply get corrupted data, which can be particularly nasty with audio as harsh/loud glitches may be heard in that case.

Yes it was the corrupt data I was hoping to spot, but so far no luck.

The question I’m left with is, how do I know I’ve hit a race condition, in the context of building and debugging a program? Will Visual Studio give me any special warnings, or do I just have to work it out on my own?

Sadly, I don’t think Visual Studio has anything by default to help with these problems. (I am not sure if there are even any optional add-ons for that at the moment.) If you can use Mac Os/Xcode (relatively new versions) or Linux, there’s the Clang Thread Sanitizer available.

OK, thanks for your help!