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_QUEUE_HPP
7 #define DBUS_QUEUE_HPP
8 
9 #include <deque>
10 #include <functional>
11 #include <boost/asio.hpp>
12 #include <boost/asio/detail/mutex.hpp>
13 
14 namespace dbus {
15 namespace detail {
16 
17 template <typename Message>
18 class queue {
19  public:
20   typedef ::boost::asio::detail::mutex mutex_type;
21   typedef Message message_type;
22   typedef std::function<void(boost::system::error_code, Message)> handler_type;
23 
24  private:
25   boost::asio::io_service& io;
26   mutex_type mutex;
27   std::deque<message_type> messages;
28   std::deque<handler_type> handlers;
29 
30  public:
31   queue(boost::asio::io_service& io_service) : io(io_service) {}
32 
33  private:
34   class closure {
35     handler_type handler_;
36     message_type message_;
37     boost::system::error_code error_;
38 
39    public:
40     void operator()() { handler_(error_, message_); }
41     closure(BOOST_ASIO_MOVE_ARG(handler_type) h, Message m,
42             boost::system::error_code e = boost::system::error_code())
43         : handler_(h), message_(m), error_(e) {}
44   };
45 
46  public:
47   void push(message_type m) {
48     mutex_type::scoped_lock lock(mutex);
49     if (handlers.empty())
50       messages.push_back(m);
51     else {
52       handler_type h = handlers.front();
53       handlers.pop_front();
54 
55       lock.unlock();
56 
57       io.post(closure(BOOST_ASIO_MOVE_CAST(handler_type)(h), m));
58     }
59   }
60 
61   template <typename MessageHandler>
62   inline BOOST_ASIO_INITFN_RESULT_TYPE(MessageHandler,
63                                        void(boost::system::error_code,
64                                             message_type))
65       async_pop(BOOST_ASIO_MOVE_ARG(MessageHandler) h) {
66     typedef ::boost::asio::detail::async_result_init<
67         MessageHandler, void(boost::system::error_code, message_type)>
68         init_type;
69 
70     mutex_type::scoped_lock lock(mutex);
71     if (messages.empty()) {
72       init_type init(BOOST_ASIO_MOVE_CAST(MessageHandler)(h));
73 
74       handlers.push_back(init.handler);
75 
76       lock.unlock();
77 
78       return init.result.get();
79 
80     } else {
81       message_type m = messages.front();
82       messages.pop_front();
83 
84       lock.unlock();
85 
86       init_type init(BOOST_ASIO_MOVE_CAST(MessageHandler)(h));
87 
88       io.post(closure(BOOST_ASIO_MOVE_CAST(handler_type)(init.handler), m));
89 
90       return init.result.get();
91     }
92   }
93 };
94 
95 }  // namespace detail
96 }  // namespace dbus
97 
98 #endif  // DBUS_QUEUE_HPP
99