clap

CLAP Audio Plugin API
Log | Files | Refs | README | LICENSE

commit 2b5f760ccd3f654d8d1637f348122a7ffbdd0507
parent d5a81b9facbfc7bd80ac85c9c1178de1a8a4f869
Author: Adrien Prokopowicz <adrien.prokopowicz@gmail.com>
Date:   Tue, 28 Nov 2023 21:08:31 +0100

Require plugins to support clap_plugin_entry's init() and deinit() being called multiple times

Diffstat:
Minclude/clap/entry.h | 47+++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 47 insertions(+), 0 deletions(-)

diff --git a/include/clap/entry.h b/include/clap/entry.h @@ -30,6 +30,43 @@ extern "C" { // // Each directory should be recursively searched for files and/or bundles as appropriate in your OS // ending with the extension `.clap`. +// +// As detailed in the respective functions' documentations below, the host is forbidden from calling +// either init() or deinit() multiple times, without calling the other first. This is to enforce +// a "normal" initialization pattern, where one can't construct or destroy an object multiple times. +// +// However, there are a few cases where the host may not be able to reliably prevent this from +// happening, such as when multiple, separate hosts co-exist within the same address space (e.g. +// Meta-Plugins, plugins that can host other plugins inside them). It may also happen when a VST +// compatibility layer is used to wrap a CLAP plugin, for instance. +// +// In those cases, the init() and deinit() pair of functions can happen to be called multiple times, +// either in a row or even simultaneously, from different threads, by multiple non-synchronized +// hosts, or by a single host that is presented multiple interfaces to a same DSO. This can happen +// even though this is forbidden behavior from the host by this specification, and despite a +// compliant host's best efforts. +// +// Despite those issues, hosts *must* ensure that each successful call to init() is only followed by +// a single call to deinit() at most. As long as a single successful init() call did not have its +// matching deinit() called, the DSO *must* remain in a fully operational state, as if deinit() had +// not yet been called. +// +// Therefore, init() and deinit() functions *should* be implemented in a way that allows for init() +// to be called any number of times in a row (possibly simultaneously, from multiple threads), and +// for deinit() to be called at most that same number of times (also simultaneously, from multiple, +// possibly different threads from the init() called). As of CLAP 1.11, this *must* be the case for +// all plugin implementations. +// +// This is already trivially the case if there is no (de)initialization work in the entry at all, +// or if the initialization work is thread safe and idempotent, and there is no de-initialization +// work to be done. Those implementations already are and remain valid, and require no additional +// consideration. +// +// If that is not the case, then using a global, refcount-like locked initialization counter can be +// used to keep track of init() and deinit() calls. Then the initialization work can be done only +// when the counter goes from 0 to 1 in init(), and de-initialization can be done when the counter +// goes to 0 in deinit(). Any subsequent calls to init() and deinit() can just increase or decrease +// the counter, respectively. typedef struct clap_plugin_entry { clap_version_t clap_version; // initialized to CLAP_VERSION @@ -43,6 +80,10 @@ typedef struct clap_plugin_entry { // This enables hosts to e.g. quickly load and unload a DSO for scanning its plugins, and then // load it again later to actually use the plugins if needed. // + // As stated above, even though hosts are forbidden to do so directly, multiple calls before any + // deinit() call may still happen. Implementations *should* take this into account, and *must* + // do so as of CLAP 1.11. + // // It should be as fast as possible, in order to perform a very quick scan of the plugin // descriptors. // @@ -55,6 +96,8 @@ typedef struct clap_plugin_entry { // Returns true on success. If init() returns false, then the DSO must be considered // uninitialized, and the host must not call deinit() nor any other CLAP-related symbols from the // DSO. + // This function also returns true in the case where the DSO is already initialized, and no + // actual initialization work is done in this call, as explain above. // // plugin_path is the path to the DSO (Linux, Windows), or the bundle (macOS). // @@ -72,6 +115,10 @@ typedef struct clap_plugin_entry { // This means that after deinit() is called, the DSO can be considered to be in the same state // as if init() was never called at all yet, enabling it to be re-initialized as needed. // + // As stated above, even though hosts are forbidden to do so directly, multiple calls before any + // new init() call may still happen. Implementations *should* take this into account, and *must* + // do so as of CLAP 1.11. + // // Just like init(), this function may be called on any thread, including a different one from // the one init() was called from, or from the one a later init() call can be made. // However, it is forbidden to call this function simultaneously from multiple threads.