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   message message_;
24   MessageHandler handler_;
25   async_send_op(boost::asio::io_service& io,
26                 BOOST_ASIO_MOVE_ARG(MessageHandler) handler);
27   static void callback(DBusPendingCall* p, void* userdata);  // for C API
28   void operator()(impl::connection& c, message& m);  // initiate operation
29   void operator()();  // bound completion handler form
30 };
31 
32 template <typename MessageHandler>
33 async_send_op<MessageHandler>::async_send_op(boost::asio::io_service& io,
34                                              BOOST_ASIO_MOVE_ARG(MessageHandler)
35                                                  handler)
36     : io_(io), handler_(BOOST_ASIO_MOVE_CAST(MessageHandler)(handler)) {}
37 
38 template <typename MessageHandler>
39 void async_send_op<MessageHandler>::operator()(impl::connection& c,
40                                                message& m) {
41   DBusPendingCall* p;
42   c.send_with_reply(m, &p, -1);
43 
44   // We have to throw this onto the heap so that the
45   // C API can store it as `void *userdata`
46   async_send_op* op =
47       new async_send_op(BOOST_ASIO_MOVE_CAST(async_send_op)(*this));
48 
49   dbus_pending_call_set_notify(p, &callback, op, NULL);
50 
51   // FIXME Race condition: another thread might have
52   // processed the pending call's reply before a notify
53   // function could be set. If so, the notify function
54   // will never trigger, so it must be called manually:
55   if (dbus_pending_call_get_completed(p)) {
56     // TODO: does this work, or might it call the notify
57     // function too many times? Might have to use steal_reply
58     // callback(p, op);
59   }
60 }
61 
62 template <typename MessageHandler>
63 void async_send_op<MessageHandler>::callback(DBusPendingCall* p,
64                                              void* userdata) {
65   boost::scoped_ptr<async_send_op> op(static_cast<async_send_op*>(userdata));
66 
67   op->message_ = dbus_pending_call_steal_reply(p);
68   dbus_pending_call_unref(p);
69 
70   op->io_.post(BOOST_ASIO_MOVE_CAST(async_send_op)(*op));
71 }
72 
73 template <typename MessageHandler>
74 void async_send_op<MessageHandler>::operator()() {
75   handler_(error(message_).error_code(), message_);
76 }
77 
78 }  // namespace detail
79 }  // namespace dbus
80 
81 #endif  // DBUS_ASYNC_SEND_OP_HPP
82