This seems to have been a problem prior to the latest changes on develop:
It can be illustrated by this (admittedly contrived) example that leaks the CapturedObject
prior to the changes (since I’m deliberately capturing by value):
#include <JuceHeader.h>
using namespace juce;
int main (int argc, char* argv[])
{
ScopedJuceInitialiser_GUI _;
struct CapturedObject
{
CapturedObject() { Logger::outputDebugString ("CapturedObject ctor"); }
~CapturedObject() { Logger::outputDebugString ("CapturedObject dtor"); }
CapturedObject (const CapturedObject& other)
: message (other.message)
{
Logger::outputDebugString ("CapturedObject copy");
}
String message;
JUCE_LEAK_DETECTOR (CapturedObject);
};
CapturedObject data;
data.message = "Hello, World!";
Timer::callAfterDelay (1000, [data]()
{
Logger::outputDebugString (data.message); // never happens, as expected
});
return 0;
}
The failure is much more obvious with the latest changes on develop since a bunch of JUCE objects leak in this case, even if captured by reference. The problem is just made more visible.
This issue can be caused in a real app/plugin if the delay time is long enough to exit the app/plugin before the delay triggers the lambda, so the contrived example isn’t just a silly edge case.
I wonder if the following is a suitable fix in JUCE Timer?
...
struct LambdaInvoker final : private Timer,
private DeletedAtShutdown
{
LambdaInvoker (int milliseconds, std::function<void()> f) : function (f)
{
startTimer (milliseconds);
}
void timerCallback() override
{
auto f = function;
delete this;
f();
}
std::function<void()> function;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LambdaInvoker)
};
...