3 * (c) 2014 Steffen Liebergeld <steffen.liebergeld@kernkonzept.com>
5 * This file is licensed under the terms of the GNU Lesser General Public
7 * See file COPYING-LGPL-2.1 for details.
11 #include <l4/cxx/hlist>
12 #include <l4/sys/cxx/ipc_server_loop>
14 namespace L4 { namespace Ipc_svr {
17 * \brief Callback interface for Timeout_queue
18 * \ingroup cxx_ipc_server
20 class Timeout : public cxx::H_list_item
22 friend class Timeout_queue;
25 Timeout() : _timeout(0) {}
28 virtual ~Timeout() = 0;
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.
35 virtual void expired() = 0;
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
43 l4_kernel_clock_t timeout() const
47 l4_kernel_clock_t _timeout;
50 inline Timeout::~Timeout() {}
53 * \brief Timeout queue to be used in l4re server loop
54 * \ingroup cxx_ipc_server
59 /// Provide a local definition of Timeout for backward compat.
60 typedef L4::Ipc_svr::Timeout Timeout;
63 * \brief Creates an empty timeout queue.
65 Timeout_queue() : _next_timeout(0) {}
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,
73 bool timeout_expired(l4_kernel_clock_t now) const
75 if (_timeouts.empty())
78 return _next_timeout <= now;
82 * \brief Get the time for the next timeout.
83 * \return the time for the next timeout or 0 if there is none
85 l4_kernel_clock_t next_timeout() const
91 * \brief run the callbacks of expired timeouts
92 * \param now the current time.
94 void handle_expired_timeouts(l4_kernel_clock_t now)
96 while (!_timeouts.empty())
98 Queue::Iterator top = _timeouts.begin();
99 if ((*top)->_timeout > now)
103 top = _timeouts.erase(top);
105 if (!_timeouts.empty())
106 _next_timeout = (*_timeouts.begin())->timeout();
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
118 void add(Timeout *timeout, l4_kernel_clock_t time)
120 timeout->_timeout = time;
121 Queue::Iterator i = _timeouts.begin();
122 while (i != _timeouts.end() && (*i)->timeout() < time)
125 _timeouts.insert_before(timeout, i);
126 _next_timeout = (*_timeouts.begin())->timeout();
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
134 void remove(Timeout *timeout)
136 _timeouts.remove(timeout);
137 if (_next_timeout == timeout->timeout())
139 if (!_timeouts.empty())
140 _next_timeout = (*_timeouts.begin())->timeout();
147 typedef cxx::H_list<Timeout> Queue;
149 l4_kernel_clock_t _next_timeout;
153 * \ingroup cxx_ipc_server
154 * \brief Loop hooks mixin for integrating a timeout queue into the server
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.
163 * \implements L4::Ipc_svr::Server_iface
165 template< typename HOOKS, typename BR_MAN = Br_manager_no_buffers >
166 class Timeout_queue_hooks : public BR_MAN
168 l4_kernel_clock_t _now()
169 { return static_cast<HOOKS*>(this)->now(); }
171 unsigned _timeout_br()
172 { return this->first_free_br(); }
175 /// get the time for the next timeout
176 l4_timeout_t timeout()
178 l4_kernel_clock_t t = queue.next_timeout();
180 return l4_timeout(L4_IPC_TIMEOUT_0, l4_timeout_abs(t, _timeout_br()));
181 return L4_IPC_SEND_TIMEOUT_0;
184 /// setup_wait() for the server loop
185 void setup_wait(l4_utcb_t *utcb, L4::Ipc_svr::Reply_mode mode)
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)
191 l4_kernel_clock_t now = _now();
192 if (queue.timeout_expired(now))
193 queue.handle_expired_timeouts(now);
196 BR_MAN::setup_wait(utcb, mode);
200 L4::Ipc_svr::Reply_mode before_reply(l4_msgtag_t, l4_utcb_t *)
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;
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.
215 * \note The timeout is automatically dequeued before the Timeout::expired()
218 int add_timeout(Timeout *timeout, l4_kernel_clock_t time)
220 queue.add(timeout, time);
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!
231 int remove_timeout(Timeout *timeout)
233 queue.remove(timeout);
237 Timeout_queue queue; ///< Use this timeout queue