plugin_cache.h
Remembering what a scan found, and what killed it.
#include <sonore/plugin_cache.h>- 2types
- 8other declarations
- Guides: Hosting plugins
On this page
Design notes
Scanning a plugin folder means LOADING SOMEBODY ELSE'S CODE. Every file in it gets dlopened, its entry point called, its factory walked. That is slow (a full folder is seconds), and it is dangerous, because one plugin that faults on load takes the whole host down with it, every single time, and the user has no way to get past it.
Two problems, and the second is the interesting one.
SLOW is solved by writing down what was found and checking timestamps: a file that has not changed since it was scanned does not need scanning again.
CRASHING is solved by a dead man's switch. The path being scanned is written to the cache and FLUSHED TO DISK before the file is opened. If the process dies inside that load, the entry is still sitting there next time, and a plugin that was mid-scan when the host last died is a plugin that killed it. It goes on the blacklist and is not tried again.
That trick is the whole reason this can work in-process. The alternative (scanning in a child process, as some hosts do) is more robust and costs a second binary, an IPC protocol and a lifetime to get wrong. This costs one fflush.
Types 2
What a file looked like when it was scanned. Size and modification time together, because either alone is fooled by something ordinary: a rebuild that lands on the same size, or an installer that preserves timestamps.
What previous scans found, and what they must not try again.
The file format is one record per line, tab separated, with a version on the first line. Not JSON and not binary: a user whose host will not start because of a cache should be able to open it in a text editor and see which plugin is the problem, and delete one line rather than the lot.
Functions 8
- fn
inline unsigned long currentProcessId()This process's id, written beside a scan mark so that ANOTHER process reading the cache can tell a scan still running from one that died.host::currentProcessId · plugin_cache.h:86 - fn
inline bool processAlive(unsigned long pid)Whether a process with this id is still running. A reused id answers yes, which errs the safe way: a plugin is not blacklisted on that evidence.host::processAlive · plugin_cache.h:96 - fn
inline FileStamp stampOfTree(const std::string& directory, int depth)A folder's files, summarised: total size and the newest modification.host::stampOfTree · plugin_cache.h:111 - fn
inline FileStamp stampOf(const std::string& path)Whathost::stampOf · plugin_cache.h:164pathlooks like now, a UTF-8 path to a plugin file or bundle. A bundle folder is stamped by its files, a few levels deep, since its own size and time do not move when the binary inside is replaced. {0, 0}, not valid(), for a path that does not exist or cannot be read. - fn
inline std::vector<PluginDescription> scanFilesCached(const std::vector<std::string>& files, PluginCache& cache, const char* cachePath)host::scanFilesCached · plugin_cache.h:642 - fn
inline std::vector<PluginDescription> scanDirectoryCached(const std::string& directory, PluginCache& cache, const char* cachePath)Scan a folder, using and updating a cache.host::scanDirectoryCached · plugin_cache.h:655 - fn
inline bool savePresetFile(const HostedPlugin& plugin, const PluginDescription& description, const char* path)Save a loaded plugin's state to a preset FILE, in the format that plugin's world uses.host::savePresetFile · plugin_cache.h:739 - fn
inline bool loadPresetFile(HostedPlugin& plugin, const PluginDescription& description, const char* path)Load a preset file into a plugin.host::loadPresetFile · plugin_cache.h:763