On Thu, 2012-02-16 at 16:47 +0100, Chris Kühl wrote:
On Thu, Feb 16, 2012 at 4:19 PM, Patrick Ohly
<patrick.ohly(a)intel.com> wrote:
> The flow of messages then becomes:
> 1. syncevo-dbus-server receives method call, stores Result pointer
> 2. syncevo-dbus-server sends method call to helper
> 3. helper receives method call, replies (asynchronously or
> synchronously)
> 4. syncevo-dbus-server receives reply
> 5. syncevo-dbus-server relays reply to original caller
>
> I'll see whether I can cook up a patch.
>
No need. I'm now looking at how you use it in LocalTransportAgent.cpp.
That should be enough to get started implementing this.
I've already finished an initial draft of the idea. Here's the commit
(applies on top of the code which uses the blocking call operator). Does
this look right?
commit e746819b058ef40ee72d092be7a2431a2ec36e5a
Author: Patrick Ohly <patrick.ohly(a)intel.com>
Date: Thu Feb 16 18:06:11 2012 +0100
syncevo-dbus-server: utility code for relaying D-Bus calls
All D-Bus calls to the syncevo-dbus-helper should be asynchronous,
with pretty much the same reaction each time that call finishes.
The new ProxyCallback template class provides that common
code: return error or result values.
It's a template class because the number of result values differs.
Only the version for zero result values is provided at the moment.
Because some proxy methods need to hook into the result processing,
signals are provided that the methods can connect to. Be careful when
binding these slots to objects which might already be deleted when
syncevo-dbus-helper replies: use the boost::signals2 tracking feature
for that:
boost::shared_ptr<Client> client = ...
boost::shared_ptr<ConnectionResource> myself = ....
ProxyCallback<GDBusCXX::Result0> callback(result);
// cast necessary because Client::detach is ambiguous
callback.m_success->connect(DBusSuccessSignal_t::slot_type(static_cast< void
(Client::*) (Resource *)>(&Client::detach),
client.get(),
myself.get()).track(client).track(myself));
m_connectionProxy->m_close.start(..., callback);
diff --git a/src/dbus/server/connection-resource.cpp
b/src/dbus/server/connection-resource.cpp
index 44974a9..8557403 100644
--- a/src/dbus/server/connection-resource.cpp
+++ b/src/dbus/server/connection-resource.cpp
@@ -21,12 +21,14 @@
#include "client.h"
#include "connection-resource.h"
#include "session-common.h"
+#include "dbus-proxy.h"
#include <synthesis/san.h>
#include <syncevo/TransportAgent.h>
#include <syncevo/SyncContext.h>
#include <boost/lexical_cast.hpp>
+#include <boost/bind.hpp>
using namespace GDBusCXX;
@@ -69,7 +71,9 @@ std::string ConnectionResource::buildDescription(const StringMap
&peer)
void ConnectionResource::process(const Caller_t &caller,
const GDBusCXX::DBusArray<uint8_t> &msg,
- const std::string &msgType)
+ const std::string &msgType,
+ const boost::shared_ptr<GDBusCXX::Result0>
&result)
+
{
boost::shared_ptr<Client> client(m_server.findClient(caller));
if (!client) {
@@ -82,13 +86,15 @@ void ConnectionResource::process(const Caller_t &caller,
throw runtime_error("client does not own connection");
}
- m_connectionProxy->m_process(msg,
- msgType,
- m_peer,
- m_mustAuthenticate);
+ m_connectionProxy->m_process.start(msg,
+ msgType,
+ m_peer,
+ m_mustAuthenticate,
+ MakeProxyCallback(result));
}
-void ConnectionResource::close(const GDBusCXX::Caller_t &caller, bool normal, const
std::string &error)
+void ConnectionResource::close(const GDBusCXX::Caller_t &caller, bool normal, const
std::string &error,
+ const boost::shared_ptr<GDBusCXX::Result0>
&result)
{
SE_LOG_DEBUG(NULL, NULL, "D-Bus client %s closes connection %s %s%s%s",
caller.c_str(),
@@ -102,11 +108,27 @@ void ConnectionResource::close(const GDBusCXX::Caller_t &caller,
bool normal, co
throw runtime_error("unknown client");
}
- m_connectionProxy->m_close(normal,
- error);
+ // Does the client have to own this resource to close it()?
+ // Probably. We also need the smart pointer below.
+ boost::shared_ptr<ConnectionResource> myself =
+ boost::static_pointer_cast<ConnectionResource,
Resource>(client->findResource(this));
+ if (!myself) {
+ throw runtime_error("client does not own connection");
+ }
- // if we get there, then call was successfull.
- client->detach(this);
+ // If the close() calls succeeds, then we remove ourselves from
+ // the client. boost::signals2 tracking ensures that
+ // Client::detach() will not be called with a stale Client or
+ // ConnectionResource pointer.
+ //
+ // static_cast is necessary because detach() is ambiguous.
+ ProxyCallback<GDBusCXX::Result0> callback(result);
+ callback.m_success->connect(DBusSuccessSignal_t::slot_type(static_cast< void
(Client::*) (Resource *)>(&Client::detach),
+ client.get(),
+
myself.get()).track(client).track(myself));
+ m_connectionProxy->m_close.start(normal,
+ error,
+ callback);
}
void ConnectionResource::replyCb(const GDBusCXX::DBusArray<uint8_t> &reply,
const std::string &replyType,
diff --git a/src/dbus/server/connection-resource.h
b/src/dbus/server/connection-resource.h
index 1872851..02d8b0c 100644
--- a/src/dbus/server/connection-resource.h
+++ b/src/dbus/server/connection-resource.h
@@ -75,10 +75,13 @@ class ConnectionResource : public GDBusCXX::DBusObjectHelper,
/** Connection.Process */
void process(const GDBusCXX::Caller_t &caller, const
GDBusCXX::DBusArray<uint8_t> &msg,
- const std::string &msgType);
+ const std::string &msgType,
+ const boost::shared_ptr<GDBusCXX::Result0> &result);
/** Connection.Close */
- void close(const GDBusCXX::Caller_t &caller, bool normal, const std::string
&error);
+ void close(const GDBusCXX::Caller_t &caller, bool normal, const std::string
&error,
+ const boost::shared_ptr<GDBusCXX::Result0> &result);
+
GDBusCXX::EmitSignal0 emitAbort;
bool m_abortSent;
diff --git a/src/dbus/server/dbus-proxy.h b/src/dbus/server/dbus-proxy.h
new file mode 100644
index 0000000..e3fa4f1
--- /dev/null
+++ b/src/dbus/server/dbus-proxy.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2012 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) version 3.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+/**
+ * Utility code for relaying D-Bus method calls and signals from
+ * syncevo-dbus-server to syncevo-dbus-helper.
+ */
+
+#ifndef DBUS_PROXY_H
+#define DBUS_PROXY_H
+
+#include <string>
+#include <boost/shared_ptr.hpp>
+#include <boost/signals2.hpp>
+
+#include <gdbus-cxx.h>
+
+#include <syncevo/declarations.h>
+SE_BEGIN_CXX
+
+typedef boost::signals2::signal<void ()> DBusSuccessSignal_t;
+typedef boost::signals2::signal<void (const std::string &)>
DBusFailureSignal_t;
+
+/**
+ * Use this class in syncevo-dbus-server as callback for asynchronous
+ * method calls to syncevo-dbus-helper. Once it gets the reply from
+ * syncevo-dbus-helper (successful or otherwise), it will finish the
+ * pending method call.
+ *
+ * It is possible to hook into the reply processing by connecting
+ * to the signal(s) provided by the class. At the moment, only
+ * a single signal is provided, with the error string as parameter.
+ * More signals could be added as needed.
+ *
+ * The signals have to be shared pointers because ProxyCallback
+ * must be copyable, which boost::signals aren't.
+ *
+ * Exceptions thrown while processing the reply will be logged by
+ * the GDBus C++ bindings, but because they happen inside the main
+ * event loop, they cannot be propagated to the upper layers.
+ *
+ * @param R some kind of GDBusCXX::Result0/1/2/3
+ */
+template <class R> class ProxyCallback
+{
+ public:
+ ProxyCallback(const boost::shared_ptr<R> &result) :
+ m_success(new DBusSuccessSignal_t),
+ m_failure(new DBusFailureSignal_t),
+ m_result(result)
+ {}
+
+ void operator () (const std::string &error);
+
+ /**
+ * Triggered after a successful method call was reported back to
+ * the original caller.
+ */
+ boost::shared_ptr< DBusSuccessSignal_t > m_success;
+
+ /**
+ * Triggered after a method call failure was reported back to the
+ * original caller.
+ */
+ boost::shared_ptr< DBusFailureSignal_t > m_failure;
+
+ private:
+ boost::shared_ptr<R> m_result;
+};
+
+template <class R> void ProxyCallback<R>::operator () (const std::string
&error)
+{
+ if (error.empty()) {
+ // TODO: split error into bus name + description and relay exactly that
+ // (see Chris' code for that)
+
m_result->failed(GDBusCXX::dbus_error("org.syncevolution.gdbuscxx.Exception",
+ error));
+ (*m_failure)(error);
+ } else {
+ m_result->done();
+ (*m_success)();
+ }
+}
+
+/**
+ * utility method for creating a callback of the right type
+ */
+template <class R> class ProxyCallback<R> MakeProxyCallback(const
boost::shared_ptr<R> &result)
+{
+ return ProxyCallback<R>(result);
+}
+
+SE_END_CXX
+
+#endif // RESOURCE_H
diff --git a/src/dbus/server/server.am b/src/dbus/server/server.am
index 1a12274..cd99acd 100644
--- a/src/dbus/server/server.am
+++ b/src/dbus/server/server.am
@@ -27,6 +27,7 @@ src_dbus_server_server_cpp_files = \
src_dbus_server_server_h_files = \
$(src_dbus_server_server_cpp_files:.cpp=.h) \
+ src/dbus/server/dbus-proxy.h \
src/dbus/server/notification-backend-base.h \
src/dbus/server/notification-manager.h \
src/dbus/server/notification-manager-base.h \
--
Best Regards, Patrick Ohly
The content of this message is my personal opinion only and although
I am an employee of Intel, the statements I make here in no way
represent Intel's position on the issue, nor am I authorized to speak
on behalf of Intel on this matter.