Post Render Exception Thrown

I am having an issue where the render completes with no problem, but, more often than not, when I close the edit after rendering, my DAW crashes.

In DEBUG mode, it crashes less often, but it still occurs.

I get
Exception Thrown - Access Violation at line void CriticalSection::enter() const noexcept { EnterCriticalSection ((CRITICAL_SECTION*) lock); } in juce_win32_Threads.cpp.

I use the following code to initiate the render;

File renderFile{ File::getSpecialLocation(File::userDesktopDirectory).getNonexistentChildFile("render", ".wav") };

te::EditTimeRange range{ 0.0, (edit->getLength() + 1.0) };// add 1.0 seconds to render tail

juce::BigInteger tracksToDo{ 0 };

for (auto i = 0; i < te::getAllTracks(*edit).size(); i++)
	tracksToDo.setBit(i);

if (te::Renderer::renderToFile("Render", renderFile, *edit, range, tracksToDo))
	AlertWindow::showMessageBoxAsync(AlertWindow::InfoIcon, "Rendered",	renderFile.getFullPathName());
else
	AlertWindow::showMessageBoxAsync(AlertWindow::WarningIcon, "Render", "Failed!");

And the behind the scenes code is;

void runTaskWithProgressBar(te::ThreadPoolJobWithProgress& t) override
{
	double progress{ 0.0 };
	TaskRunner runner(t, progress);

	AlertWindow w("Rendering", {}, AlertWindow::NoIcon);
	w.addProgressBarComponent(progress);
	w.setVisible(true);

	while (runner.isThreadRunning())
		if (!MessageManager::getInstance()->runDispatchLoopUntil(10))
			break;
}

//==============================================================================
struct TaskRunner : public Thread
{
	TaskRunner(te::ThreadPoolJobWithProgress& t, double& prog ) :
		Thread(t.getJobName()), task(t), progress(prog)
	{
		startThread();
	}

	~TaskRunner()
	{
		task.signalJobShouldExit();
		waitForThreadToExit(10000);
	}

	void run() override
	{
		while (!threadShouldExit())
		{
			progress = task.getCurrentTaskProgress();
			if (task.runJob() == ThreadPoolJob::jobHasFinished)
				break;
		}
	}
	te::ThreadPoolJobWithProgress& task;
	double& progress;
};

Could it be that the render is not letting go of the thread? What are the best practices here?

I am also suspicious of the line,

waitForThreadToExit(10000);

, in the destructor for the TaskRunner class. That 10 second wait time seems very arbitrary. And, changing it to other values does not seem to make any difference. Perhaps there is a better way to handle the thread exit?

I am not a threading guru by any stretch of the imagination, so any pointers or suggestions are welcome?

This seems like I have a dead pointer being referenced somewhere. But I am declaring everything on the stack, or using std::unique_ptr<>, or OwnedArray<> everywhere. So, I am at a loss.

Again, as mentioned above, it only crashes after a render. And, often is fine in debug mode, with only an occasional crash producing the exception throw.

By the way, it does so on the tracktion develop branch as well as the tracktion_graph branch. So, it is not a tracktion_graph issue.

Does my code for initiating the render, and running it, look correct?

Are there any suggestions as to what to try?

Can you build with address sanitiser enabled? That’s usually pretty good at finding memory use issues and pointing to the cause.

You are very right! Address Sanitizer helped point me to a reference that was being used after the object was gone. Now, I just have to refactor to make sure that never happens!

Thank you!

Great, glad you got it sorted :+1: