Possible bug in performAnyPendingRepaintsNow?


#1

Hi Jules,

 

I can trigger (with some efforts) a crash in the 64-bit win vst version of my plugin. I'm using a quite old juce version , and cannot reproduce this issue in the juce demo plugin (it involves displaying an extra desktop window in addition to the plugin gui, showing/hiding them many times, it probably depends on a very precise time of event to trigger it). However, if you look at HWNDComponentPeer::performAnyPendingRepaintsNow :

 

if (component.isVisible()
             && (PeekMessage (&m, hwnd, WM_PAINT, WM_PAINT, PM_REMOVE) || isUsingUpdateLayeredWindow()))

the ComponentPeer may be destroyed during the call to PeekMessage (this is what happens when my plugins crashes, its destructor is getting called during the PeekMessage call). And in that case, the call to isUsingUpdateLayeredWindow() triggers the crash , because the componentpeer is dead.

 

So I'm avoiding this crash using :

        Component::SafePointer<Component> ptr(&component);
        if (component.isVisible())
        {
            if (PeekMessage (&m, hwnd, WM_PAINT, WM_PAINT, PM_REMOVE) ||
                (ptr.getComponent() && isUsingUpdateLayeredWindow()))
            {
                handlePaintMessage();
            }
        }

 

I also have to edit the caller function doIdleCallback in juce_VST_Wrapper, as the loop on componentPeers is no more correct if some of them get deleted during the loop.

 

                for (int i = ComponentPeer::getNumPeers(); --i >= 0;)

+                if (i < ComponentPeer::getNumPeers())

                    ComponentPeer::getPeer (i)->performAnyPendingRepaintsNow();


(this is an horrible fix , I know)

 

So, I can't say for sure that there is still a bug in juce 3 as I can't reproduce it with the juce demo, and my vst uses juce 30cc1ed7574f29a74113a83da8b76731f0a74555 from 2013/07/23 , but maybe what I said actually makes sense and there is still a possibility that a componentpeer gets destroyed in the middle of its performAnyPendingRepaintsNow function.
 


#2

Ok, interesting..

I'll certainly add the safeguard to check whether the component peer has been deleted in that loop. I thought I already added an assertion to catch people doing silly things like deleting a component inside its paint method, but there's no harm in making the loop more robust even if they do something like that.

I'm a bit puzzled by what you say about PeekMessage though - the only way that I can think of where that function call could possibly invoke some code would be if there's a hook attached which is receiving callbacks when the message queue is accessed.. but even if the plugin's host is using a hook to detect key events, it shouldn't do anything when you're peeking for a paint message.. ?


#3

Here is a backtrace taken when a HWNDComponentPeer is destroyed during juce::HWNDComponentPeer::performAnyPendingRepaintsNow ,

if that brings any light to you:

 

     [0x000007FEEB8EE196+134]   juce::HWNDComponentPeer::~HWNDComponentPeer - (modules\juce_gui_basics\native\juce_win32_windowing.cpp:557)
     [0x000007FEEB8EE2E4+20]    juce::HWNDComponentPeer::`scalar deleting destructor'
     [0x000007FEEB8C18F4+172]   juce::Component::removeFromDesktop - (modules\juce_gui_basics\components\juce_component.cpp:744)
     [0x000007FEEB8CF288+284]   juce::Component::~Component - (modules\juce_gui_basics\components\juce_component.cpp:407)
     [0x000007FEEB8DF414+20]    juce::DropShadower::ShadowWindow::`scalar deleting destructor'
     [0x000007FEEB8DF7BC+908]   juce::DropShadower::updateShadows - (modules\juce_gui_basics\misc\juce_dropshadower.cpp:205)
     [0x000007FEEB8E7333+15]    juce::DropShadower::componentMovedOrResized - (modules\juce_gui_basics\misc\juce_dropshadower.cpp:121)
     [0x000007FEEB8AA32A+106]   juce::ListenerList<juce::ComboBox::Listener,juce::Array<juce::ComboBox::Listener * __ptr64,juce::DummyCriticalSection,0> >::callChecked<juce::Component::BailOutChecker,juce::ComboBox * __ptr64> - (modules\juce_events\broadcasters\juce_listenerlist.h:178)
     [0x000007FEEB8B64E5+101]   juce::Component::sendVisibilityChangeMessage - (modules\juce_gui_basics\components\juce_component.cpp:491)
     [0x000007FEEB8C8342+306]   juce::Component::setVisible - (modules\juce_gui_basics\components\juce_component.cpp:471)
     [0x000007FEEB8B9369+397]   juce::HWNDComponentPeer::handlePositionChanging - (modules\juce_gui_basics\native\juce_win32_windowing.cpp:2157)
     [0x000007FEEB8E75C9+357]   juce::HWNDComponentPeer::peerWindowProc - (modules\juce_gui_basics\native\juce_win32_windowing.cpp:2360)
     [0x000007FEEB8EAF8F+111]   juce::HWNDComponentPeer::windowProc - (modules\juce_gui_basics\native\juce_win32_windowing.cpp:2256)
     [0x00000000777C9BD1+673]   TranslateMessageEx
     [0x00000000777C3BFC+156]   CallWindowProcW
     [0x00000000777F28FB+27]    CallWindowProcA
      [0x0000000069A83713]       (No symbol)
     [0x00000000777C8971+129]   GetWindowDC
     [0x00000000777C72CB+631]   SetWindowTextW
     [0x00000000777C0A6B+427]   RegisterClassW
     [0x0000000077A211F5+31]    KiUserCallbackDispatcher
     [0x00000000777C908A+186]   PeekMessageW
     [0x00000000777C9055+133]   PeekMessageW
     [0x000007FEEB8EE85D+137]   juce::HWNDComponentPeer::performAnyPendingRepaintsNow - (modules\juce_gui_basics\native\juce_win32_windowing.cpp:903)
     [0x000007FEEB67113A+122]   JuceVSTWrapper::doIdleCallback - (modules\juce_audio_plugin_client\vst\juce_vst_wrapper.cpp:1117)
     [0x000007FEEB670D47+87]    JuceVSTWrapper::dispatcher - (modules\juce_audio_plugin_client\vst\juce_vst_wrapper.cpp:1202)
     [0x000007FEEB66C6BB+235]   AudioEffect::dispatchEffectClass - (vstsdk2.4\public.sdk\source\vst2.x\audioeffect.cpp:33)

 


#4

Wow, bizarre.. I had no idea it would actually dispatch any messages! Thanks, I'll add some safeguards for that then!