mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-08 12:30:12 +02:00
Add functions to schedule tasks in main IO context
This commit is contained in:
@@ -122,7 +122,7 @@ void GroupBridge::handle_plugin_run(size_t plugin_id, HostBridge* bridge) {
|
||||
// potentially corrupt our heap. This way we can also properly join the
|
||||
// thread again. If no active plugins remain, then we'll terminate the
|
||||
// process.
|
||||
boost::asio::post(main_context.context, [this, plugin_id]() {
|
||||
main_context.schedule_task([this, plugin_id]() {
|
||||
std::lock_guard lock(active_plugins_mutex);
|
||||
|
||||
// The join is implicit because we're using Win32Thread (which mimics
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
#include "vst2.h"
|
||||
|
||||
#include <boost/asio/dispatch.hpp>
|
||||
#include <future>
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
|
||||
@@ -333,15 +331,13 @@ void Vst2Bridge::run() {
|
||||
// instantiated and where the Win32 message loop is
|
||||
// handled.
|
||||
if (unsafe_opcodes.contains(opcode)) {
|
||||
std::promise<intptr_t> dispatch_result;
|
||||
boost::asio::dispatch(main_context.context, [&]() {
|
||||
const intptr_t result = dispatch_wrapper(
|
||||
plugin, opcode, index, value, data, option);
|
||||
|
||||
dispatch_result.set_value(result);
|
||||
});
|
||||
|
||||
return dispatch_result.get_future().get();
|
||||
return main_context
|
||||
.run_in_context<intptr_t>([&]() {
|
||||
return dispatch_wrapper(plugin, opcode,
|
||||
index, value, data,
|
||||
option);
|
||||
})
|
||||
.get();
|
||||
} else {
|
||||
return dispatch_wrapper(plugin, opcode, index,
|
||||
value, data, option);
|
||||
|
||||
@@ -16,11 +16,8 @@
|
||||
|
||||
#include "vst3.h"
|
||||
|
||||
#include <future>
|
||||
|
||||
#include "../boost-fix.h"
|
||||
|
||||
#include <boost/asio/dispatch.hpp>
|
||||
#include <public.sdk/source/vst/hosting/module_win32.cpp>
|
||||
|
||||
#include "vst3-impls/component-handler-proxy.h"
|
||||
@@ -115,16 +112,15 @@ void Vst3Bridge::run() {
|
||||
},
|
||||
[&](const Vst3PluginProxy::Destruct& request)
|
||||
-> Vst3PluginProxy::Destruct::Response {
|
||||
std::promise<void> latch;
|
||||
|
||||
boost::asio::dispatch(main_context.context, [&]() {
|
||||
// Remove the instance from within the main IO context so
|
||||
// removing it doesn't interfere with the Win32 message loop
|
||||
std::lock_guard lock(object_instances_mutex);
|
||||
object_instances.erase(request.instance_id);
|
||||
|
||||
latch.set_value();
|
||||
});
|
||||
main_context
|
||||
.run_in_context([&]() {
|
||||
// Remove the instance from within the main IO context
|
||||
// so removing it doesn't interfere with the Win32
|
||||
// message loop
|
||||
std::lock_guard lock(object_instances_mutex);
|
||||
object_instances.erase(request.instance_id);
|
||||
})
|
||||
.wait();
|
||||
|
||||
// XXX: I don't think we have to wait for the object to be
|
||||
// deleted most of the time, but I can imagine a situation
|
||||
@@ -132,7 +128,6 @@ void Vst3Bridge::run() {
|
||||
// Win32 timer in between where the above closure is being
|
||||
// executed and when the actual host application context on
|
||||
// the plugin side gets deallocated.
|
||||
latch.get_future().wait();
|
||||
return Ack{};
|
||||
},
|
||||
[&](Vst3PluginProxy::SetState& request)
|
||||
@@ -432,29 +427,29 @@ void Vst3Bridge::run() {
|
||||
|
||||
// Creating the window and having the plugin embed in it should
|
||||
// be done in the main UI thread
|
||||
std::promise<tresult> attach_result{};
|
||||
boost::asio::dispatch(main_context.context, [&]() {
|
||||
Editor& editor_instance =
|
||||
object_instances[request.owner_instance_id]
|
||||
.editor.emplace(config, window_class, x11_handle);
|
||||
return main_context
|
||||
.run_in_context<tresult>([&]() {
|
||||
Editor& editor_instance =
|
||||
object_instances[request.owner_instance_id]
|
||||
.editor.emplace(config, window_class,
|
||||
x11_handle);
|
||||
|
||||
const tresult result =
|
||||
object_instances[request.owner_instance_id]
|
||||
.plug_view->attached(
|
||||
editor_instance.get_win32_handle(),
|
||||
type.c_str());
|
||||
const tresult result =
|
||||
object_instances[request.owner_instance_id]
|
||||
.plug_view->attached(
|
||||
editor_instance.get_win32_handle(),
|
||||
type.c_str());
|
||||
|
||||
// Get rid of the editor again if the plugin didn't embed
|
||||
// itself in it
|
||||
if (result != Steinberg::kResultOk) {
|
||||
object_instances[request.owner_instance_id]
|
||||
.editor.reset();
|
||||
}
|
||||
// Get rid of the editor again if the plugin didn't
|
||||
// embed itself in it
|
||||
if (result != Steinberg::kResultOk) {
|
||||
object_instances[request.owner_instance_id]
|
||||
.editor.reset();
|
||||
}
|
||||
|
||||
attach_result.set_value(result);
|
||||
});
|
||||
|
||||
return attach_result.get_future().get();
|
||||
return result;
|
||||
})
|
||||
.get();
|
||||
},
|
||||
[&](YaPlugView::GetSize& request) -> YaPlugView::GetSize::Response {
|
||||
const tresult result =
|
||||
|
||||
+50
-2
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "boost-fix.h"
|
||||
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
@@ -27,6 +28,7 @@
|
||||
#endif
|
||||
#include <windows.h>
|
||||
|
||||
#include <boost/asio/dispatch.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <function2/function2.hpp>
|
||||
|
||||
@@ -62,6 +64,52 @@ class MainContext {
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/**
|
||||
* Asynchronously execute a function inside of this main IO context and
|
||||
* return the results as a future. This is used to make sure that operations
|
||||
* that may involve the Win32 message loop are all run from the same thread.
|
||||
*/
|
||||
template <typename T, typename F>
|
||||
std::future<T> run_in_context(F fn) {
|
||||
std::promise<T> result{};
|
||||
std::future<T> future = result.get_future();
|
||||
boost::asio::dispatch(context,
|
||||
[result = std::move(result), fn]() mutable {
|
||||
result.set_value(fn());
|
||||
});
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
/**
|
||||
* The same as the above, but without returning a value. This allows us to
|
||||
* wait for the task to have been run.
|
||||
*
|
||||
* @overload
|
||||
*/
|
||||
template <typename F>
|
||||
std::future<void> run_in_context(F fn) {
|
||||
std::promise<void> result{};
|
||||
std::future<void> future = result.get_future();
|
||||
boost::asio::dispatch(context,
|
||||
[result = std::move(result), fn]() mutable {
|
||||
fn();
|
||||
result.set_value();
|
||||
});
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a task within the IO context. The difference with `run_in_context()`
|
||||
* is that this version does not guarantee that it's going to be executed as
|
||||
* soon as possible, and thus we also won't return a future.
|
||||
*/
|
||||
template <typename F>
|
||||
void schedule_task(F fn) {
|
||||
boost::asio::post(context, fn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a timer to handle events every `event_loop_interval` milliseconds.
|
||||
*
|
||||
@@ -88,8 +136,8 @@ class MainContext {
|
||||
}
|
||||
|
||||
/**
|
||||
* The raw IO context. Can and should be used directly for everything that's
|
||||
* not the event handling loop.
|
||||
* The raw IO context. Used to bind our sockets onto. Running things within
|
||||
* this IO context should be done with the functions above.
|
||||
*/
|
||||
boost::asio::io_context context;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user