VST3 plugin scanning crash protection

How does JUCE protect the VST3 hosting application from crashing when it scans VST3 plugins?

Some plugins seem to crash my Windows app while JUCE is scanning VST3 plugins.
Occasionally the whole OS (Windows) is required to restart so the application can be started again after such a crash had occured during VST3 plugin scanning.

Any ideas what could be causing such behavior?
How to protect the application properly in case plugin crashes during scanning?

Plugins are normally loaded in the same process as the host application, therefore when they crash they also crash the host application. A common trick to avoid this is to load the plugins in a separate process while scanning them. If this process crashes you can detect the plugin that caused the crash, blacklist it, and avoid scanning the same plugin next time while keeping the host application running. The AudioPluginHost allows scanning out of process, it might be worth looking there for an example. Some hosts also do this when loading a plugin in general however this will have some impact on latency/performance.

1 Like

I’ll try it out.

How to detect if the core actually crashed or if it’s still just scanning the plugin?

You could use InterprocessConnection.
https://docs.juce.com/master/classInterprocessConnection.html

Not sure how to use that in my case.

Should I create InterprocessConnection for a thread that scans a single plugin and then try to ping it somehow?

Take a look at how this is implemented in the AudioPluginHost for an example.

The PluginScannerSubprocess can be found in HostStartup.cpp, and is responsible for scanning plugin paths that are sent to it by the coordinating process, and replying with an XML PluginDescription for each plugin that was scanned successfully.

The CustomPluginScanner that runs the plugin scan in the coordinating process can be found in MainHostWindow.cpp.

ChildProcessWorker and ChildProcessCoordinator handle crash detection automatically. The Worker sends a ping message back to the Coordinator periodically; if the ping isn’t received, then the Worker is assumed to have crashed.

OK, so it’s based on pinging.

Is the ping sent only after each plugin’s scan finishes, or is there some way to send it several times while the scanning of single plugin is happening? I assume this is not possible?

What if the plugin takes a long time to scan, longer than the ping frequency is?

I assume scanning a plugin takes as long as it would when the end user loads the plugin into the application? So if the plugin by default loads a gigabyte of samples, it might take a long time, thus the failed ping might indicate a false failure?

The ping is sent from a dedicated thread, so it will be sent consistently, even if opening a plugin takes a long time.

Wait. Do I read the code and JUCE documentation right, that we’re not talking about a thread created by the application, but actually a real whole different instance of the same executable file/application, which gets started up with ChildProcessWorker::initialiseFromCommandLine() ?

So the main application starts up a second version of it self, using commandline parameters for the second instance of itself to indicate that it should be in the “plugin scanning mode” instead of what it does normally?
Then these two separate instances of the executable communicate with each other using the InterprocessConnection? This way if plugin scan crashes something, it’s going to be the second instance of the application, and the crash is protected by operating system itself?

So what’s keeping the ping alive only up until the second application crashes? If the application crashes, does the operating system halt all the threads created by the crashing application? Does it work like this:

MainApp → Starts up PluginScanApp.
PluginScanApp → Creates a thread which pings periodically MainApp.
PluginScanApp crashes —> The pinging thread is also automatically halted by the operating system.
Now MainApp stops receiving the ping and knows for sure that something went horribly wrong and can react accordingly?

Yes. As mentioned, a thread is part of an existing process, so all threads from the same process can crash the process.

That’s the reason for a separate process (not thread). and as mentioned here the process running the plug-in has multiple threads (one that pings to avoid plug-in code that might block).
Also, any plug-in running in it can also make its own threads of course.

1 Like

OK, now it’s starting to make sense what’s going on.

Are there any downsides between these two alternative approaches to which process is responsible for figuring out the VST3 file names that will be scanned:

  1. Plugin scanner process figures out the exact filenames and does the scanning.
  2. Main application figures out the exact filenames and sends them to the scanner.

As far as I can see, nothing can crash when figuring out the filenames, as they’re not scanned in any way at that point. So it’s probably OK to do it in either process, regarding which one works best for the application in question?

I’m going through my own plugin scanning adventure and I found this helpful thread.

One extra thing I’m dealing with is that I am doing such scanning from my own plugin (I’m implementing a plugin that hosts other plugins as a wrapper).

If I understand the AudioPluginHost code correctly, it’s actually forking another process of itself (source) when doing the plugin scanning out-of-process, correct? That seems like it would not be possible in my scenario as the parent process is the DAW, not my plugin (which is a DLL and not a process). And even if that’s possible, I don’t think it’s desirable as forking the whole DAW has loads of overhead (memory allocation, etc), and potentially all sorts of side effects.

I believe I’m left with the only option of building my own plugin scanning executable that my plugin forks, and then use that process to communicate back the scan progress to my plugin instance.

@anthony-nicholls @reuk Am I understanding things correctly? Or is it still possible to somehow fork a standalone version of the plugin for the sole purpose of running the scanner?

Yes, it’s probably best to include a separate plugin-scanner executable with your plugin. Your installer should put this executable somewhere that can be found easily by the plugin.