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