1 // Copyright (c) Benjamin Kietzman (github.com/bkietz)
2 //
3 // Distributed under the Boost Software License, Version 1.0. (See accompanying
4 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5 
6 #ifndef DBUS_ASYNC_SEND_OP_HPP
7 #define DBUS_ASYNC_SEND_OP_HPP
8 
9 #include <boost/scoped_ptr.hpp>
10 
11 #include <dbus/dbus.h>
12 #include <dbus/error.hpp>
13 #include <dbus/message.hpp>
14 
15 #include <dbus/impl/connection.ipp>
16 
17 namespace dbus {
18 namespace detail {
19 
20 template <typename MessageHandler>
21 struct async_send_op {
22   boost::asio::io_service& io_;
23   // TODO(ed) Instead of making message_ a unique_ptr, should probably split
24   // async_send_op into 2 classes, one that expects a return, and one that does
25   // not
26   std::shared_ptr<message> message_;
27   MessageHandler handler_;
28   async_send_op(boost::asio::io_service& io,
29                 BOOST_ASIO_MOVE_ARG(MessageHandler) handler);
30   static void callback(DBusPendingCall* p, void* userdata);  // for C API
31   void operator()(impl::connection& c, message& m);  // initiate operation
32   void operator()();  // bound completion handler form
33 };
34 
35 template <typename MessageHandler>
async_send_op(boost::asio::io_service & io,BOOST_ASIO_MOVE_ARG (MessageHandler)handler)36 async_send_op<MessageHandler>::async_send_op(boost::asio::io_service& io,
37                                              BOOST_ASIO_MOVE_ARG(MessageHandler)
38                                                  handler)
39     : io_(io), handler_(BOOST_ASIO_MOVE_CAST(MessageHandler)(handler)) {}
40 
41 template <typename MessageHandler>
operator ()(impl::connection & c,message & m)42 void async_send_op<MessageHandler>::operator()(impl::connection& c,
43                                                message& m) {
44   DBusPendingCall* p;
45   if (m.get_type() != "method_call") {
46     // For some undocumented reason, dbus_connection_send_with_reply doesn't
47     // work when the message type is signal.  To avoid a memory leak on the
48     // dbuspending call when the callback doesn't fire, simply send it without a
49     // reply
50     c.send(m);
51   } else {
52     c.send_with_reply(m, &p, -1);
53 
54     // We have to throw this onto the heap so that the
55     // C API can store it as `void *userdata`
56     async_send_op* op =
57         new async_send_op(BOOST_ASIO_MOVE_CAST(async_send_op)(*this));
58     // dbus_pending_call_unref(p);
59 
60     dbus_pending_call_set_notify(p, &callback, op, NULL);
61 
62     // FIXME Race condition: another thread might have
63     // processed the pending call's reply before a notify
64     // function could be set. If so, the notify function
65     // will never trigger, so it must be called manually:
66     if (dbus_pending_call_get_completed(p)) {
67       // TODO: does this work, or might it call the notify
68       // function too many times? Might have to use steal_reply
69       // callback(p, op);
70     }
71   }
72 }
73 
74 template <typename MessageHandler>
callback(DBusPendingCall * p,void * userdata)75 void async_send_op<MessageHandler>::callback(DBusPendingCall* p,
76                                              void* userdata) {
77   boost::scoped_ptr<async_send_op> op(static_cast<async_send_op*>(userdata));
78   auto x = dbus_pending_call_steal_reply(p);
79   op->message_ = std::make_shared<message>(x);
80   dbus_message_unref(x);
81   dbus_pending_call_unref(p);
82 
83   op->io_.post(BOOST_ASIO_MOVE_CAST(async_send_op)(*op));
84 }
85 
86 template <typename MessageHandler>
operator ()()87 void async_send_op<MessageHandler>::operator()() {
88   handler_(error(*message_.get()).error_code(), *message_.get());
89 }
90 
91 }  // namespace detail
92 }  // namespace dbus
93 
94 #endif  // DBUS_ASYNC_SEND_OP_HPP
95