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    // TODO(Ed) check error code
108    dbus_connection_send_with_reply(conn, m, p, timeout_in_milliseconds);
109  }
110
111  // begin asynchronous operation
112  // FIXME should not get io from an argument
113  void start(boost::asio::io_service& io) {
114    bool old_value(true);
115    if (is_paused.compare_exchange_strong(old_value, false)) {
116      // If two threads call connection::async_send()
117      // simultaneously on a paused connection, then
118      // only one will pass the CAS instruction and
119      // only one dispatch_handler will be injected.
120      io.post(detail::dispatch_handler(io, conn));
121    }
122  }
123
124  void cancel(boost::asio::io_service& io) {
125    bool old_value(false);
126    if (is_paused.compare_exchange_strong(old_value, true)) {
127      // TODO
128    }
129  }
130};
131
132}  // namespace impl
133}  // namespace dbus
134
135#endif  // DBUS_CONNECTION_IPP
136