CriticalSection/ScopedLock not working issue


#1

Hi fellow jucers,

I’m facing a weird thing that CriticalSeciton (or RAII, ScopedLock) is now working, the codes following the lock is still run simultaneously in different threads.

void MyThreadClass::run()
{
    cs.enter(); //or using a ScopedLock (cs) here.
    //doing stuff, like loading samples from files.
    cs.exit();
}

This run() method is the override of the Juce Thread class, which means the running code of each thread.
All the codes in the critical section are run inside different threads at the same time, though at different phases.

Did I misunderstand something?

Thanks.


#2

It would be helpful to have more context. Do all the threads share the same CriticalSection?


#3

The only way to screw up a critical section is to acquire it from one thread and attempt to release it from another.


#4

@Graeme

Yes, all the threads are using this, i.e. the same run() code, by startThread(…) juce function.


#5

this might be my case…


#6

If you’re manually calling enter() / exit() instead of always using a ScopedLock, then this is very likely your problem.

Get rid of all manual locking and unlocking.


#7

I simulated my condition in the below, maybe will help find out where my fault lies

#include "../JuceLibraryCode/JuceHeader.h"

static int counter = 0;

class CSTrial : public Thread
{
public:
    CSTrial()
    :Thread("CS Trial " + String(++counter))
    {
        id = counter;
        startThread(1);
    }
    
    ~CSTrial()
    {
        signalThreadShouldExit();
        waitForThreadToExit(-1);
    }
    
    //@override
    void run()
    {
        const ScopedLock sl(cs);
        doStuff();
        sleep(500);
        std::cerr << id << ": about to end run()\n";
    }
    
    void doStuff()
    {
        std::cerr << id << ": do Stuff\n";
    }
    
    CriticalSection cs;
    int id;
};

//==============================================================================
int main (int argc, char* argv[])
{

    // ..your code goes here!
    CSTrial cst1;
    CSTrial cst2;

    return 0;
}

I used Introjucer to create this console project.
On Xcode debug mode, the print is ALWAYS:

JUCE v2.0.28
2: do Stuff
1: do Stuff
2: about to end run()
1: about to end run()

On Xocde release mode, the print could be:
2: do Stuff
1: do Stuff
21: about to end run()
: about to end run()

or
1: do Stuff
2: do Stuff
12: about to end run()
: about to end run()

or
2: do Stuff
1: do Stuff
2: about to end run()
1: about to end run()

In my real code, the doStuff counterpart is some loading file job which may take one second or two.
In any case of the above, the doStuff() is reached in the different CSTrial thread. Is it possible to make the lock work? Or I misunderstand some theoretical thing on mutex?

thx.


How to pass CriticalSection (compilation error with JUCE_DELETED_FUNCTION)?
#8

It looks like what I questioned earlier. Your code shows each thread having it’s own critical section which will do nothing. The threads need to act on the same CriticalSection instance.

e.g. (something to this effect)

CriticalSection cs;  //the single critical section used by all the threads
CSTrial cst1 (cs);  //pass the critical section to cst1
CSTrial cst2 (cs);  //pass the same critical section to cst2

#9

To clarify, keep your run method the same but move “CriticalSection cs” outside of your CSTrial class. Both threads need to use the same CriticalSection object for the ScopedLock to work.


#10

Thx, I’ve changed the CriticalSection to a static one, and fixed the issue.

Looking back, I wonder why I can’t see this evident cause. anyway