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