Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add initial draft for background operation (loading the state and activate the plugin from a background thread) #343

Draft
wants to merge 9 commits into
base: next
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

* [undo.h](include/clap/ext/draft/undo.h): undo support
* [incremental-state.h](include/clap/ext/draft/incremental-state.h): incremental state
* [background-thread.h](include/clap/ext/draft/background-operation.h): run some operations in background

# Changes in 1.1.8

Expand Down
1 change: 1 addition & 0 deletions include/clap/clap.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@

#include "ext/draft/ambisonic.h"
#include "ext/draft/audio-ports-activation.h"
#include "ext/draft/background-operation.h"
#include "ext/draft/context-menu.h"
#include "ext/draft/cv.h"
#include "ext/draft/midi-mappings.h"
Expand Down
92 changes: 92 additions & 0 deletions include/clap/ext/draft/background-operation.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#pragma once

#include "../../plugin.h"

static const char CLAP_EXT_BACKGROUND_OPERATION[] = "clap.background_operation.draft/0";

/*

This extension let the host execute some operations on a background thread.

Some operations can be CPU intensive and require a considerable amount of time, and being force to
abique marked this conversation as resolved.
Show resolved Hide resolved
run them on the main thread prevent parallelisation.
abique marked this conversation as resolved.
Show resolved Hide resolved

For example, loading a state or activating a plugin can require a bit of processing.
When loading a large project, with a thousand of plugin instances, it is desirable to be able
abique marked this conversation as resolved.
Show resolved Hide resolved
to load the plugin state and activate the plugin in a background thread. This can significantly
improve the loading time.

Concurrency isn't a trivial thing, and this extension should only be implemented if the plugin has
expensive tasks to perform during load state or activate.

Here is how this extension works:

1. Check which operation can be performed in background

plugin_bgop->is_supported(plugin, CLAP_EXT_STATE);

2. Inform the plugin that the host is about to start a background operation

const bool can_start = plugin_bgop->about_to_start(plugin);

Once about_to_start() is called, no more call can be made on the main thread.
abique marked this conversation as resolved.
Show resolved Hide resolved
If the background thread couldn't be created then it is allowed to jump to the step 6. immediately.

3. Inform the plugin that we've setup the background thread and are about to start (on the
background thread)

plugin_bgop->started(plugin);

4. Perform the background operations

plugin_state->load(...)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Must these calls be made on the [bgop-thread]? My reading is yes, the thread must be the same, but I think this should be made explicit as the concept of "symbolic" threads exists elsewhere in the CLAP spec.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens when the plug-in needs to call the host on the main thread as part of an operation here? It's not allowed to main main-thread calls so some things may be difficult for the plug-in author to write

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is good question, I'm not sure what's the best answer is:

  1. allow callbacks on bgop-thread
  2. delay the callbacks until finished()

My gut feeling is that 1. would be the right answer. I don't think it is realistic to have the plugin asking what interfaces it can call from the host on the background thread, so it'd have to be ALL OF THEM.

...
plugin->activate(...)

5. Inform the plugin that we're about to quit the background thread

plugin_bgop->about_to_finish(plugin);

6. Inform the plugin that we're done and back onto the main thread

plugin_bgop->finished(plugin);

*/

#ifdef __cplusplus
extern "C" {
#endif

typedef struct clap_plugin_background_operation {
// Returns true if an extension can be used in [bgop-thread].
// [main-thread]
bool(CLAP_ABI *is_supported)(const clap_plugin_t *plugin, const char *extension_id);

// The host is about to start a background operation.
// Return false to cancel the background operation.
// If the plugin returns true then no further call can be made on the [main-thread] by the plugin
// and host, except `finished()`.
//
// [main-thread]
bool(CLAP_ABI *about_to_start)(const clap_plugin_t *plugin);

// The background operation has started.
// The background thread must remain the same for the whole background operation.
//
// [bgop-thread]
void(CLAP_ABI *started)(const clap_plugin_t *plugin);

// The background operation is about to finish.
// [bgop-thread]
void(CLAP_ABI *about_to_finish)(const clap_plugin_t *plugin);

// The background operation is now finished.
// The plugin and host can call each other on the [main-thread] again.
//
// [main-thread]
void(CLAP_ABI *finished)(const clap_plugin_t *plugin);
} clap_plugin_background_operation_t;

#ifdef __cplusplus
}
#endif