Debug_heap breakpoint hit on startup (sometimes)

I’m returning to development with IPlug2 after several years, and I’m encountering an issue with my new project, where I’m not sure if it’s a problem with my code or due to some changes that have been made to IPlug2 since I last worked with the framework.

About half the time (or more), the app crashes shortly after startup, seemingly right after the UI has been drawn. It throws and exception in the free_debug_nolock function in debug_heap.cpp. This function call results from the destructor of WDL_HeapBuf, which in turn results from the destructor of WDL_TypedBuf<IRECT>. This appears to be triggered by the IRECTList variable rects going out of scope at the end of IGraphicsWin::OnDisplayTimer.

Does this sound like a me issue or an IPLUG2 issue? I’ve just started this project, so the UI is not very complicated. It consists of a matrix of knobs, a few number boxes, and a IVTabBoxControl (which has its own issues, as nice as it is to have, but that’s a question for another time). Switching from NanoVG to Skia fixed some other common breakpoints, but not this heap issue. The issue issues to occur when msgCount and curCount are out of step with each other, but I don’t know if that’s relevant.

Thanks!

Update: I’ve located the source of the problem by systematically removing various UI elements. The breakpoint occurs when buttons (of any type) are added to the matrix of knobs.

Here is the context. I am looping through the rows and columns of the modulation matrix and adding control knobs, indicator lights (ILEDControl), and buttons. The line that produces the issue is marked with a comment above it. The issue doesn’t occur if I replace the button with an IVLabelControl, but it happens with multiple types of buttons. The more buttons that are added, the greater the chance that the debug_heap breakpoint is triggered, so it may occur with just a single button added, though I didn’t observe that when I tested it.

// Function Matrix
IRECT matrixBounds = b.GetReducedFromBottom(380.f).GetReducedFromRight(300.f).GetReducedFromLeft(80.f);
constexpr int nMatrixRows = N_OSC;
constexpr int nMatrixCols = N_OSC * 3;
for (int j{ 0 }; j < nMatrixCols; j++) {
    for (int i{ 0 }; i < nMatrixRows; i++) {
        // Add pitch selectors on the left side of the matrix
        if (j == 0) {
            IControl* pBox = pGraphics->AttachControl(
                new IVPitchRatioControl(matrixBounds.GetFromLeft(50.f).GetHShifted(-50.f).GetGridCell(i, 0, nMatrixRows, 1).GetCentredInside(50.f, 26.f),
                    kParamOsc1Pitch + i, nullptr, "", PROJECT_STYLE, true, 1.f, 0.25f, 20.f, "%0.2f", 1.f, 0.25, false)
            );
            const char* labels[]{ "Osc1", "Osc2", "Osc3", "Osc4", "Osc5" };
            pGraphics->AttachControl(new IVLabelControl(pBox->GetRECT().GetHShifted(-50.f), labels[i]));
        }

        // Matrix knobs
        const int tagIdx = kCtrlMatrixR01C01 + j * nMatrixCols + i;
        IRECT knobAndButtonRect = matrixBounds.GetGridCell(i, j, nMatrixRows, nMatrixCols);
        IControl* knob = pGraphics->AttachControl(new IVKnobControl(knobAndButtonRect.FracRectVertical(0.7, true).GetCentredInside(32.f), kParamMatrixOsc1Phase1 + static_cast<EParams>(j * nMatrixRows + i), "", PROJECT_STYLE.WithShowLabel(false).WithShowValue(false), false, false), tagIdx < kNumCtrlTags ? tagIdx : -1);
        // Add Modulation LED indicators, mod edit buttons, and matrix labels
        if (knob) {
            pGraphics->AttachControl(new ILEDControl(knobAndButtonRect.GetFromTRHC(5.f, 5.f), IColor(255, 0, 0, 0)));
            IRECT modButtonRect = knob->GetRECT().GetVSliced(20.f).GetVShifted(30.f).GetHPadded(8.f);
            // PROBLEM LINE BELOW:
            pGraphics->AttachControl(new IVToggleControl(modButtonRect, SplashClickActionFunc, "", PROJECT_STYLE.WithShowLabel(false)));
            if (i == 0) {
                const char* symbols[]{ "φ₁", "ω", "A" };
                const char* subscripts[]{ "1", "2", "3", "4", "5" };
                IControl* pSymbol = pGraphics->AttachControl(new IVLabelControl(knob->GetRECT().GetVShifted(-knob->GetRECT().H()-4.f), symbols[j / N_OSC]));
                pGraphics->AttachControl(new IVLabelControl(pSymbol->GetRECT().GetFromBRHC(10.f, 10.f), subscripts[j % N_OSC], DEFAULT_TEXT_STYLE.WithValueText(DEFAULT_VALUE_TEXT.WithSize(12.f).WithFGColor(COLOR_WHITE))));
            }
        }

        // Add gain knobs on the right side of the matrix
        if (j + 1 == nMatrixCols) {
            pGraphics->AttachControl(new IVKnobControl(matrixBounds.GetFromRight(50.f).GetHShifted(50.f).GetGridCell(i, 0, nMatrixRows, 1).GetCentredInside(50.f), kParamOsc1OutputGain + i, "", PROJECT_STYLE.WithShowLabel(false), false, false)
            );
        }
    }
}

The full matrix is supposed to look like this:

The buttons/toggles that say “off” are the culprit.

Another update: the crashes seem to occur after the IGraphicsWin::onDisplayTimer reaches the final return statement even when msgCount != curCount is true. This shouldn’t be possible, as the function is supposed to return earlier under that condition.