juce_initialiseThreadEvents() weirdness


#1

First of all, thanks for a great lib!

I’ve been writing unit tests to verify parts of my app and it’s been working great in conjunction with Juce. However, today I ran into an issue with an uninitialized event. When digging deeper I felt that it was kind of weird how it was setup and that I couldn’t initialize it easily from the “outside”.

Here’s the story…

I am testing my class which runs a background thread to perform some tasks, and I’m using Thread::stopThread() to gracefully shutdown the background thread when exiting. Everything works great until its time to shutdown, i.e. when stopThread() is called. The reason is that stopThread() calls Thread::waitForThreadToExit() which in turn calls Thread::sleep() to sleep for 5 ms while waiting and then it crashes (or stops at the assert) because sleepEvent is null. This is how the method looks:

void JUCE_CALLTYPE Thread::sleep (const int millisecs)
{
    if (millisecs >= 10)
    {
        Sleep (millisecs);
    }
    else
    {
        jassert (sleepEvent != 0);

        // unlike Sleep() this is guaranteed to return to the current thread after
        // the time expires, so we'll use this for short waits, which are more likely
        // to need to be accurate
        WaitForSingleObject (sleepEvent, millisecs);
    }
}

Ok, so why isn’t sleepEvent initialized then? Well, it turns out that it is created when juce_initialiseThreadEvents() is called… Great, so can I just call this method then to set it up?

void juce_initialiseThreadEvents()
{
    if (sleepEvent == 0)
#ifdef JUCE_DEBUG
        sleepEvent = CreateEvent (0, 0, 0, _T("Juce Sleep Event"));
#else
        sleepEvent = CreateEvent (0, 0, 0, 0);
#endif
}

No, I can’t call juce_initialiseThreadEvents() because it’s internal to Juce and not callable from the outside. So who calls it then?

Some searching in the code reveals that it’s called by SystemStats::initialiseStats(). This method is callable from the outside so it looks like I can use that to setup things correctly. As a side note, it seems quite weird that a generic initialization for handling a thread event is setup inside a specific function for working with system stats. Maybe this could be changed?

Nevertheless I proceeded and called the static function SystemStats::initialiseStats() because I wanted my unit test to pass. This got the code working, but when exiting it reports a lot of string memory leaks that I guess SystemStats is leaving behind. I could however not find any way to clear that up.

So my question is basically if this the way its supposed to be or if there is any other proper way to initialize Juce without having the entire JuceApplication tied in. It already works very well with unit testing so this is just a minor issue I ran into but it would be really nice if I didn’t have to init SystemStats just to test threading. And it would be great if it didn’t leak memory… :slight_smile:


#2

Looks like you searched in every place apart from the right one! It’s all explained in juce_Initialisation.h. A ScopedJuceInitialiser_GUI is the best way to do it.


#3

Ah, thanks for the pointer! I’ve missed that completely. Since I’m running unit tests with a console I used ScopedJuceInitialiser_NonGUI and it seems to do the trick.


#4

I discovered another issue now. When shutdownJuce_NonGUI() is called it will issue a call to Thread::stopAllThreads(). Since I have some singleton managers that derive from DeletedAtShutdown which manage threads this means that they never get a chance to do a proper shutdown. Wouldn’t it be nice if DeletedAtShutdown::deleteAll() was called before trying to stop all threads by force?


#5

DeletedAtShutdown::deleteAll() gets called in shutdownJuce_GUI(). It’s not designed to be used in console apps, though you could call the shutdown method yourself if you need to.