ComponentPeers threading issue

Hi

I think that I found a thread safety issue in latest Juce Version.

To the best of my understanding, the problem comes from non-protected conccurential accesses to the vector of juce::Desktop::peers when creating/deleting temporary component peers for tooltips. Important characteristics of my application are that (1) it uses tooltips windows heavily and (2) it has a juce::OpenGLContext, hence a distinct OpenGL thread.

The sympton is that from times to times, the following assertion is randomly thrown by Array::getUnchecked() in juce_Array.h at line 256 from the Juce OpenGL Rendering Thread:

jassert (isPositiveAndBelow (index, numUsed) && data.elements != nullptr); // in my case index == 8, numUsed == 8 and data.elements was not nullptr.

After this assertion most of the time the application crashes rather quickly due to corrupted memory.

Apparently, the critical section type used in the Array of peers is DummyCriticalSection, whereas multiple thread can access to this vector. Using peers.getUnchecked() in ComponentPeer::getPeerFor() seems thus to be non thread safe. To solve this, the best seems to use a real critical section (if this choice is not due to a performance issue)...

Since this issue is very hard to reproduce I send you my call stacks:

Juce OpenGL Rendering Thread call stack:

Array<juce::ComponentPeer * __ptr64,juce::DummyCriticalSection,0>::getUnchecked(const int index=8)
ComponentPeer::getPeerFor(const juce::Component * const component=0x000000000476b230)
Component::getPeer()
Component::ComponentHelpers::convertToParentSpace<juce::Rectangle<int> >(const juce::Component & comp={...}, juce::Rectangle<int> * pointInLocalSpace=0x000000000a9cf3f8)
Component::ComponentHelpers::convertCoordinate<juce::Rectangle<int> >(const juce::Component * target=0x0000000000000000, const juce::Component * source=0x000000000476b230, juce::Rectangle<int> * p=0x000000000a9cf4b0)
Component::localAreaToGlobal(const juce::Rectangle<int> & area={...})
Component::getScreenBounds()
OpenGLContext::CachedImage::renderFrame()
OpenGLContext::CachedImage::run()

Juce Message Thread call stack

HWNDComponentPeer::destroyWindowCallback(void * handle=0x000000000062033e)
HWNDComponentPeer::callFunctionIfNotLocked(void * (void *)* callback=0x000007feed4e2693, void * userData=0x000000000062033e)
HWNDComponentPeer::~HWNDComponentPeer()
HWNDComponentPeer::`scalar deleting destructor'()
Component::removeFromDesktop()
Component::~Component()
DropShadower::ShadowWindow::~ShadowWindow()
DropShadower::ShadowWindow::`scalar deleting destructor'()
ContainerDeletePolicy<juce::Component>::destroy(juce::Component * object=0x00000000070d13a0)
OwnedArray<juce::Component,juce::DummyCriticalSection>::deleteAllObjects()
OwnedArray<juce::Component,juce::DummyCriticalSection>::clear(bool deleteObjects=true)
DropShadower::~DropShadower()
DropShadower::`vector deleting destructor'()
ContainerDeletePolicy<juce::DropShadower>::destroy(juce::DropShadower * object=0x0000000007073080)
ScopedPointer<juce::DropShadower>::operator=(juce::DropShadower * const newObjectToTakePossessionOf=0x0000000000000000)
HWNDComponentPeer::~HWNDComponentPeer()
HWNDComponentPeer::`scalar deleting destructor'()
Component::removeFromDesktop()
TooltipWindow::hideTip()
TooltipWindow::timerCallback()
Timer::TimerThread::callTimers()
Timer::TimerThread::CallTimersMessage::messageCallback()
WindowsMessageHelpers::dispatchMessageFromLParam(__int64 lParam=76275488)
MessageManager::dispatchNextMessageOnSystemQueue(const bool returnIfNoPendingMessages=false)
MessageManager::runDispatchLoopUntil(int millisecondsToRunFor=-1)
MessageManager::runDispatchLoop()
JUCEApplicationBase::main()


I build my application with Visual Studio 2010 in 64bit. It's running on Windows 7 64 SP1 US + Intel Core i7 960 + NVidia GTX680 driver 334.89.

Thanks in advance for your concern about this.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Thanks for the heads-up on this - I'll investigate and figure out a fix asap!

I just merge your correction.
Thank you very much for that.