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_CONNECTION_IPP
7#define DBUS_CONNECTION_IPP
8
9#include <dbus/dbus.h>
10#include <dbus/detail/watch_timeout.hpp>
11
12#include <boost/atomic.hpp>
13
14namespace dbus {
15namespace impl {
16
17class connection {
18 public:
19  boost::atomic<bool> is_paused;
20
21 private:
22  DBusConnection* conn;
23
24 public:
25  connection() : is_paused(true), conn(NULL) {}
26
27  connection(const connection& other) = delete;  // non construction-copyable
28  connection& operator=(const connection&) = delete;  // non copyable
29  connection(connection&&) = delete;
30  connection& operator=(connection&&) = delete;
31
32  void open(boost::asio::io_service& io, int bus) {
33    error e;
34    conn = dbus_bus_get_private((DBusBusType)bus, e);
35    e.throw_if_set();
36
37    dbus_connection_set_exit_on_disconnect(conn, false);
38
39    detail::set_watch_timeout_dispatch_functions(conn, io);
40  }
41
42  void open(boost::asio::io_service& io, const string& address) {
43    error e;
44    conn = dbus_connection_open_private(address.c_str(), e);
45    e.throw_if_set();
46
47    dbus_bus_register(conn, e);
48    e.throw_if_set();
49
50    dbus_connection_set_exit_on_disconnect(conn, false);
51
52    detail::set_watch_timeout_dispatch_functions(conn, io);
53  }
54
55  void request_name(const string& name) {
56    error e;
57    dbus_bus_request_name(
58        conn, name.c_str(),
59        DBUS_NAME_FLAG_DO_NOT_QUEUE | DBUS_NAME_FLAG_REPLACE_EXISTING, e);
60    e.throw_if_set();
61  }
62
63  std::string get_unique_name() {
64    error e;
65    auto name = dbus_bus_get_unique_name(conn);
66    e.throw_if_set();
67    return std::string(name);
68  }
69
70  ~connection() {
71    if (conn != NULL) {
72      dbus_connection_close(conn);
73      dbus_connection_unref(conn);
74    }
75  }
76
77  message new_method_return(message& m) {
78    auto ptr = dbus_message_new_method_return(m);
79    auto x = message(ptr);
80    dbus_message_unref(ptr);
81    return x;
82  }
83
84  operator DBusConnection*() { return conn; }
85  operator const DBusConnection*() const { return conn; }
86
87  message send_with_reply_and_block(message& m,
88                                    int timeout_in_milliseconds = -1) {
89    error e;
90
91    DBusMessage* out = dbus_connection_send_with_reply_and_block(
92        conn, m, timeout_in_milliseconds, e);
93
94    e.throw_if_set();
95    message reply(out);
96    dbus_message_unref(out);
97    return reply;
98  }
99
100  void send(message& m) {
101    // ignoring message serial for now
102    dbus_connection_send(conn, m, NULL);
103  }
104
105  void send_with_reply(message& m, DBusPendingCall** p,
106                       int timeout_in_milliseconds = -1) {
107    dbus_connection_send_with_reply(conn, m, p, timeout_in_milliseconds);
108  }
109
110  // begin asynchronous operation
111  // FIXME should not get io from an argument
112  void start(boost::asio::io_service& io) {
113    bool old_value(true);
114    if (is_paused.compare_exchange_strong(old_value, false)) {
115      // If two threads call connection::async_send()
116      // simultaneously on a paused connection, then
117      // only one will pass the CAS instruction and
118      // only one dispatch_handler will be injected.
119      io.post(detail::dispatch_handler(io, conn));
120    }
121  }
122
123  void cancel(boost::asio::io_service& io) {
124    bool old_value(false);
125    if (is_paused.compare_exchange_strong(old_value, true)) {
126      // TODO
127    }
128  }
129};
130
131}  // namespace impl
132}  // namespace dbus
133
134#endif  // DBUS_CONNECTION_IPP
135