L4Re - L4 Runtime Environment
ipc_server_loop
1 // vi:set ft=cpp: -*- Mode: C++ -*-
2 /*
3  * Copyright (C) 2015 Kernkonzept GmbH.
4  * Author(s): Alexander Warg <alexander.warg@kernkonzept.com>
5  *
6  * This file is distributed under the terms of the GNU General Public
7  * License, version 2. Please see the COPYING-GPL-2 file for details.
8  *
9  * As a special exception, you may use this file as part of a free software
10  * library without restriction. Specifically, if other files instantiate
11  * templates or use macros or inline functions from this file, or you compile
12  * this file and link it with other files to produce an executable, this
13  * file does not by itself cause the resulting executable to be covered by
14  * the GNU General Public License. This exception does not however
15  * invalidate any other reasons why the executable file might be covered by
16  * the GNU General Public License.
17  */
18 #pragma once
19 
20 #include "ipc_epiface"
21 
22 namespace L4 {
23 
24 /**
25  * \ingroup client_server_ipc
26  *
27  * Server-Side framework for implementing object-oriented servers.ยด
28  * \defgroup cxx_ipc_server Server-Side IPC framework
29  */
30 /**
31  * \ingroup cxx_ipc_server
32  *
33  * Helper classes for L4::Server instantiation.
34  */
35 namespace Ipc_svr {
36 
37 /**
38  * \ingroup cxx_ipc_server
39  *
40  * Reply mode for server loop.
41  *
42  * The reply mode specifies if the server loop shall do a compound reply
43  * and wait operation (#Reply_compound), which is the most performant
44  * method. Note, setup_wait() is called before the reply. The other
45  * way is to call reply and wait separately and call setup_wait in between.
46  *
47  * The actual mode is determined by the return value of the before_reply()
48  * hook in the LOOP_HOOKS of L4::Server.
49  */
50 enum Reply_mode
51 {
52  Reply_compound, ///< Server shall use a compound reply and wait (fast).
53  Reply_separate ///< Server shall call reply and wait separately.
54 };
55 
56 /**
57  * \ingroup cxx_ipc_server
58  *
59  * Mix in for LOOP_HOOKS to ignore IPC errors.
60  */
61 struct Ignore_errors
62 { static void error(l4_msgtag_t, l4_utcb_t *) {} };
63 
64 /**
65  * \ingroup cxx_ipc_server
66  *
67  * Mix in for LOOP_HOOKS to use a 0 send and a infinite receive timeout.
68  */
69 struct Default_timeout
70 { static l4_timeout_t timeout() { return L4_IPC_SEND_TIMEOUT_0; } };
71 
72 /**
73  * \ingroup cxx_ipc_server
74  *
75  * Mix in for LOOP_HOOKS to always use compound reply and wait.
76  */
77 struct Compound_reply
78 {
79  static Reply_mode before_reply(l4_msgtag_t, l4_utcb_t *)
80  { return Reply_compound; }
81 };
82 
83 /**
84  * \ingroup cxx_ipc_server
85  *
86  * Mix in for LOOP_HOOKS for setup_wait no op.
87  */
88 struct Default_setup_wait
89 { static void setup_wait(l4_utcb_t *, Reply_mode) {} };
90 
91 /**
92  * DEPRECATED
93  * \ingroup cxx_ipc_server
94  * \deprecated Use L4::Ipc_svr::Timeout_queue_hooks
95  */
96 template< typename HOOKS >
97 class Timed_work : public HOOKS
98 {
99 protected:
100  l4_cpu_time_t _timeout;
101 
102 public:
103  Timed_work()
104  : _timeout(HOOKS::next_timeout(HOOKS::current_time())) {}
105 
106  l4_timeout_t timeout()
107  {
108  return l4_timeout(L4_IPC_TIMEOUT_0,
109  l4_timeout_abs(_timeout, this->timeout_br()));
110  }
111 
112  void setup_wait(l4_utcb_t *utcb, Reply_mode mode)
113  {
114  if (_timeout <= this->current_time()
115  && mode == Reply_separate)
116  {
117  this->work();
118  _timeout = this->next_timeout(_timeout);
119  }
120  HOOKS::setup_wait(utcb, mode);
121  }
122 
123  Reply_mode before_reply(l4_msgtag_t, l4_utcb_t *)
124  {
125  if (_timeout <= this->current_time())
126  return Reply_separate;
127  return Reply_compound;
128  }
129 };
130 
131 /**
132  * Direct disptach helper, for forwarding dispatch calls
133  * a registry \a R.
134  * \tparam R Data type of the registry that is used for dispatching to
135  * different server objects, usually based on the protected IPC
136  * label.
137  */
138 template< typename R >
139 struct Direct_dispatch
140 {
141  /// stores a reference to the registry object
142  R &r;
143 
144  /// Make a direct dispatcher
145  Direct_dispatch(R &r) : r(r) {}
146 
147  /// call operator forwarding to r.dispatch()
148  l4_msgtag_t operator () (l4_msgtag_t tag, l4_umword_t obj, l4_utcb_t *utcb)
149  { return r.dispatch(tag, obj, utcb); }
150 };
151 
152 /**
153  * Direct disptach helper, for forwarding dispatch calls via a pointer to
154  * a registry \a R.
155  * \tparam R Data type of the registry that is used for dispatching to
156  * different server objects, usually based on the protected IPC
157  * label.
158  */
159 template< typename R >
160 struct Direct_dispatch<R*>
161 {
162  /// stores a pointer to the registry object
163  R *r;
164 
165  /// Make a direct dispatcher
166  Direct_dispatch(R *r) : r(r) {}
167 
168  /// call operator forwarding to r->dispatch()
169  l4_msgtag_t operator () (l4_msgtag_t tag, l4_umword_t obj, l4_utcb_t *utcb)
170  { return r->dispatch(tag, obj, utcb); }
171 };
172 
173 #ifdef __EXCEPTIONS
174 /**
175  * Dispatch helper wrapping try {} catch {} around the dispatch call.
176  * \tparam R Data type of the registry used for dispatching to objects.
177  * \tparam Exc Data type of the exceptions that shall be catched.
178  * This data type must provide a member err_no() that returns
179  * the negative integer (int) error code for the exception.
180  *
181  * This dispatcher wraps Direct_dispatch<R> with a try-catch (Exc).
182  */
183 template< typename R, typename Exc> // = L4::Runtime_error>
184 struct Exc_dispatch : private Direct_dispatch<R>
185 {
186  /// Make en exception handling dispatcher
187  Exc_dispatch(R r) : Direct_dispatch<R>(r) {}
188 
189  /**
190  * Dispatch the call via Direct_dispatch<R>() and handle exceptions.
191  */
192  l4_msgtag_t operator () (l4_msgtag_t tag, l4_umword_t obj, l4_utcb_t *utcb)
193  {
194  try
195  {
196  return Direct_dispatch<R>::operator () (tag, obj, utcb);
197  }
198  catch (Exc &e)
199  {
200  return l4_msgtag(e.err_no(), 0, 0, 0);
201  }
202  }
203 };
204 #endif
205 
206 /**
207  * \ingroup cxx_ipc_server
208  *
209  * Empty implementation of Server_iface.
210  *
211  * This implementation of Server_iface provides no buffer or timeout management
212  * at all it just returns errors for all calls that express other than empty
213  * demands. However, this may be useful for very simple servers that serve
214  * simple server objects only.
215  */
216 class Br_manager_no_buffers : public Server_iface
217 {
218 public:
219  /**
220  * \copydoc Server_iface::alloc_buffer_demand()
221  * \return success (0) if demand is empty, -L4_ENOMEM else.
222  */
223  int alloc_buffer_demand(Demand const &demand)
224  {
225  if (!demand.no_demand())
226  return -L4_ENOMEM;
227  return L4_EOK;
228  }
229 
230  /// Returns L4::Cap<void>::Invalid, we have no buffer management
231  L4::Cap<void> get_rcv_cap(int) const
232  { return L4::Cap<void>::Invalid; }
233 
234  /// Returns -L4_ENOMEM, we have no buffer management
235  int realloc_rcv_cap(int)
236  { return -L4_ENOMEM; }
237 
238  /// Returns -L4_ENOSYS, we have no timeout queue
239  int add_timeout(Timeout *, l4_kernel_clock_t)
240  { return -L4_ENOSYS; }
241 
242  /// Returns -L4_ENOSYS, we have no timeout queue
243  int remove_timeout(Timeout *)
244  { return -L4_ENOSYS; }
245 
246 protected:
247  /// Returns 1 as first free buffer
248  unsigned first_free_br() const
249  { return 1; }
250 
251  /// Setup wait function for the server loop (Server<>).
252  void setup_wait(l4_utcb_t *utcb, L4::Ipc_svr::Reply_mode)
253  {
254  l4_buf_regs_t *br = l4_utcb_br_u(utcb);
255  br->bdr = 0;
256  br->br[0] = 0;
257  }
258 };
259 
260 /**
261  * \ingroup cxx_ipc_server
262  *
263  * Default LOOP_HOOKS.
264  *
265  * Combination of Ignore_errors, Default_timeout, Compound_reply,
266  * and Br_manager_no_buffers.
267  */
268 struct Default_loop_hooks :
269  public Ignore_errors, public Default_timeout, public Compound_reply,
270  Br_manager_no_buffers
271 {};
272 
273 }
274 
275 /**
276  * \ingroup cxx_ipc_server
277  *
278  * Basic server loop for handling client requests.
279  * \param LOOP_HOOKS the server inherits from LOOP_HOOKS and calls the
280  * hooks defined in LOOP_HOOKS in the server loop.
281  * See Ipc_svr::Default_loop_hooks, Ipc_svr::Ignore_errors,
282  * Ipc_svr::Default_timeout, Ipc_svr::Compound_reply, and
283  * Ipc_svr::Br_manager_no_buffers.
284  *
285  * This is basically a simple server loop that uses a single message buffer
286  * for receiving requests and sending replies. The dispatcher determines
287  * how incoming messages are handled.
288  */
289 template< typename LOOP_HOOKS = Ipc_svr::Default_loop_hooks >
290 class Server :
291  public LOOP_HOOKS
292 {
293 public:
294  /**
295  * Initializes the server loop.
296  * \param utcb The UTCB of the thread running the server loop.
297  */
298  explicit Server(l4_utcb_t *utcb) : _utcb(utcb) {}
299 
300  /**
301  * The server loop.
302  *
303  * This function usually never returns, it waits for
304  * incoming messages calls the dispatcher, sends a reply and waits again.
305  */
306  template< typename DISPATCH >
307  inline L4_NORETURN void internal_loop(DISPATCH dispatch);
308 
309  /**
310  * Server loop without exception handling.
311  */
312  template< typename R >
313  inline L4_NORETURN void loop_noexc(R r)
314  { internal_loop(Ipc_svr::Direct_dispatch<R>(r)); }
315 #ifdef __EXCEPTIONS
316  /**
317  * Server loop with internal exception handling.
318  *
319  * This server loop translates L4::Runtime_error exceptions
320  * into negative error return codes sent to the caller.
321  */
322  template< typename EXC, typename R >
323  inline L4_NORETURN void loop(R r)
324  { internal_loop(Ipc_svr::Exc_dispatch<R, EXC>(r)); while(1) ; }
325 #endif
326 protected:
327  /// Internal implementation for reply and wait
328  inline l4_msgtag_t reply_n_wait(l4_msgtag_t reply, l4_umword_t *p);
329 
330 public:
331  l4_utcb_t *_utcb;
332 };
333 
334 template< typename L >
335 inline l4_msgtag_t
336 Server<L>::reply_n_wait(l4_msgtag_t reply, l4_umword_t *p)
337 {
338  if (reply.label() != -L4_ENOREPLY)
339  {
340  Ipc_svr::Reply_mode m = this->before_reply(reply, _utcb);
341  if (m == Ipc_svr::Reply_compound)
342  {
343  this->setup_wait(_utcb, m);
344  return l4_ipc_reply_and_wait(_utcb, reply, p, this->timeout());
345  }
346  else
347  {
348  l4_msgtag_t res = l4_ipc_send(L4_INVALID_CAP | L4_SYSF_REPLY, _utcb, reply, this->timeout());
349  if (res.has_error())
350  return res;
351  }
352  }
353  this->setup_wait(_utcb, Ipc_svr::Reply_separate);
354  return l4_ipc_wait(_utcb, p, this->timeout());
355 }
356 
357 template< typename L >
358 template< typename DISPATCH >
359 inline L4_NORETURN void
360 Server<L>::internal_loop(DISPATCH dispatch)
361 {
362  l4_msgtag_t res;
363  l4_umword_t p;
364  l4_msgtag_t r = l4_msgtag(-L4_ENOREPLY, 0, 0, 0);
365 
366  while (true)
367  {
368  res = reply_n_wait(r, &p);
369  if (res.has_error())
370  {
371  this->error(res, _utcb);
372  r = l4_msgtag(-L4_ENOREPLY, 0, 0, 0);
373  continue;
374  }
375 
376  r = dispatch(res, p, _utcb);
377  }
378 }
379 
380 } // namespace L4