genode/repos/base/include/base/rpc_server.h

497 lines
14 KiB
C
Raw Normal View History

2011-12-22 16:19:25 +01:00
/*
* \brief Server-side API of the RPC framework
* \author Norman Feske
* \date 2006-04-28
*/
/*
2013-01-10 21:44:47 +01:00
* Copyright (C) 2006-2013 Genode Labs GmbH
2011-12-22 16:19:25 +01:00
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__BASE__RPC_SERVER_H_
#define _INCLUDE__BASE__RPC_SERVER_H_
#include <base/rpc.h>
#include <base/thread.h>
#include <base/ipc.h>
#include <base/object_pool.h>
#include <base/lock.h>
#include <base/log.h>
2013-08-13 16:15:52 +02:00
#include <base/trace/events.h>
2011-12-22 16:19:25 +01:00
#include <cap_session/cap_session.h>
namespace Genode {
class Ipc_server;
template <typename, typename> class Rpc_dispatcher;
class Rpc_object_base;
template <typename, typename> struct Rpc_object;
class Rpc_entrypoint;
}
2011-12-22 16:19:25 +01:00
/**
* RPC dispatcher implementing the specified RPC interface
*
* \param RPC_INTERFACE class providing the RPC interface description
* \param SERVER class to invoke for the server-side RPC functions
*
* This class is the base class of each server-side RPC implementation. It
* contains the logic for dispatching incoming RPC requests and calls the
* server functions according to the RPC declarations in 'RPC_INTERFACE'.
*
* If using the default argument for 'SERVER', the 'RPC_INTERFACE' is expected
* to contain the abstract interface for all RPC functions. So virtual methods
* must be declared in 'RPC_INTERFACE'. In contrast, by explicitly specifying
* the 'SERVER' argument, the server-side dispatching performs direct
* calls to the respective methods of the 'SERVER' class and thereby
* omits virtual method calls.
*/
template <typename RPC_INTERFACE, typename SERVER = RPC_INTERFACE>
class Genode::Rpc_dispatcher : public RPC_INTERFACE
{
2011-12-22 16:19:25 +01:00
/**
* Shortcut for the type list of RPC functions provided by this server
* component
2011-12-22 16:19:25 +01:00
*/
typedef typename RPC_INTERFACE::Rpc_functions Rpc_functions;
2011-12-22 16:19:25 +01:00
protected:
2011-12-22 16:19:25 +01:00
template <typename ARG_LIST>
ARG_LIST _read_args(Ipc_unmarshaller &msg,
Meta::Overload_selector<ARG_LIST>)
{
typename Trait::Rpc_direction<typename ARG_LIST::Head>::Type direction;
typedef typename ARG_LIST::Stored_head Arg;
Arg arg = _read_arg<Arg>(msg, direction);
2011-12-22 16:19:25 +01:00
Meta::Overload_selector<typename ARG_LIST::Tail> tail_selector;
typename ARG_LIST::Tail subsequent_args = _read_args(msg,
tail_selector);
ARG_LIST args { arg, subsequent_args };
return args;
}
2011-12-22 16:19:25 +01:00
Meta::Empty _read_args(Ipc_unmarshaller &msg,
Meta::Overload_selector<Meta::Empty>)
{
return Meta::Empty();
}
template <typename ARG>
ARG _read_arg(Ipc_unmarshaller &msg, Rpc_arg_in)
{
return msg.extract(Meta::Overload_selector<ARG>());
}
template <typename ARG>
ARG _read_arg(Ipc_unmarshaller &msg, Rpc_arg_inout)
{
return _read_arg<ARG>(msg, Rpc_arg_in());
}
template <typename ARG>
ARG _read_arg(Ipc_unmarshaller &msg, Rpc_arg_out)
{
return ARG();
}
2011-12-22 16:19:25 +01:00
template <typename ARG_LIST>
void _write_results(Msgbuf_base &msg, ARG_LIST &args)
{
if (Trait::Rpc_direction<typename ARG_LIST::Head>::Type::OUT)
msg.insert(args._1);
2011-12-22 16:19:25 +01:00
_write_results(msg, args._2);
}
2011-12-22 16:19:25 +01:00
void _write_results(Msgbuf_base &, Meta::Empty) { }
2011-12-22 16:19:25 +01:00
template <typename RPC_FUNCTION, typename EXC_TL>
typename RPC_FUNCTION::Ret_type
_do_serve(typename RPC_FUNCTION::Server_args &args,
Meta::Overload_selector<RPC_FUNCTION, EXC_TL>)
{
enum { EXCEPTION_CODE = Rpc_exception_code::EXCEPTION_BASE
- Meta::Length<EXC_TL>::Value };
try {
typedef typename EXC_TL::Tail Exc_tail;
return _do_serve(args,
Meta::Overload_selector<RPC_FUNCTION, Exc_tail>());
} catch (typename EXC_TL::Head) {
/**
* By passing the exception code through an exception we ensure that
* a return value is only returned if it exists. This way, the return
* type does not have to be default-constructible.
*/
throw Rpc_exception_code(EXCEPTION_CODE);
}
}
2011-12-22 16:19:25 +01:00
template <typename RPC_FUNCTION>
typename RPC_FUNCTION::Ret_type
_do_serve(typename RPC_FUNCTION::Server_args &args,
Meta::Overload_selector<RPC_FUNCTION, Meta::Empty>)
{
typedef typename RPC_FUNCTION::Ret_type Ret_type;
SERVER *me = static_cast<SERVER *>(this);
return RPC_FUNCTION::template serve<SERVER, Ret_type>(*me, args);
}
2011-12-22 16:19:25 +01:00
template <typename RPC_FUNCTIONS_TO_CHECK>
Rpc_exception_code _do_dispatch(Rpc_opcode opcode,
Ipc_unmarshaller &in, Msgbuf_base &out,
Meta::Overload_selector<RPC_FUNCTIONS_TO_CHECK>)
{
using namespace Meta;
2011-12-22 16:19:25 +01:00
typedef typename RPC_FUNCTIONS_TO_CHECK::Head This_rpc_function;
2011-12-22 16:19:25 +01:00
if (opcode.value == Index_of<Rpc_functions, This_rpc_function>::Value) {
2011-12-22 16:19:25 +01:00
/* read arguments from incoming message */
typedef typename This_rpc_function::Server_args Server_args;
Meta::Overload_selector<Server_args> arg_selector;
Server_args args = _read_args(in, arg_selector);
2011-12-22 16:19:25 +01:00
{
Trace::Rpc_dispatch trace_event(This_rpc_function::name());
}
2011-12-22 16:19:25 +01:00
/*
* Dispatch call to matching RPC base class, using
* 'This_rpc_function' and the list of its exceptions to
* select the overload.
*/
typedef typename This_rpc_function::Ret_type Ret_type;
Rpc_exception_code exc(Rpc_exception_code::SUCCESS);
try {
typedef typename This_rpc_function::Exceptions Exceptions;
Overload_selector<This_rpc_function, Exceptions> overloader;
Ret_type ret = _do_serve(args, overloader);
_write_results(out, args);
out.insert(ret);
} catch (Rpc_exception_code thrown) {
/**
* Output arguments may be modified although an exception was thrown.
* However, a return value does not exist. So we do not insert one.
*/
_write_results(out, args);
exc = thrown;
}
2011-12-22 16:19:25 +01:00
{
Trace::Rpc_reply trace_event(This_rpc_function::name());
2011-12-22 16:19:25 +01:00
}
return exc;
2011-12-22 16:19:25 +01:00
}
typedef typename RPC_FUNCTIONS_TO_CHECK::Tail Tail;
return _do_dispatch(opcode, in, out, Overload_selector<Tail>());
}
Rpc_exception_code _do_dispatch(Rpc_opcode opcode,
Ipc_unmarshaller &, Msgbuf_base &,
Meta::Overload_selector<Meta::Empty>)
{
error("invalid opcode ", opcode.value);
return Rpc_exception_code(Rpc_exception_code::INVALID_OPCODE);
}
/**
* Handle corner case of having an RPC interface with no RPC functions
*/
Rpc_exception_code _do_dispatch(Rpc_opcode opcode,
Ipc_unmarshaller &, Msgbuf_base &,
Meta::Overload_selector<Meta::Type_list<> >)
{
return Rpc_exception_code(Rpc_exception_code::SUCCESS);
}
2011-12-22 16:19:25 +01:00
/**
* Protected constructor
*
* This class is only usable as base class.
*/
Rpc_dispatcher() { }
2011-12-22 16:19:25 +01:00
public:
2011-12-22 16:19:25 +01:00
Rpc_exception_code dispatch(Rpc_opcode opcode,
Ipc_unmarshaller &in, Msgbuf_base &out)
{
return _do_dispatch(opcode, in, out,
Meta::Overload_selector<Rpc_functions>());
}
};
2011-12-22 16:19:25 +01:00
class Genode::Rpc_object_base : public Object_pool<Rpc_object_base>::Entry
{
public:
2011-12-22 16:19:25 +01:00
virtual ~Rpc_object_base() { }
2011-12-22 16:19:25 +01:00
/**
* Interface to be implemented by a derived class
*
* \param op opcode of invoked method
* \param in incoming message with method arguments
* \param out outgoing message for storing method results
*/
virtual Rpc_exception_code
dispatch(Rpc_opcode op, Ipc_unmarshaller &in, Msgbuf_base &out) = 0;
};
2011-12-22 16:19:25 +01:00
/**
* Object that is accessible from remote protection domains
*
* A 'Rpc_object' is a locally implemented object that can be referenced
* from the outer world using a capability. The capability gets created
* when attaching a 'Rpc_object' to a 'Rpc_entrypoint'.
*/
template <typename RPC_INTERFACE, typename SERVER = RPC_INTERFACE>
struct Genode::Rpc_object : Rpc_object_base, Rpc_dispatcher<RPC_INTERFACE, SERVER>
{
/*****************************
** Server-object interface **
*****************************/
Rpc_exception_code dispatch(Rpc_opcode opcode, Ipc_unmarshaller &in, Msgbuf_base &out)
2011-12-22 16:19:25 +01:00
{
return Rpc_dispatcher<RPC_INTERFACE, SERVER>::dispatch(opcode, in, out);
}
2011-12-22 16:19:25 +01:00
Capability<RPC_INTERFACE> const cap() const
{
return reinterpret_cap_cast<RPC_INTERFACE>(Rpc_object_base::cap());
}
};
/**
* RPC entrypoint serving RPC objects
*
* The entrypoint's thread will initialize its capability but will not
* immediately enable the processing of requests. This way, the
* activation-using server can ensure that it gets initialized completely
* before the first capability invocations come in. Once the server is
* ready, it must enable the entrypoint explicitly by calling the
* 'activate()' method. The 'start_on_construction' argument is a
* shortcut for the common case where the server's capability is handed
* over to other parties _after_ the server is completely initialized.
*/
class Genode::Rpc_entrypoint : Thread, public Object_pool<Rpc_object_base>
{
private:
/**
* Prototype capability to derive capabilities for RPC objects
* from.
*/
Untyped_capability _cap;
enum { SND_BUF_SIZE = 1024, RCV_BUF_SIZE = 1024 };
Msgbuf<SND_BUF_SIZE> _snd_buf;
Msgbuf<RCV_BUF_SIZE> _rcv_buf;
/**
* Hook to let low-level thread init code access private members
*
* This method is only used on NOVA.
*/
static void _activation_entry();
struct Exit
{
GENODE_RPC(Rpc_exit, void, _exit);
GENODE_RPC_INTERFACE(Rpc_exit);
};
struct Exit_handler : Rpc_object<Exit, Exit_handler>
2011-12-22 16:19:25 +01:00
{
int exit;
Exit_handler() : exit(false) { }
void _exit() { exit = true; }
};
protected:
Native_capability _caller;
Lock _cap_valid; /* thread startup synchronization */
Lock _delay_start; /* delay start of request dispatching */
Lock _delay_exit; /* delay destructor until server settled */
Pd_session &_pd_session; /* for creating capabilities */
Exit_handler _exit_handler;
Capability<Exit> _exit_cap;
/**
* Access to kernel-specific part of the PD session interface
*
* Some kernels like NOVA need a special interface for creating RPC
* object capabilities.
*/
Capability<Pd_session::Native_pd> _native_pd_cap;
/**
* Back end used to associate RPC object with the entry point
*
* \noapi
*/
Untyped_capability _manage(Rpc_object_base *obj);
/**
* Back end used to Dissolve RPC object from entry point
*
* \noapi
*/
void _dissolve(Rpc_object_base *obj);
/**
* Wait until the entrypoint activation is initialized
*
* \noapi
*/
void _block_until_cap_valid();
/**
* Allocate new RPC object capability
*
* Regular servers allocate capabilities from their protection domain
* via the component's environment. This method allows core to have a
* special implementation that does not rely on a PD session.
*
* The 'entry' argument is used only on NOVA. It is the server-side
* instruction pointer to be associated with the RPC object capability.
*/
Native_capability _alloc_rpc_cap(Pd_session &, Native_capability ep,
addr_t entry = 0);
/**
* Free RPC object capability
*/
void _free_rpc_cap(Pd_session &, Native_capability);
/**
* Thread interface
*
* \noapi
*/
void entry();
public:
/**
* Constructor
*
* \param cap_session 'Cap_session' for creating capabilities
* for the RPC objects managed by this entry
* point
* \param stack_size stack size of entrypoint thread
* \param name name of entrypoint thread
* \param location CPU affinity
*/
Rpc_entrypoint(Pd_session *pd_session, size_t stack_size,
char const *name, bool start_on_construction = true,
Affinity::Location location = Affinity::Location());
~Rpc_entrypoint();
/**
* Associate RPC object with the entry point
*/
template <typename RPC_INTERFACE, typename RPC_SERVER>
Capability<RPC_INTERFACE>
manage(Rpc_object<RPC_INTERFACE, RPC_SERVER> *obj)
{
return reinterpret_cap_cast<RPC_INTERFACE>(_manage(obj));
2011-12-22 16:19:25 +01:00
}
/**
* Dissolve RPC object from entry point
*/
template <typename RPC_INTERFACE, typename RPC_SERVER>
void dissolve(Rpc_object<RPC_INTERFACE, RPC_SERVER> *obj)
2011-12-22 16:19:25 +01:00
{
_dissolve(obj);
2011-12-22 16:19:25 +01:00
}
/**
* Activate entrypoint, start processing RPC requests
*/
void activate();
2011-12-22 16:19:25 +01:00
/**
* Request reply capability for current call
*
* \noapi
*
* Note: This is a temporary API method, which is going to be
* removed. Please do not use this method.
*
* Typically, a capability obtained via this method is used as
* argument of 'intermediate_reply'.
*/
Untyped_capability reply_dst() { return _caller; }
2011-12-22 16:19:25 +01:00
/**
* Prevent reply of current request
*
* \noapi
*
* Note: This is a temporary API method, which is going to be
* removed. Please do not use this method.
*
* This method can be used to keep the calling client blocked
* after the server has finished the processing of the client's
* request. At a later time, the server may chose to unblock the
* client via the 'intermedate_reply' method.
*/
void omit_reply() { _caller = Native_capability(); }
2011-12-22 16:19:25 +01:00
/**
* Send a reply out of the normal call-reply order
*
* \noapi
*
* In combination with the 'reply_dst' accessor method, this method
* allows for the dispatching of client requests out of order. The only
* designated user of this method is core's PD service. The
* 'Pd_session::submit' RPC function uses it to send a reply to a
* caller of the 'Signal_source::wait_for_signal' RPC function before
* returning from the 'submit' call.
*/
void reply_signal_info(Untyped_capability reply_cap,
unsigned long imprint, unsigned long cnt);
/**
* Return true if the caller corresponds to the entrypoint called
*
* \noapi
*
* This method is solely needed on Linux.
*/
bool is_myself() const;
/**
* Required outside of core. E.g. launchpad needs it to forcefully kill
* a client which blocks on a session opening request where the service
* is not up yet.
*/
void cancel_blocking() { Thread::cancel_blocking(); }
};
2011-12-22 16:19:25 +01:00
#endif /* _INCLUDE__BASE__RPC_SERVER_H_ */