From f3d5dd78c44e570cae76abfbcd54c42e0ce97c1b Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Sun, 11 Sep 2022 17:47:20 +0200 Subject: [PATCH] Add a way to run functions on host main thread From the plugin side. --- .../bridges/clap-impls/plugin-proxy.cpp | 14 ++++-- src/plugin/bridges/clap-impls/plugin-proxy.h | 49 +++++++++++++++++++ 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/src/plugin/bridges/clap-impls/plugin-proxy.cpp b/src/plugin/bridges/clap-impls/plugin-proxy.cpp index f437198b..8f467f74 100644 --- a/src/plugin/bridges/clap-impls/plugin-proxy.cpp +++ b/src/plugin/bridges/clap-impls/plugin-proxy.cpp @@ -39,7 +39,10 @@ clap_plugin_proxy::clap_plugin_proxy(ClapPluginBridge& bridge, .get_extension = plugin_get_extension, .on_main_thread = plugin_on_main_thread, }), - host_(host) {} + host_(host), + // These function objects are relatively large, and we probably won't be + // getting that many of them + pending_callbacks_(128) {} bool CLAP_ABI clap_plugin_proxy::plugin_init(const struct clap_plugin* plugin) { assert(plugin && plugin->plugin_data); @@ -153,7 +156,12 @@ clap_plugin_proxy::plugin_get_extension(const struct clap_plugin* plugin, void CLAP_ABI clap_plugin_proxy::plugin_on_main_thread(const struct clap_plugin* plugin) { assert(plugin && plugin->plugin_data); - auto self = static_cast(plugin->plugin_data); + auto self = static_cast(plugin->plugin_data); - // TODO: Use this for spooling main thread callbacks + // Functions are pushed to this queue so they can be run on the host's main + // thread + HostCallback callback; + while (self->pending_callbacks_.try_pop(callback)) { + callback(); + } } diff --git a/src/plugin/bridges/clap-impls/plugin-proxy.h b/src/plugin/bridges/clap-impls/plugin-proxy.h index ec234d9f..14d9becc 100644 --- a/src/plugin/bridges/clap-impls/plugin-proxy.h +++ b/src/plugin/bridges/clap-impls/plugin-proxy.h @@ -16,9 +16,12 @@ #pragma once +#include #include #include +#include +#include #include "../../common/serialization/clap/plugin.h" @@ -30,6 +33,17 @@ class ClapPluginBridge; */ class clap_plugin_proxy { public: + /** + * A function that can be called on the host's main thread through a + * combination of `clap_host::request_callback()` and + * `clap_plugin::on_main_thread()`. This is identical to + * `fu2::unique_function` except that it doesn't throw and is + * noexcept move assignable. That is a requirement for using these in the + * MPMC queue. + */ + using HostCallback = fu2:: + function_base; + /** * Construct a proxy for a plugin that has already been created on the Wine * side. This is done in our `clap_plugin_factory::create()` implementation. @@ -53,6 +67,7 @@ class clap_plugin_proxy { inline const clap_plugin_t* plugin_vtable() const { return &plugin_vtable_; } + /** * The instance ID of the plugin instance this proxy belongs to. */ @@ -78,6 +93,34 @@ class clap_plugin_proxy { static void CLAP_ABI plugin_on_main_thread(const struct clap_plugin* plugin); + /** + * Asynchronously run a function on the host's main thread, returning the + * result as a future. + */ + template + std::future> run_on_main_thread(F&& fn) { + using Result = std::invoke_result_t; + + std::promise response_promise{}; + std::future response_future = response_promise.get_future(); + pending_callbacks_.push(HostCallback( + [fn = std::forward(fn), + response_promise = std::move(response_promise)]() mutable { + if constexpr (std::is_void_v) { + fn(); + response_promise.set_value(); + } else { + response_promise.set_value(fn()); + } + })); + + // In theory the host will now call `clap_plugin::on_main_thread()`, + // where we can pop the function from the queue + host_->request_callback(host_); + + return response_future; + } + private: ClapPluginBridge& bridge_; size_t instance_id_; @@ -102,4 +145,10 @@ class clap_plugin_proxy { * by the proxied plugin instance must go through ere. */ const clap_host_t* host_; + + /** + * Pending callbacks that must be sent to the host on the main thread. If a + * socket needs to make a main thread function call, it will + */ + rigtorp::MPMCQueue pending_callbacks_; };