From a159299a4b4d208f2d4576b6c83503ca285d86fd Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 4 Jun 2018 11:22:55 +0200 Subject: [PATCH] lib/dbus/AsyncRequest: new helper class --- Makefile.am | 1 + src/lib/dbus/AsyncRequest.hxx | 111 ++++++++++++++++++ src/neighbor/plugins/UdisksNeighborPlugin.cxx | 28 ++--- 3 files changed, 121 insertions(+), 19 deletions(-) create mode 100644 src/lib/dbus/AsyncRequest.hxx diff --git a/Makefile.am b/Makefile.am index c73a1c141..f94136999 100644 --- a/Makefile.am +++ b/Makefile.am @@ -285,6 +285,7 @@ if ENABLE_DBUS noinst_LIBRARIES += libodbus.a libodbus_a_SOURCES = \ src/lib/dbus/AppendIter.hxx \ + src/lib/dbus/AsyncRequest.hxx \ src/lib/dbus/Connection.cxx src/lib/dbus/Connection.hxx \ src/lib/dbus/Error.cxx src/lib/dbus/Error.hxx \ src/lib/dbus/Iter.hxx \ diff --git a/src/lib/dbus/AsyncRequest.hxx b/src/lib/dbus/AsyncRequest.hxx new file mode 100644 index 000000000..bd968f69f --- /dev/null +++ b/src/lib/dbus/AsyncRequest.hxx @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2018 Max Kellermann + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ODBUS_ASYNC_REQUEST_HXX +#define ODBUS_ASYNC_REQUEST_HXX + +#include "PendingCall.hxx" + +#include + +#include + +namespace ODBus { + +class Message; + +/** + * Helper class which makes sending messages and receiving the + * response asynchronously easy. + * + * Remember to always cancel pending operations before destructing + * this class. + */ +class AsyncRequest { + PendingCall pending_call; + + std::function callback; + +public: + operator bool() const noexcept { + return pending_call; + } + + /** + * Send a message on the specified connection and invoke the + * given callback upon completion (or error). + * + * The callback should invoke Message::CheckThrowError() to + * check for errors. + * + * This object must be kept around until the operation + * completes. It can only be reused after completion. + */ + template + void Send(DBusConnection &connection, + DBusMessage &message, + int timeout_milliseconds, + F &&_callback) { + assert(!pending_call); + + callback = std::forward(_callback); + pending_call = PendingCall::SendWithReply(&connection, + &message, + timeout_milliseconds); + pending_call.SetNotify(Notify, this); + } + + template + void Send(DBusConnection &connection, + DBusMessage &message, + F &&_callback) { + Send(connection, message, -1, std::forward(_callback)); + } + + void Cancel() { + pending_call.Cancel(); + } + +private: + void Notify(DBusPendingCall *pending) noexcept { + assert(pending_call.Get() == pending); + pending_call = {}; + std::exchange(callback, {})(Message::StealReply(*pending)); + } + + static void Notify(DBusPendingCall *pending, + void *user_data) noexcept { + auto &ar = *(AsyncRequest *)user_data; + ar.Notify(pending); + } +}; + +} /* namespace ODBus */ + +#endif diff --git a/src/neighbor/plugins/UdisksNeighborPlugin.cxx b/src/neighbor/plugins/UdisksNeighborPlugin.cxx index 658ecc39a..31165a338 100644 --- a/src/neighbor/plugins/UdisksNeighborPlugin.cxx +++ b/src/neighbor/plugins/UdisksNeighborPlugin.cxx @@ -23,7 +23,7 @@ #include "lib/dbus/Error.hxx" #include "lib/dbus/Glue.hxx" #include "lib/dbus/Message.hxx" -#include "lib/dbus/PendingCall.hxx" +#include "lib/dbus/AsyncRequest.hxx" #include "lib/dbus/ReadIter.hxx" #include "lib/dbus/ObjectManager.hxx" #include "lib/dbus/UDisks2.hxx" @@ -82,7 +82,7 @@ class UdisksNeighborExplorer final Manual> dbus_glue; - ODBus::PendingCall pending_list_call; + ODBus::AsyncRequest list_request; /** * Protects #by_uri, #by_path. @@ -118,13 +118,7 @@ private: void Insert(UdisksObject &&o) noexcept; void Remove(const std::string &path) noexcept; - void OnListNotify(DBusPendingCall *pending) noexcept; - - static void OnListNotify(DBusPendingCall *pending, - void *user_data) noexcept { - auto &e = *(UdisksNeighborExplorer *)user_data; - e.OnListNotify(pending); - } + void OnListNotify(ODBus::Message reply) noexcept; DBusHandlerResult HandleMessage(DBusConnection *dbus_connection, DBusMessage *message) noexcept; @@ -159,8 +153,9 @@ UdisksNeighborExplorer::DoOpen() UDISKS2_PATH, DBUS_OM_INTERFACE, "GetManagedObjects"); - pending_list_call = PendingCall::SendWithReply(connection, msg.Get()); - pending_list_call.SetNotify(OnListNotify, this); + list_request.Send(connection, *msg.Get(), + std::bind(&UdisksNeighborExplorer::OnListNotify, + this, std::placeholders::_1)); } catch (...) { dbus_glue.Destruct(); throw; @@ -176,8 +171,8 @@ UdisksNeighborExplorer::Open() inline void UdisksNeighborExplorer::DoClose() noexcept { - if (pending_list_call) { - pending_list_call.Cancel(); + if (list_request) { + list_request.Cancel(); } // TODO: remove_match @@ -317,14 +312,9 @@ UdisksNeighborExplorer::Remove(const std::string &path) noexcept } inline void -UdisksNeighborExplorer::OnListNotify(DBusPendingCall *pending) noexcept +UdisksNeighborExplorer::OnListNotify(ODBus::Message reply) noexcept { - assert(pending == pending_list_call.Get()); - - pending_list_call = {}; - using namespace ODBus; - Message reply = Message::StealReply(*pending); try { reply.CheckThrowError();