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