summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2025-10-26 02:11:38 +0200
committerCarl Hetherington <cth@carlh.net>2025-10-26 02:11:38 +0200
commita645b0cff750bcd8c470055c227457d7d0d3e077 (patch)
tree679be046c44281661ba424b63690009323d2265a /src
parent60ab02524bc8d64a1c81e29e5e74c169bc87f76c (diff)
Add new signalling code.
Diffstat (limited to 'src')
-rw-r--r--src/lib/signal.cc17
-rw-r--r--src/lib/signal.h183
-rw-r--r--src/lib/wscript1
-rw-r--r--src/wx/wx_signal.h49
4 files changed, 250 insertions, 0 deletions
diff --git a/src/lib/signal.cc b/src/lib/signal.cc
new file mode 100644
index 000000000..ba7f95685
--- /dev/null
+++ b/src/lib/signal.cc
@@ -0,0 +1,17 @@
+
+#include "signal.h"
+
+boost::mutex pending::mutex;
+std::map<boost::thread::id, std::list<std::function<void ()>>> pending::callbacks;
+
+ThreadWaker* thread_waker = nullptr;
+
+void pending::process()
+{
+ boost::mutex::scoped_lock lm(mutex);
+ auto const thread_id = boost::this_thread::get_id();
+ for (auto i: callbacks[thread_id]) {
+ i();
+ }
+ callbacks[thread_id].clear();
+}
diff --git a/src/lib/signal.h b/src/lib/signal.h
new file mode 100644
index 000000000..a2ea1cf98
--- /dev/null
+++ b/src/lib/signal.h
@@ -0,0 +1,183 @@
+/*
+ Copyright (C) 2025 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#ifndef DCPOMATIC_SIGNAL_H
+#define DCPOMATIC_SIGNAL_H
+
+
+#include <boost/thread.hpp>
+#include <iostream>
+
+
+class SignalBase
+{
+public:
+ virtual ~SignalBase() = default;
+ virtual void disconnect(int id) = 0;
+};
+
+
+/** @class Connection
+ * @brief Record of a connection to a signal, so it can be disconnected.
+ */
+class Connection
+{
+public:
+ Connection(SignalBase& signal, int id)
+ : _signal(signal)
+ , _id(id)
+ {}
+
+ Connection& operator=(Connection const& other)
+ {
+ _signal = other._signal;
+ _id = other._id;
+ return *this;
+ }
+
+ void disconnect()
+ {
+ _signal.disconnect(_id);
+ }
+
+private:
+ SignalBase& _signal;
+ int _id;
+};
+
+
+/** @class ScopedConnection
+ * @brief Connection that disconnects itself on destruction.
+ */
+class ScopedConnection
+{
+public:
+ ScopedConnection() {}
+
+ ScopedConnection(Connection connection)
+ : _connection(std::move(connection))
+ {}
+
+ ScopedConnection(ScopedConnection const& other) = delete;
+
+ ScopedConnection(ScopedConnection&& other)
+ : _connection(std::move(other._connection))
+ {}
+
+ ScopedConnection& operator=(ScopedConnection const& other) = delete;
+
+ ScopedConnection& operator=(ScopedConnection&& other)
+ {
+ if (this != &other) {
+ _connection = other._connection;
+ other._connection.reset();
+ }
+ return *this;
+ }
+
+ ~ScopedConnection()
+ {
+ if (_connection) {
+ _connection->disconnect();
+ }
+ }
+
+private:
+ boost::optional<Connection> _connection;
+};
+
+
+namespace pending {
+
+extern boost::mutex mutex;
+extern std::map<boost::thread::id, std::list<std::function<void ()>>> callbacks;
+extern void process();
+
+};
+
+
+class ThreadWaker
+{
+public:
+ virtual void wake() {}
+};
+
+
+extern ThreadWaker* thread_waker;
+
+
+template <typename Parameters>
+class Signal : public SignalBase
+{
+private:
+ struct Callback
+ {
+ int id;
+ boost::thread::id thread;
+ std::function<Parameters> function;
+ };
+
+public:
+ Connection connect(std::function<Parameters> callback)
+ {
+ boost::mutex::scoped_lock lm(_mutex);
+ auto const id = _id++;
+ auto const thread_id = boost::this_thread::get_id();
+ _callbacks.push_back(Callback{id, thread_id, callback});
+ return Connection(*this, id);
+ }
+
+ void disconnect(int id) override
+ {
+ boost::mutex::scoped_lock lm(_mutex);
+ auto iter = std::find_if(_callbacks.begin(), _callbacks.end(), [id](Callback const& callback) { return callback.id == id; });
+ if (iter != _callbacks.end()) {
+ _callbacks.erase(iter);
+ }
+ }
+
+ template <typename... Args>
+ void emit(Args... args)
+ {
+ boost::mutex::scoped_lock lm(_mutex);
+ auto thread_id = boost::this_thread::get_id();
+ for (auto const& callback: _callbacks) {
+ if (thread_id == callback.thread) {
+ callback.function(args...);
+ } else {
+ boost::mutex::scoped_lock lm2(pending::mutex);
+ pending::callbacks[callback.thread].push_back(boost::bind(callback.function, args...));
+ if (thread_waker) {
+ thread_waker->wake();
+ }
+ }
+ }
+ }
+
+private:
+ boost::mutex _mutex;
+ int _id = 0;
+ std::list<Callback> _callbacks;
+};
+
+
+#endif
+
diff --git a/src/lib/wscript b/src/lib/wscript
index 2e7b0339c..bc4c7d472 100644
--- a/src/lib/wscript
+++ b/src/lib/wscript
@@ -192,6 +192,7 @@ sources = """
send_notification_email_job.cc
send_problem_report_job.cc
server.cc
+ signal.cc
shuffler.cc
state.cc
spl.cc
diff --git a/src/wx/wx_signal.h b/src/wx/wx_signal.h
new file mode 100644
index 000000000..e59592755
--- /dev/null
+++ b/src/wx/wx_signal.h
@@ -0,0 +1,49 @@
+/*
+ Copyright (C) 2016-2022 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#include "lib/signal.h"
+#include <dcp/warnings.h>
+LIBDCP_DISABLE_WARNINGS
+#include <wx/wx.h>
+LIBDCP_ENABLE_WARNINGS
+
+
+/** @class wxThreadWaker
+ * @brief Class to wake a thread so that it looks for incoming signals that it should deliver.
+ */
+class wxThreadWaker : public ThreadWaker
+{
+public:
+ wxThreadWaker(wxEvtHandler* handler)
+ : _handler(handler)
+ {}
+
+ void wake() override
+ {
+ /* This will result in an idle handler being called, which should process
+ * pending events.
+ */
+ _handler->QueueEvent(new wxCommandEvent(wxEVT_IDLE));
+ }
+
+private:
+ wxEvtHandler* _handler;
+};