summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2018-01-21 20:52:32 +0100
committerDavid Robillard <d@drobilla.net>2018-01-22 00:20:19 +0100
commitd9c5d89d230b204a90cca4dee4165ba6ebdf00fd (patch)
treebfef33474927a6c22f723fcc849d8df801f618e9
parent7150b9bc10511e17abdd6e528fd1317522f64eae (diff)
downloadingen-d9c5d89d230b204a90cca4dee4165ba6ebdf00fd.tar.gz
ingen-d9c5d89d230b204a90cca4dee4165ba6ebdf00fd.tar.bz2
ingen-d9c5d89d230b204a90cca4dee4165ba6ebdf00fd.zip
Only enqueue messages when the engine is remote
When the engine is local, messages are emitted in the Gtk thread and applied immediately. This should make the GUI more responsive.
-rw-r--r--ingen/QueuedInterface.hpp66
-rw-r--r--ingen/client/ThreadedSigClientInterface.hpp95
-rw-r--r--src/gui/App.cpp29
-rw-r--r--src/gui/App.hpp22
-rw-r--r--src/gui/BreadCrumbs.cpp2
-rw-r--r--src/gui/ConnectWindow.cpp13
-rw-r--r--src/gui/ingen_gui.cpp24
-rw-r--r--tests/ingen_test.cpp1
8 files changed, 119 insertions, 133 deletions
diff --git a/ingen/QueuedInterface.hpp b/ingen/QueuedInterface.hpp
new file mode 100644
index 00000000..bf424edd
--- /dev/null
+++ b/ingen/QueuedInterface.hpp
@@ -0,0 +1,66 @@
+/*
+ This file is part of Ingen.
+ Copyright 2018 David Robillard <http://drobilla.net/>
+
+ Ingen is free software: you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or any later version.
+
+ Ingen is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with Ingen. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef INGEN_ENGINE_QUEUEDINTERFACE_HPP
+#define INGEN_ENGINE_QUEUEDINTERFACE_HPP
+
+#include <mutex>
+#include <vector>
+
+#include "ingen/Interface.hpp"
+#include "ingen/Message.hpp"
+
+namespace Ingen {
+
+/** Stores all messages and emits them to a sink on demand.
+ *
+ * This can be used to make an interface thread-safe.
+ */
+class QueuedInterface : public Interface
+{
+public:
+ explicit QueuedInterface(SPtr<Interface> sink) : _sink(std::move(sink)) {}
+
+ URI uri() const override { return URI("ingen:/QueuedInterface"); }
+
+ void message(const Message& message) override {
+ std::lock_guard<std::mutex> lock(_mutex);
+ _messages.emplace_back(message);
+ }
+
+ void emit() {
+ std::vector<Message> messages;
+ {
+ std::lock_guard<std::mutex> lock(_mutex);
+ _messages.swap(messages);
+ }
+
+ for (const auto& i : messages) {
+ _sink->message(i);
+ }
+ }
+
+ const SPtr<Interface>& sink() const { return _sink; }
+
+private:
+ std::mutex _mutex;
+ SPtr<Interface> _sink;
+ std::vector<Message> _messages;
+};
+
+} // namespace Ingen
+
+#endif // INGEN_ENGINE_QUEUEDINTERFACE_HPP
diff --git a/ingen/client/ThreadedSigClientInterface.hpp b/ingen/client/ThreadedSigClientInterface.hpp
deleted file mode 100644
index 751ea37b..00000000
--- a/ingen/client/ThreadedSigClientInterface.hpp
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- This file is part of Ingen.
- Copyright 2007-2015 David Robillard <http://drobilla.net/>
-
- Ingen is free software: you can redistribute it and/or modify it under the
- terms of the GNU Affero General Public License as published by the Free
- Software Foundation, either version 3 of the License, or any later version.
-
- Ingen is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU Affero General Public License for details.
-
- You should have received a copy of the GNU Affero General Public License
- along with Ingen. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef INGEN_CLIENT_THREADEDSIGCLIENTINTERFACE_HPP
-#define INGEN_CLIENT_THREADEDSIGCLIENTINTERFACE_HPP
-
-#include <cstdint>
-#include <mutex>
-#include <string>
-#include <vector>
-
-#undef nil
-#include <sigc++/sigc++.h>
-
-#include "ingen/Atom.hpp"
-#include "ingen/Interface.hpp"
-#include "ingen/client/SigClientInterface.hpp"
-#include "ingen/ingen.h"
-
-/** Returns nothing and takes no parameters (because they have all been bound) */
-typedef sigc::slot<void> Closure;
-
-namespace Ingen {
-
-class Interface;
-
-namespace Client {
-
-/** A LibSigC++ signal emitting interface for clients to use.
- *
- * This emits signals (possibly) in a different thread than the ClientInterface
- * functions are called. It must be explicitly driven with the emit_signals()
- * function, which fires all enqueued signals up until the present. You can
- * use this in a GTK idle callback for receiving thread safe engine signals.
- *
- * @ingroup IngenClient
- */
-class INGEN_API ThreadedSigClientInterface : public SigClientInterface
-{
-public:
- ThreadedSigClientInterface()
- : message_slot(_signal_message.make_slot())
- {}
-
- URI uri() const override { return URI("ingen:/clients/sig_queue"); }
-
- void message(const Message& msg) override {
- std::lock_guard<std::mutex> lock(_mutex);
- _sigs.push_back(sigc::bind(message_slot, msg));
- }
-
- /** Process all queued events - Called from GTK thread to emit signals. */
- bool emit_signals() override {
- // Get pending signals
- std::vector<Closure> sigs;
- {
- std::lock_guard<std::mutex> lock(_mutex);
- std::swap(sigs, _sigs);
- }
-
- for (auto& ev : sigs) {
- ev();
- ev.disconnect();
- }
-
- return true;
- }
-
-private:
- std::mutex _mutex;
- std::vector<Closure> _sigs;
-
- using Graph = Resource::Graph;
- using Path = Raul::Path;
-
- sigc::slot<void, Message> message_slot;
-};
-
-} // namespace Client
-} // namespace Ingen
-
-#endif
diff --git a/src/gui/App.cpp b/src/gui/App.cpp
index a8128d8d..de587a7d 100644
--- a/src/gui/App.cpp
+++ b/src/gui/App.cpp
@@ -28,6 +28,7 @@
#include "ingen/EngineBase.hpp"
#include "ingen/Interface.hpp"
#include "ingen/Log.hpp"
+#include "ingen/QueuedInterface.hpp"
#include "ingen/StreamWriter.hpp"
#include "ingen/World.hpp"
#include "ingen/client/ClientStore.hpp"
@@ -157,7 +158,7 @@ App::run()
}
void
-App::attach(SPtr<SigClientInterface> client)
+App::attach(SPtr<Ingen::Interface> client)
{
assert(!_client);
assert(!_store);
@@ -168,7 +169,7 @@ App::attach(SPtr<SigClientInterface> client)
}
_client = client;
- _store = SPtr<ClientStore>(new ClientStore(_world->uris(), _world->log(), client));
+ _store = SPtr<ClientStore>(new ClientStore(_world->uris(), _world->log(), sig_client()));
_loader = SPtr<ThreadedLoader>(new ThreadedLoader(*this, _world->interface()));
if (!_world->store()) {
_world->set_store(_store);
@@ -181,12 +182,12 @@ App::attach(SPtr<SigClientInterface> client)
stderr,
ColorContext::Color::CYAN));
- client->signal_message().connect(
+ sig_client()->signal_message().connect(
sigc::mem_fun(*_dumper.get(), &StreamWriter::message));
}
_graph_tree_window->init(*this, *_store);
- _client->signal_message().connect(sigc::mem_fun(this, &App::message));
+ sig_client()->signal_message().connect(sigc::mem_fun(this, &App::message));
}
void
@@ -212,6 +213,16 @@ App::request_plugins_if_necessary()
}
}
+SPtr<SigClientInterface>
+App::sig_client()
+{
+ SPtr<QueuedInterface> qi = dynamic_ptr_cast<QueuedInterface>(_client);
+ if (qi) {
+ return dynamic_ptr_cast<SigClientInterface>(qi->sink());
+ }
+ return dynamic_ptr_cast<SigClientInterface>(_client);
+}
+
SPtr<Serialiser>
App::serialiser()
{
@@ -264,7 +275,7 @@ App::set_property(const URI& subject,
went as planned here and fire the signal ourselves as if the server
feedback came back immediately. */
if (key != uris().ingen_activity) {
- _client->signal_message().emit(SetProperty{0, subject, key, value, ctx});
+ sig_client()->signal_message().emit(SetProperty{0, subject, key, value, ctx});
}
}
@@ -365,8 +376,6 @@ App::activity_port_destroyed(Port* port)
if (i != _activity_ports.end()) {
_activity_ports.erase(i);
}
-
- return;
}
bool
@@ -412,16 +421,16 @@ App::gtk_main_iteration()
_messages_window->flush();
}
+ _enable_signal = false;
if (_world->engine()) {
if (!_world->engine()->main_iteration()) {
Gtk::Main::quit();
return false;
}
} else {
- _enable_signal = false;
- _client->emit_signals();
- _enable_signal = true;
+ dynamic_ptr_cast<QueuedInterface>(_client)->emit();
}
+ _enable_signal = true;
return true;
}
diff --git a/src/gui/App.hpp b/src/gui/App.hpp
index 573925e6..953e1702 100644
--- a/src/gui/App.hpp
+++ b/src/gui/App.hpp
@@ -75,7 +75,7 @@ public:
void error_message(const std::string& str);
- void attach(SPtr<Client::SigClientInterface> client);
+ void attach(SPtr<Ingen::Interface> client);
void detach();
@@ -118,11 +118,13 @@ public:
Style* style() const { return _style; }
WindowFactory* window_factory() const { return _window_factory; }
- Ingen::Forge& forge() const { return _world->forge(); }
- SPtr<Ingen::Interface> interface() const { return _world->interface(); }
- SPtr<Client::SigClientInterface> client() const { return _client; }
- SPtr<Client::ClientStore> store() const { return _store; }
- SPtr<ThreadedLoader> loader() const { return _loader; }
+ Ingen::Forge& forge() const { return _world->forge(); }
+ SPtr<Ingen::Interface> interface() const { return _world->interface(); }
+ SPtr<Ingen::Interface> client() const { return _client; }
+ SPtr<Client::ClientStore> store() const { return _store; }
+ SPtr<ThreadedLoader> loader() const { return _loader; }
+
+ SPtr<Client::SigClientInterface> sig_client();
SPtr<Serialiser> serialiser();
@@ -157,10 +159,10 @@ protected:
static Gtk::Main* _main;
- SPtr<Client::SigClientInterface> _client;
- SPtr<Client::ClientStore> _store;
- SPtr<ThreadedLoader> _loader;
- SPtr<StreamWriter> _dumper;
+ SPtr<Ingen::Interface> _client;
+ SPtr<Client::ClientStore> _store;
+ SPtr<ThreadedLoader> _loader;
+ SPtr<StreamWriter> _dumper;
Style* _style;
diff --git a/src/gui/BreadCrumbs.cpp b/src/gui/BreadCrumbs.cpp
index 3f69e998..ae7882e3 100644
--- a/src/gui/BreadCrumbs.cpp
+++ b/src/gui/BreadCrumbs.cpp
@@ -35,7 +35,7 @@ BreadCrumbs::BreadCrumbs(App& app)
, _full_path("/")
, _enable_signal(true)
{
- app.client()->signal_message().connect(
+ app.sig_client()->signal_message().connect(
sigc::mem_fun(this, &BreadCrumbs::message));
set_can_focus(false);
diff --git a/src/gui/ConnectWindow.cpp b/src/gui/ConnectWindow.cpp
index 8f235264..623db4a0 100644
--- a/src/gui/ConnectWindow.cpp
+++ b/src/gui/ConnectWindow.cpp
@@ -29,11 +29,12 @@
#include "ingen/Interface.hpp"
#include "ingen/Log.hpp"
#include "ingen/Module.hpp"
+#include "ingen/QueuedInterface.hpp"
#include "ingen/World.hpp"
#include "ingen/client/ClientStore.hpp"
#include "ingen/client/GraphModel.hpp"
+#include "ingen/client/SigClientInterface.hpp"
#include "ingen/client/SocketClient.hpp"
-#include "ingen/client/ThreadedSigClientInterface.hpp"
#include "ingen_config.h"
#include "App.hpp"
@@ -194,13 +195,13 @@ ConnectWindow::connect_remote(const URI& uri)
{
Ingen::World* world = _app->world();
- SPtr<ThreadedSigClientInterface> tsci(
- new Client::ThreadedSigClientInterface());
+ SPtr<SigClientInterface> sci(new SigClientInterface());
+ SPtr<QueuedInterface> qi(new QueuedInterface(sci));
- SPtr<Ingen::Interface> iface(world->new_interface(uri, tsci));
+ SPtr<Ingen::Interface> iface(world->new_interface(uri, qi));
if (iface) {
world->set_interface(iface);
- _app->attach(tsci);
+ _app->attach(qi);
_app->register_callbacks();
return true;
}
@@ -231,7 +232,7 @@ ConnectWindow::connect(bool existing)
_connect_stage = 1;
SPtr<Client::SocketClient> client = dynamic_ptr_cast<Client::SocketClient>(world->interface());
if (client) {
- _app->attach(dynamic_ptr_cast<Client::SigClientInterface>(client->respondee()));
+ _app->attach(client->respondee());
_app->register_callbacks();
} else {
error("Connected with invalid client interface type");
diff --git a/src/gui/ingen_gui.cpp b/src/gui/ingen_gui.cpp
index 83e41a7e..677296fd 100644
--- a/src/gui/ingen_gui.cpp
+++ b/src/gui/ingen_gui.cpp
@@ -16,7 +16,8 @@
#include "ingen/Configuration.hpp"
#include "ingen/Module.hpp"
-#include "ingen/client/ThreadedSigClientInterface.hpp"
+#include "ingen/QueuedInterface.hpp"
+#include "ingen/client/SigClientInterface.hpp"
#include "App.hpp"
@@ -24,18 +25,16 @@ namespace Ingen {
namespace GUI {
struct GUIModule : public Module {
- void load(World* world) {
- using Client::SigClientInterface;
- using Client::ThreadedSigClientInterface;
+ using SigClientInterface = Client::SigClientInterface;
- std::string uri = world->conf().option("connect").ptr<char>();
+ void load(World* world) {
+ URI uri(world->conf().option("connect").ptr<char>());
if (!world->interface()) {
- SPtr<SigClientInterface> client(new ThreadedSigClientInterface());
- world->set_interface(world->new_interface(URI(uri), client));
- } else if (!dynamic_ptr_cast<Client::SigClientInterface>(
+ world->set_interface(
+ world->new_interface(URI(uri), make_client(world)));
+ } else if (!dynamic_ptr_cast<SigClientInterface>(
world->interface()->respondee())) {
- SPtr<SigClientInterface> client(new ThreadedSigClientInterface());
- world->interface()->set_respondee(client);
+ world->interface()->set_respondee(make_client(world));
}
app = GUI::App::create(world);
@@ -45,6 +44,11 @@ struct GUIModule : public Module {
app->run();
}
+ SPtr<Interface> make_client(World* const world) {
+ SPtr<SigClientInterface> sci(new SigClientInterface());
+ return world->engine() ? sci : SPtr<Interface>(new QueuedInterface(sci));
+ }
+
SPtr<GUI::App> app;
};
diff --git a/tests/ingen_test.cpp b/tests/ingen_test.cpp
index beac1a7f..c0a7bd32 100644
--- a/tests/ingen_test.cpp
+++ b/tests/ingen_test.cpp
@@ -42,7 +42,6 @@
#include "ingen/Store.hpp"
#include "ingen/URIMap.hpp"
#include "ingen/World.hpp"
-#include "ingen/client/ThreadedSigClientInterface.hpp"
#include "ingen/filesystem.hpp"
#include "ingen/runtime_paths.hpp"
#include "ingen/types.hpp"