From 461da920646fd9dde71c4576957c62124e4369d9 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 17 Jan 2024 18:10:28 +0100 Subject: [PATCH] lib/avahi/Publisher: make the service list dynamic This allows editing the list of services at any time instead of passing a constant list at construction time. To do this, Service instances are now caller-owned and managed in an IntrusiveList instead of Publisher-owned in a std::forward_list. --- src/lib/avahi/Publisher.cxx | 62 +++++++++++++++++++++++++++-------- src/lib/avahi/Publisher.hxx | 28 ++++++++++++++-- src/lib/avahi/Service.hxx | 4 ++- src/zeroconf/avahi/Helper.cxx | 23 ++++++------- src/zeroconf/avahi/Helper.hxx | 4 ++- 5 files changed, 91 insertions(+), 30 deletions(-) diff --git a/src/lib/avahi/Publisher.cxx b/src/lib/avahi/Publisher.cxx index b3ca6493d..8c059bc27 100644 --- a/src/lib/avahi/Publisher.cxx +++ b/src/lib/avahi/Publisher.cxx @@ -38,28 +38,41 @@ MakePidName(const char *prefix) noexcept } Publisher::Publisher(Client &_client, const char *_name, - std::forward_list _services, ErrorHandler &_error_handler) noexcept :error_handler(_error_handler), name(MakePidName(_name)), client(_client), defer_register_services(client.GetEventLoop(), - BIND_THIS_METHOD(DeferredRegisterServices)), - services(std::move(_services)) + BIND_THIS_METHOD(DeferredRegisterServices)) { - assert(!services.empty()); - client.AddListener(*this); - - if (client.IsConnected()) - defer_register_services.Schedule(); } Publisher::~Publisher() noexcept { + assert(services.empty()); + client.RemoveListener(*this); } +void +Publisher::AddService(Service &service) noexcept +{ + services.push_back(service); + + if (visible && client.IsConnected()) + defer_register_services.Schedule(); +} + +void +Publisher::RemoveService(Service &service) noexcept +{ + services.erase(services.iterator_to(service)); + + if (visible && client.IsConnected()) + defer_register_services.Schedule(); +} + inline void Publisher::GroupCallback(AvahiEntryGroup *g, AvahiEntryGroupState state) noexcept @@ -82,6 +95,7 @@ try { } /* And recreate the services */ + should_reset_group = false; RegisterServices(*g); break; @@ -122,7 +136,7 @@ AddService(AvahiEntryGroup &group, const Service &service, static void AddServices(AvahiEntryGroup &group, - const std::forward_list &services, const char *name) + const IntrusiveList &services, const char *name) { for (const auto &i : services) AddService(group, i, name); @@ -131,11 +145,20 @@ AddServices(AvahiEntryGroup &group, void Publisher::RegisterServices(AvahiEntryGroup &g) { + if (should_reset_group) { + should_reset_group = false; + avahi_entry_group_reset(&g); + } + AddServices(g, services, name.c_str()); - if (int error = avahi_entry_group_commit(&g); - error != AVAHI_OK) - throw MakeError(error, "Failed to commit Avahi service group"); + if (!services.empty()) { + should_reset_group = true; + + if (int error = avahi_entry_group_commit(&g); + error != AVAHI_OK) + throw MakeError(error, "Failed to commit Avahi service group"); + } } void @@ -144,6 +167,8 @@ Publisher::RegisterServices(AvahiClient *c) assert(visible); if (!group) { + assert(!should_reset_group); + group.reset(avahi_entry_group_new(c, GroupCallback, this)); if (!group) throw MakeError(*c, "Failed to create Avahi service group"); @@ -175,8 +200,12 @@ Publisher::HideServices() noexcept defer_register_services.Cancel(); - if (group) + if (group) { + should_reset_group = false; avahi_entry_group_reset(group.get()); + } else { + assert(!should_reset_group); + } } void @@ -194,7 +223,10 @@ Publisher::ShowServices() noexcept void Publisher::OnAvahiConnect(AvahiClient *c) noexcept { - if (visible) + assert(!group); + assert(!should_reset_group); + + if (visible && !services.empty()) try { RegisterServices(c); } catch (...) { @@ -206,12 +238,14 @@ void Publisher::OnAvahiDisconnect() noexcept { group.reset(); + should_reset_group = false; } void Publisher::OnAvahiChanged() noexcept { group.reset(); + should_reset_group = false; } } // namespace Avahi diff --git a/src/lib/avahi/Publisher.hxx b/src/lib/avahi/Publisher.hxx index 14bf6b6aa..cabdcbaeb 100644 --- a/src/lib/avahi/Publisher.hxx +++ b/src/lib/avahi/Publisher.hxx @@ -7,11 +7,11 @@ #include "EntryGroup.hxx" #include "ConnectionListener.hxx" #include "event/DeferEvent.hxx" +#include "util/IntrusiveList.hxx" #include #include -#include class SocketAddress; @@ -36,7 +36,14 @@ class Publisher final : ConnectionListener { EntryGroupPtr group; - const std::forward_list services; + IntrusiveList services; + + /** + * Should avahi_entry_group_reset() be called by the next + * RegisterServices() call? This is true if the #gorup is + * non-empty. + */ + bool should_reset_group = false; /** * Shall the published services be visible? This is controlled by @@ -46,13 +53,28 @@ class Publisher final : ConnectionListener { public: Publisher(Client &client, const char *_name, - std::forward_list _services, ErrorHandler &_error_handler) noexcept; ~Publisher() noexcept; Publisher(const Publisher &) = delete; Publisher &operator=(const Publisher &) = delete; + /** + * Publish another service. + * + * @param service a #Service instance owned by the caller (it + * must remain valid until you call RemoveService()) + */ + void AddService(Service &service) noexcept; + + /** + * Unpublish a service. + * + * @param service a #Service instance which was previously + * passed to AddService() + */ + void RemoveService(Service &service) noexcept; + /** * Temporarily hide all registered services. You can undo this * with ShowServices(). diff --git a/src/lib/avahi/Service.hxx b/src/lib/avahi/Service.hxx index 1a8f3fbdb..432d374c1 100644 --- a/src/lib/avahi/Service.hxx +++ b/src/lib/avahi/Service.hxx @@ -4,6 +4,8 @@ #pragma once +#include "util/IntrusiveList.hxx" + #include #include @@ -14,7 +16,7 @@ namespace Avahi { /** * A service that will be published by class #Publisher. */ -struct Service { +struct Service : IntrusiveListHook<> { AvahiIfIndex interface = AVAHI_IF_UNSPEC; AvahiProtocol protocol = AVAHI_PROTO_UNSPEC; std::string type; diff --git a/src/zeroconf/avahi/Helper.cxx b/src/zeroconf/avahi/Helper.cxx index e7807f193..a7ed97aec 100644 --- a/src/zeroconf/avahi/Helper.cxx +++ b/src/zeroconf/avahi/Helper.cxx @@ -4,7 +4,6 @@ #include "Helper.hxx" #include "lib/avahi/Client.hxx" #include "lib/avahi/ErrorHandler.hxx" -#include "lib/avahi/Service.hxx" #include "lib/fmt/RuntimeError.hxx" #include "Log.hxx" @@ -29,12 +28,19 @@ static std::weak_ptr shared_avahi_client; inline AvahiHelper::AvahiHelper(std::shared_ptr _client, const char *service_name, - std::forward_list &&services) + const char *service_type, unsigned port) :client(std::move(_client)), - publisher(client->client, service_name, - std::move(services), *client) {} + publisher(client->client, service_name, *client), + service(AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, + service_type, port) +{ + publisher.AddService(service); +} -AvahiHelper::~AvahiHelper() noexcept = default; +AvahiHelper::~AvahiHelper() noexcept +{ + publisher.RemoveService(service); +} std::unique_ptr AvahiInit(EventLoop &event_loop, const char *service_name, @@ -49,11 +55,6 @@ AvahiInit(EventLoop &event_loop, const char *service_name, shared_avahi_client = client = std::make_shared(event_loop); - std::forward_list services; - services.emplace_front(AVAHI_IF_UNSPEC, - AVAHI_PROTO_UNSPEC, - service_type, port); - return std::make_unique(std::move(client), service_name, - std::move(services)); + service_type, port); } diff --git a/src/zeroconf/avahi/Helper.hxx b/src/zeroconf/avahi/Helper.hxx index 177a4eb4d..083f0e87f 100644 --- a/src/zeroconf/avahi/Helper.hxx +++ b/src/zeroconf/avahi/Helper.hxx @@ -4,6 +4,7 @@ #pragma once #include "lib/avahi/Publisher.hxx" +#include "lib/avahi/Service.hxx" #include @@ -15,11 +16,12 @@ class SharedAvahiClient; class AvahiHelper final { std::shared_ptr client; Avahi::Publisher publisher; + Avahi::Service service; public: AvahiHelper(std::shared_ptr _client, const char *service_name, - std::forward_list &&services); + const char *service_type, unsigned port); ~AvahiHelper() noexcept; };