]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/l4re-core/cxx/lib/ipc/include/ipc_timeout_queue
Update
[l4.git] / l4 / pkg / l4re-core / cxx / lib / ipc / include / ipc_timeout_queue
1 // vim: ft=cpp:
2 /*
3  * (c) 2014 Steffen Liebergeld <steffen.liebergeld@kernkonzept.com>
4  *
5  * This file is licensed under the terms of the GNU Lesser General Public
6  * License 2.1.
7  * See file COPYING-LGPL-2.1 for details.
8  */
9 #pragma once
10
11 #include <l4/cxx/hlist>
12 #include <l4/sys/cxx/ipc_server_loop>
13
14 namespace L4 { namespace Ipc_svr {
15
16 /**
17  * \brief Callback interface for Timeout_queue
18  * \ingroup cxx_ipc_server
19  */
20 class Timeout : public cxx::H_list_item
21 {
22   friend class Timeout_queue;
23 public:
24   /// Make a timeout
25   Timeout() : _timeout(0) {}
26
27   /// Destroy a timeout
28   virtual ~Timeout() = 0;
29
30   /**
31    * \brief callback function to be called when timeout happened
32    * \note The timeout object is already dequeued when this function is called,
33    * this means the timeout may be safely again within the expired() function.
34    */
35   virtual void expired() = 0;
36
37   /**
38    * \brief return absolute timeout of this callback.
39    * \return absolute timeout for this instance of the timeout.
40    * \pre The timeout object must have been in a queue before, otherwise the
41    *      timeout is not set.
42    */
43   l4_kernel_clock_t timeout() const
44   { return _timeout; }
45
46 private:
47   l4_kernel_clock_t _timeout;
48 };
49
50 inline Timeout::~Timeout() {}
51
52 /**
53  * \brief Timeout queue to be used in l4re server loop
54  * \ingroup cxx_ipc_server
55  */
56 class Timeout_queue
57 {
58 public:
59   /// Provide a local definition of Timeout for backward compat.
60   typedef L4::Ipc_svr::Timeout Timeout;
61
62   /**
63    * \brief Creates an empty timeout queue.
64    */
65   Timeout_queue() : _next_timeout(0) {}
66
67   /**
68    * \brief Determine if a timeout has happened
69    * \param now the current time.
70    * \return true if there is at least one expired timeout in the queue,
71    *         false if not.
72    */
73   bool timeout_expired(l4_kernel_clock_t now) const
74   {
75     if (_timeouts.empty())
76       return false;
77
78     return _next_timeout <= now;
79   }
80
81   /**
82    * \brief Get the time for the next timeout.
83    * \return the time for the next timeout or 0 if there is none
84    */
85   l4_kernel_clock_t next_timeout() const
86   {
87     return _next_timeout;
88   }
89
90   /**
91    * \brief run the callbacks of expired timeouts
92    * \param now the current time.
93    */
94   void handle_expired_timeouts(l4_kernel_clock_t now)
95   {
96     while (!_timeouts.empty())
97       {
98         Queue::Iterator top = _timeouts.begin();
99         if ((*top)->_timeout > now)
100           return;
101
102         Timeout *t = *top;
103         top = _timeouts.erase(top);
104         t->expired();
105         if (!_timeouts.empty())
106           _next_timeout = (*_timeouts.begin())->timeout();
107         else
108           _next_timeout = 0;
109       }
110   }
111
112   /**
113    * \brief Add a timeout to the queue
114    * \param timeout timeout object to add
115    * \param time the time when the timeout expires
116    * \pre \a timeout must not be in any queue already
117    */
118   void add(Timeout *timeout, l4_kernel_clock_t time)
119   {
120     timeout->_timeout = time;
121     Queue::Iterator i = _timeouts.begin();
122     while (i != _timeouts.end() && (*i)->timeout() < time)
123       ++i;
124
125     _timeouts.insert_before(timeout, i);
126     _next_timeout = (*_timeouts.begin())->timeout();
127   }
128
129   /**
130    * \brief Remove \a timeout from the queue.
131    * \param timeout  timeout to remove from timeout queue
132    * \pre \a timeout must be in this queue
133    */
134   void remove(Timeout *timeout)
135   {
136     _timeouts.remove(timeout);
137     if (_next_timeout == timeout->timeout())
138       {
139         if (!_timeouts.empty())
140           _next_timeout = (*_timeouts.begin())->timeout();
141         else
142           _next_timeout = 0;
143       }
144   }
145
146 private:
147   typedef cxx::H_list<Timeout> Queue;
148   Queue _timeouts;
149   l4_kernel_clock_t _next_timeout;
150 };
151
152 /**
153  * \ingroup cxx_ipc_server
154  * \brief Loop hooks mixin for integrating a timeout queue into the server
155  *        loop.
156  * \tparam HOOKS   has to inherit from Timeout_queue_hooks<> and provide
157  *                 the functions now() that has to return the current time.
158  * \tparam BR_MAN  This used as a base class for and provides the API for
159  *                 selecting the buffer register (BR) that is used to store the
160  *                 timeout value. This is usually L4Re::Util::Br_manager or
161  *                 L4::Ipc_svr::Br_manager_no_buffers.
162  *
163  * \implements L4::Ipc_svr::Server_iface
164  */
165 template< typename HOOKS, typename BR_MAN = Br_manager_no_buffers >
166 class Timeout_queue_hooks : public BR_MAN
167 {
168   l4_kernel_clock_t _now()
169   { return static_cast<HOOKS*>(this)->now(); }
170
171   unsigned _timeout_br()
172   { return this->first_free_br(); }
173
174 public:
175   /// get the time for the next timeout
176   l4_timeout_t timeout()
177   {
178     l4_kernel_clock_t t = queue.next_timeout();
179     if (t)
180        return l4_timeout(L4_IPC_TIMEOUT_0, l4_timeout_abs(t, _timeout_br()));
181     return L4_IPC_SEND_TIMEOUT_0;
182   }
183
184   /// setup_wait() for the server loop
185   void setup_wait(l4_utcb_t *utcb, L4::Ipc_svr::Reply_mode mode)
186   {
187     // we must handle the timer only when called after a possible reply
188     // otherwise we probably destroy the reply message.
189     if (mode == L4::Ipc_svr::Reply_separate)
190       {
191         l4_kernel_clock_t now = _now();
192         if (queue.timeout_expired(now))
193           queue.handle_expired_timeouts(now);
194       }
195
196     BR_MAN::setup_wait(utcb, mode);
197   }
198
199   /// server loop hook
200   L4::Ipc_svr::Reply_mode before_reply(l4_msgtag_t, l4_utcb_t *)
201   {
202     // split up reply and wait when a timeout has expired
203     if (queue.timeout_expired(_now()))
204       return L4::Ipc_svr::Reply_separate;
205     return L4::Ipc_svr::Reply_compound;
206   }
207
208   /**
209    * Add a timout to the queue for time \a time.
210    * \param timeout  The timeout object to add into the queue (must not be in
211    *                 any queue currently).
212    * \param time     The time when the timeout shall expire.
213    * \pre timeout must not be in any queue.
214    *
215    * \note The timeout is automatically dequeued before the Timeout::expired()
216    *       function is called
217    */
218   int add_timeout(Timeout *timeout, l4_kernel_clock_t time)
219   {
220     queue.add(timeout, time);
221     return 0;
222   }
223
224   /**
225    * Remove timeout from the queue.
226    * \param timeout  The timeout object to be removed from the queue.
227    * \note This function may be safely called even if the timeout is not
228    *       currently enqueued.
229    * \note in Timeout::expired() the timeout is already dequeued!
230    */
231   int remove_timeout(Timeout *timeout)
232   {
233     queue.remove(timeout);
234     return 0;
235   }
236
237   Timeout_queue queue; ///< Use this timeout queue
238 };
239
240 }}
241