lib/dbus/AsyncRequest: new helper class
This commit is contained in:
		
							
								
								
									
										111
									
								
								src/lib/dbus/AsyncRequest.hxx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								src/lib/dbus/AsyncRequest.hxx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | ||||
| /* | ||||
|  * Copyright (C) 2018 Max Kellermann <max.kellermann@gmail.com> | ||||
|  * | ||||
|  * 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 <functional> | ||||
|  | ||||
| #include <assert.h> | ||||
|  | ||||
| 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<void(Message) noexcept> 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<typename F> | ||||
| 	void Send(DBusConnection &connection, | ||||
| 		  DBusMessage &message, | ||||
| 		  int timeout_milliseconds, | ||||
| 		  F &&_callback) { | ||||
| 		assert(!pending_call); | ||||
|  | ||||
| 		callback = std::forward<F>(_callback); | ||||
| 		pending_call = PendingCall::SendWithReply(&connection, | ||||
| 							  &message, | ||||
| 							  timeout_milliseconds); | ||||
| 		pending_call.SetNotify(Notify, this); | ||||
| 	} | ||||
|  | ||||
| 	template<typename F> | ||||
| 	void Send(DBusConnection &connection, | ||||
| 		  DBusMessage &message, | ||||
| 		  F &&_callback) { | ||||
| 		Send(connection, message, -1, std::forward<F>(_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 | ||||
| @@ -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<SafeSingleton<ODBus::Glue>> 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(); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Max Kellermann
					Max Kellermann