incremental
diff --git a/boost-dbus/include/dbus/detail/async_send_op.hpp b/boost-dbus/include/dbus/detail/async_send_op.hpp
new file mode 100644
index 0000000..996a4e7
--- /dev/null
+++ b/boost-dbus/include/dbus/detail/async_send_op.hpp
@@ -0,0 +1,78 @@
+// Copyright (c) Benjamin Kietzman (github.com/bkietz)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef DBUS_ASYNC_SEND_OP_HPP
+#define DBUS_ASYNC_SEND_OP_HPP
+
+#include <boost/scoped_ptr.hpp>
+
+#include <dbus/dbus.h>
+#include <dbus/error.hpp>
+#include <dbus/message.hpp>
+
+#include <dbus/impl/connection.ipp>
+
+namespace dbus {
+namespace detail {
+
+template <typename MessageHandler>
+struct async_send_op {
+  boost::asio::io_service& io_;
+  message message_;
+  MessageHandler handler_;
+  async_send_op(boost::asio::io_service& io,BOOST_ASIO_MOVE_ARG(MessageHandler) handler);
+  static void callback(DBusPendingCall* p, void* userdata);  // for C API
+  void operator()(impl::connection& c, message& m);  // initiate operation
+  void operator()();  // bound completion handler form
+};
+
+template <typename MessageHandler>
+async_send_op<MessageHandler>::async_send_op(boost::asio::io_service& io,BOOST_ASIO_MOVE_ARG(MessageHandler)handler)
+    : io_(io), handler_(BOOST_ASIO_MOVE_CAST(MessageHandler)(handler)) {}
+
+template <typename MessageHandler>
+void async_send_op<MessageHandler>::operator()(impl::connection& c,
+                                               message& m) {
+  DBusPendingCall* p;
+  c.send_with_reply(m, &p, -1);
+
+  // We have to throw this onto the heap so that the
+  // C API can store it as `void *userdata`
+  async_send_op* op =
+      new async_send_op(BOOST_ASIO_MOVE_CAST(async_send_op)(*this));
+
+  dbus_pending_call_set_notify(p, &callback, op, NULL);
+
+  // FIXME Race condition: another thread might have
+  // processed the pending call's reply before a notify
+  // function could be set. If so, the notify function
+  // will never trigger, so it must be called manually:
+  if (dbus_pending_call_get_completed(p)) {
+    // TODO: does this work, or might it call the notify
+    // function too many times? Might have to use steal_reply
+    // callback(p, op);
+  }
+}
+
+template <typename MessageHandler>
+void async_send_op<MessageHandler>::callback(DBusPendingCall* p,
+                                             void* userdata) {
+  boost::scoped_ptr<async_send_op> op(static_cast<async_send_op*>(userdata));
+
+  op->message_ = dbus_pending_call_steal_reply(p);
+  dbus_pending_call_unref(p);
+
+  op->io_.post(BOOST_ASIO_MOVE_CAST(async_send_op)(*op));
+}
+
+template <typename MessageHandler>
+void async_send_op<MessageHandler>::operator()() {
+  handler_(error(message_).error_code(), message_);
+}
+
+}  // namespace detail
+}  // namespace dbus
+
+#endif  // DBUS_ASYNC_SEND_OP_HPP
diff --git a/boost-dbus/include/dbus/detail/queue.hpp b/boost-dbus/include/dbus/detail/queue.hpp
new file mode 100644
index 0000000..c435af3
--- /dev/null
+++ b/boost-dbus/include/dbus/detail/queue.hpp
@@ -0,0 +1,98 @@
+// Copyright (c) Benjamin Kietzman (github.com/bkietz)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef DBUS_QUEUE_HPP
+#define DBUS_QUEUE_HPP
+
+#include <deque>
+#include <functional>
+#include <boost/asio.hpp>
+#include <boost/asio/detail/mutex.hpp>
+
+namespace dbus {
+namespace detail {
+
+template <typename Message>
+class queue {
+ public:
+  typedef ::boost::asio::detail::mutex mutex_type;
+  typedef Message message_type;
+  typedef std::function<void(boost::system::error_code, Message)> handler_type;
+
+ private:
+  boost::asio::io_service& io;
+  mutex_type mutex;
+  std::deque<message_type> messages;
+  std::deque<handler_type> handlers;
+
+ public:
+  queue(boost::asio::io_service& io_service) : io(io_service) {}
+
+ private:
+  class closure {
+    handler_type handler_;
+    message_type message_;
+    boost::system::error_code error_;
+
+   public:
+    void operator()() { handler_(error_, message_); }
+    closure(BOOST_ASIO_MOVE_ARG(handler_type) h, Message m,
+            boost::system::error_code e = boost::system::error_code())
+        : handler_(h), message_(m), error_(e) {}
+  };
+
+ public:
+  void push(message_type m) {
+    mutex_type::scoped_lock lock(mutex);
+    if (handlers.empty())
+      messages.push_back(m);
+    else {
+      handler_type h = handlers.front();
+      handlers.pop_front();
+
+      lock.unlock();
+
+      io.post(closure(BOOST_ASIO_MOVE_CAST(handler_type)(h), m));
+    }
+  }
+
+  template <typename MessageHandler>
+  inline BOOST_ASIO_INITFN_RESULT_TYPE(MessageHandler,
+                                       void(boost::system::error_code,
+                                            message_type))
+      async_pop(BOOST_ASIO_MOVE_ARG(MessageHandler) h) {
+    typedef ::boost::asio::detail::async_result_init<
+        MessageHandler, void(boost::system::error_code, message_type)>
+        init_type;
+
+    mutex_type::scoped_lock lock(mutex);
+    if (messages.empty()) {
+      init_type init(BOOST_ASIO_MOVE_CAST(MessageHandler)(h));
+
+      handlers.push_back(init.handler);
+
+      lock.unlock();
+
+      return init.result.get();
+
+    } else {
+      message_type m = messages.front();
+      messages.pop_front();
+
+      lock.unlock();
+
+      init_type init(BOOST_ASIO_MOVE_CAST(MessageHandler)(h));
+
+      io.post(closure(BOOST_ASIO_MOVE_CAST(handler_type)(init.handler), m));
+
+      return init.result.get();
+    }
+  }
+};
+
+}  // namespace detail
+}  // namespace dbus
+
+#endif  // DBUS_QUEUE_HPP
diff --git a/boost-dbus/include/dbus/detail/watch_timeout.hpp b/boost-dbus/include/dbus/detail/watch_timeout.hpp
new file mode 100644
index 0000000..ef2e708
--- /dev/null
+++ b/boost-dbus/include/dbus/detail/watch_timeout.hpp
@@ -0,0 +1,151 @@
+// Copyright (c) Benjamin Kietzman (github.com/bkietz)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef DBUS_WATCH_TIMEOUT_HPP
+#define DBUS_WATCH_TIMEOUT_HPP
+
+#include <dbus/dbus.h>
+#include <boost/asio/generic/stream_protocol.hpp>
+#include <boost/asio/steady_timer.hpp>
+
+#include <chrono>
+
+namespace dbus {
+namespace detail {
+
+static void watch_toggled(DBusWatch *dbus_watch, void *data);
+struct watch_handler {
+  DBusWatchFlags flags;
+  DBusWatch *dbus_watch;
+  watch_handler(DBusWatchFlags f, DBusWatch *w) : flags(f), dbus_watch(w) {}
+  void operator()(boost::system::error_code ec, size_t) {
+    if (ec) return;
+    dbus_watch_handle(dbus_watch, flags);
+
+    boost::asio::generic::stream_protocol::socket &socket = *static_cast<boost::asio::generic::stream_protocol::socket *>(
+        dbus_watch_get_data(dbus_watch));
+
+    watch_toggled(dbus_watch, &socket.get_io_service());
+  }
+};
+static void watch_toggled(DBusWatch *dbus_watch, void *data) {
+  boost::asio::generic::stream_protocol::socket &socket =
+      *static_cast<boost::asio::generic::stream_protocol::socket *>(dbus_watch_get_data(dbus_watch));
+
+  if (dbus_watch_get_enabled(dbus_watch)) {
+    if (dbus_watch_get_flags(dbus_watch) & DBUS_WATCH_READABLE)
+      socket.async_read_some(boost::asio::null_buffers(),
+                             watch_handler(DBUS_WATCH_READABLE, dbus_watch));
+
+    if (dbus_watch_get_flags(dbus_watch) & DBUS_WATCH_WRITABLE)
+      socket.async_write_some(boost::asio::null_buffers(),
+                              watch_handler(DBUS_WATCH_WRITABLE, dbus_watch));
+
+  } else {
+    socket.cancel();
+  }
+}
+
+static dbus_bool_t add_watch(DBusWatch *dbus_watch, void *data) {
+  if (!dbus_watch_get_enabled(dbus_watch)) return TRUE;
+
+  boost::asio::io_service &io = *static_cast<boost::asio::io_service *>(data);
+
+  int fd = dbus_watch_get_unix_fd(dbus_watch);
+
+  if (fd == -1)
+    // socket based watches
+    fd = dbus_watch_get_socket(dbus_watch);
+
+  boost::asio::generic::stream_protocol::socket &socket = *new boost::asio::generic::stream_protocol::socket(io);
+
+  socket.assign(boost::asio::generic::stream_protocol(0, 0), fd);
+
+  dbus_watch_set_data(dbus_watch, &socket, NULL);
+
+  watch_toggled(dbus_watch, &io);
+  return TRUE;
+}
+
+static void remove_watch(DBusWatch *dbus_watch, void *data) {
+  delete static_cast<boost::asio::generic::stream_protocol::socket *>(
+      dbus_watch_get_data(dbus_watch));
+}
+
+struct timeout_handler {
+  DBusTimeout *dbus_timeout;
+  timeout_handler(DBusTimeout *t) : dbus_timeout(t) {}
+  void operator()(boost::system::error_code ec) {
+    if (ec) return;
+    dbus_timeout_handle(dbus_timeout);
+  }
+};
+
+static void timeout_toggled(DBusTimeout *dbus_timeout, void *data) {
+  boost::asio::steady_timer &timer =
+      *static_cast<boost::asio::steady_timer *>(dbus_timeout_get_data(dbus_timeout));
+
+  if (dbus_timeout_get_enabled(dbus_timeout)) {
+    boost::asio::steady_timer::duration interval =
+        std::chrono::milliseconds(dbus_timeout_get_interval(dbus_timeout));
+    timer.expires_from_now(interval);
+    timer.cancel();
+    timer.async_wait(timeout_handler(dbus_timeout));
+  } else {
+    timer.cancel();
+  }
+}
+
+static dbus_bool_t add_timeout(DBusTimeout *dbus_timeout, void *data) {
+  if (!dbus_timeout_get_enabled(dbus_timeout)) return TRUE;
+
+  boost::asio::io_service &io = *static_cast<boost::asio::io_service *>(data);
+
+  boost::asio::steady_timer &timer = *new boost::asio::steady_timer(io);
+
+  dbus_timeout_set_data(dbus_timeout, &timer, NULL);
+
+  timeout_toggled(dbus_timeout, &io);
+  return TRUE;
+}
+
+static void remove_timeout(DBusTimeout *dbus_timeout, void *data) {
+  delete static_cast<boost::asio::steady_timer *>(dbus_timeout_get_data(dbus_timeout));
+}
+
+struct dispatch_handler {
+  boost::asio::io_service &io;
+  DBusConnection *conn;
+  dispatch_handler(boost::asio::io_service &i, DBusConnection *c)
+      : io(i), conn(c) {}
+  void operator()() {
+    if (dbus_connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS)
+      io.post(dispatch_handler(io, conn));
+  }
+};
+
+static void dispatch_status(DBusConnection *conn, DBusDispatchStatus new_status,
+                            void *data) {
+  boost::asio::io_service &io = *static_cast<boost::asio::io_service *>(data);
+  if (new_status == DBUS_DISPATCH_DATA_REMAINS)
+    io.post(dispatch_handler(io, conn));
+}
+
+static void set_watch_timeout_dispatch_functions(DBusConnection *conn,
+                                                 boost::asio::io_service &io) {
+  dbus_connection_set_watch_functions(conn, &add_watch, &remove_watch,
+                                      &watch_toggled, &io, NULL);
+
+  dbus_connection_set_timeout_functions(conn, &add_timeout, &remove_timeout,
+                                        &timeout_toggled, &io, NULL);
+
+  dbus_connection_set_dispatch_status_function(conn, &dispatch_status, &io,
+                                               NULL);
+}
+
+}  // namespace detail
+}  // namespace dbus
+
+#endif  // DBUS_WATCH_TIMEOUT_HPP