From c853ffb5b2f75f5a889ed2e3ef89b818a736e87a Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 13 Apr 2024 13:50:49 +0200 Subject: Adding upstream version 1.3+ds. Signed-off-by: Daniel Baumann --- src/async/channel.h | 184 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 src/async/channel.h (limited to 'src/async/channel.h') diff --git a/src/async/channel.h b/src/async/channel.h new file mode 100644 index 0000000..d58841e --- /dev/null +++ b/src/async/channel.h @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** \file Channel + * Thread-safe communication channel for asyncs. + */ +#ifndef INKSCAPE_ASYNC_CHANNEL_H +#define INKSCAPE_ASYNC_CHANNEL_H + +#include +#include +#include +#include +#include +#include "util/funclog.h" + +namespace Inkscape { +namespace Async { +namespace Channel { +namespace detail { + +class Shared final + : public std::enable_shared_from_this +{ +public: + Shared() + { + dispatcher->connect([this] { + auto ref = shared_from_this(); + grab().exec_while([this] { return is_open; }); + }); + } + + Shared(Shared const &) = delete; + Shared &operator=(Shared const &) = delete; + + operator bool() const + { + auto g = std::lock_guard(mutables); + return is_open; + } + + template + bool run(F &&f) const + { + auto g = std::lock_guard(mutables); + if (!is_open) return false; + if (funclog.empty()) dispatcher->emit(); + funclog.emplace(std::forward(f)); + return true; + } + + void close() + { + disconnect_source(); + dispatcher.reset(); + funclog.clear(); + } + +private: + mutable std::mutex mutables; + mutable std::optional dispatcher = std::make_optional(); + mutable Util::FuncLog funclog; + bool is_open = true; + + Util::FuncLog grab() const + { + auto g = std::lock_guard(mutables); + return std::move(funclog); + } + + void disconnect_source() + { + auto g = std::lock_guard(mutables); + is_open = false; + } +}; + +struct Create; + +} // namespace detail + +class Source final +{ +public: + Source() = default; + Source(Source const &) = delete; + Source &operator=(Source const &) = delete; + Source(Source &&) = default; + Source &operator=(Source &&) = default; + + /** + * Check whether the channel is still open. + */ + explicit operator bool() const { return shared && shared->operator bool(); } + + /** + * Attempt to run a function on the main loop that the Channel was created in. This will + * either succeed and execute or destroy the function in the main loop's thread, or fail and + * leave it untouched. + * + * \return Whether the Channel is still open at the time of calling. + * + * Note that a return value of true doesn't indicate whether the function will actually run, + * because the Channel could be closed in the meantime. If it does run, it is guaranteed the + * Dest object still exists and \a close() has not been called on it. + */ + template + bool run(F &&f) const { return shared && shared->run(std::forward(f)); } + + /** + * Close the channel. No more functions submitted through run() will be run. + */ + void close() { shared.reset(); } + +private: + std::shared_ptr shared; + explicit Source(std::shared_ptr shared_) : shared(std::move(shared_)) {} + friend struct detail::Create; +}; + +class Dest final +{ +public: + Dest() = default; + Dest(Dest const &) = delete; + Dest &operator=(Dest const &) = delete; + Dest(Dest &&) = default; + Dest &operator=(Dest &&) = default; + ~Dest() { close(); } + + /** + * Close the channel. No further functions submitted by the other end will be run, and it will + * be notified of closure whenever it checks. + */ + void close() { if (shared) { shared->close(); shared.reset(); } } + + /** + * Check whether \a close() has already been called, or if the channel was never opened. + * + * Note: This does not check whether the corresponding \a close() method of Source has been + * called. In fact, this condition is meaningless without further synchronization. If you need + * to know whether the Source has closed, you can have it manually send this information + * over the Channel instead. + */ + explicit operator bool() const { return (bool)shared; } + +private: + std::shared_ptr shared; + explicit Dest(std::shared_ptr shared_) : shared(std::move(shared_)) {} + friend struct detail::Create; +}; + +namespace detail { + +struct Create +{ + Create() = delete; + + static auto create() + { + auto shared = std::make_shared(); + auto src = Source(shared); + auto dst = Dest(std::move(shared)); + return std::make_pair(std::move(src), std::move(dst)); + } +}; + +} // namespace detail + +/** + * Create a linked Source - Destination pair forming a thread-safe communication channel. + * + * As long as the channel is still open, the Source can use it to run commands in the main loop of + * the creation thread and check if the channel is still open. Destructing either end closes the channel. + */ +inline std::pair create() +{ + return detail::Create::create(); +} + +} // namespace Channel +} // namespace Async +} // namespace Inkscape + +#endif // INKSCAPE_ASYNC_CHANNEL_H -- cgit v1.2.3