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> 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> 42 void async_send_op<MessageHandler>::operator()(impl::connection& c, 43 message& m) { 44 DBusPendingCall* p; 45 if (m.get_type() == "signal" || m.get_type() == "method_return") { 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> 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> 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