Hi all, I have quite a few components that need to render on a background thread but I can’t seem to avoid hitting various assertions about leaked Typeface objects or decrementing reference counts below 0 if I create that TimeSliceThread internally and I create more than one instance of the object. Essentially that means each object creates and manages the lifetime of its own TimeSliceThread. There are no problems when using a shared TimeSliceThread between multiple objects.
Now I know its not good practice to internally create TimeSliceThreads, and a user supplied one is a much cleaner approach, but I thought this would be handy for quick GUI elements. It also bugs me because I can’t see why this is happening.
I’ve managed to distill this down to a simple test component that just fills an image with a different colour on the background thread and then a timer signals periodic repaints. Creating one of these with a nullptr to the constructor is fine, create two and the asserts intermittently happen, use a shared TimeSliceThread and pass it to the constructors and the asserts go away.
[code]#ifndef TESTSCOPE_H_1771BC75
#define TESTSCOPE_H_1771BC75
#include “…/JuceLibraryCode/JuceHeader.h”
//==============================================================================
class TestScope : public Component,
public Timer,
public TimeSliceClient
{
public:
//==============================================================================
/**
/
TestScope (TimeSliceThread backgroundThreadToUse = nullptr);
/** Destructor. */
~TestScope();
//==============================================================================
/** @internal */
void resized();
/** @internal */
void paint (Graphics& g);
/** @internal */
void timerCallback();
/** @internal */
int useTimeSlice();
private:
//==============================================================================
Image image;
CriticalSection imageLock;
Random random;
OptionalScopedPointer<TimeSliceThread> backgroundThreadToUse;
//==============================================================================
void renderImage();
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TestScope);
};
#endif // TESTSCOPE_H_1771BC75
[/code]
[code]#include “TestScope.h”
TestScope::TestScope (TimeSliceThread* backgroundThreadToUse_)
: backgroundThreadToUse (backgroundThreadToUse_, backgroundThreadToUse_ == nullptr ? true : false)
{
const ScopedLock sl (imageLock);
image = Image (Image::RGB, jmax (1, getWidth()), jmax (1, getHeight()), false);
Graphics g (image);
g.fillAll (Colours::black);
if (backgroundThreadToUse == nullptr)
{
OptionalScopedPointer<TimeSliceThread> newThread (new TimeSliceThread ("Scope Rendering Thread"), true);
backgroundThreadToUse = newThread;
backgroundThreadToUse->startThread (1);
}
backgroundThreadToUse->addTimeSliceClient (this);
startTimer (1000 / 60);
}
TestScope::~TestScope()
{
stopTimer();
backgroundThreadToUse->removeTimeSliceClient (this);
if (backgroundThreadToUse.willDeleteObject())
backgroundThreadToUse->stopThread (500);
}
//==============================================================================
void TestScope::resized()
{
const ScopedLock sl (imageLock);
image = Image (Image::RGB, jmax (1, getWidth()), jmax (1, getHeight()), false);
Graphics g (image);
g.fillAll (Colours::black);
}
void TestScope::paint (Graphics& g)
{
const ScopedLock sl (imageLock);
g.drawImageAt (image, 0, 0);
}
void TestScope::timerCallback()
{
repaint();
}
int TestScope::useTimeSlice()
{
renderImage();
return 10;
}
//==============================================================================
void TestScope::renderImage()
{
const ScopedLock sl (imageLock);
Graphics g (image);
// draw something onto the image here
g.fillAll (Colour (random.nextInt()));
}[/code]
I think it must have something to do with the destruction of the TimeSliceThread but I can’t see why the reference counting would go haywire when there’s no reference to a Typeface or Graphics in the destructor.
Any ideas? It would be enlightening to know and possibly helpful to others. I’ve created a simple Introjucer project with two instances of the above component which easily demonstrates the issue.[attachment=0]TestScope.zip[/attachment]