genode/base/include/base/rpc_server.h
Norman Feske 5fe29e8e4a Express affinities via Cartesian coordinates
This patch introduces new types for expressing CPU affinities. Instead
of dealing with physical CPU numbers, affinities are expressed as
rectangles in a grid of virtual CPU nodes. This clears the way to
conveniently assign sets of adjacent CPUs to subsystems, each of them
managing their respective viewport of the coordinate space.

By using 2D Cartesian coordinates, the locality of CPU nodes can be
modeled for different topologies such as SMP (simple Nx1 grid), grids of
NUMA nodes, or ring topologies.
2013-08-13 17:08:24 +02:00

397 lines
12 KiB
C++

/*
* \brief Server-side API of the RPC framework
* \author Norman Feske
* \date 2006-04-28
*/
/*
* Copyright (C) 2006-2013 Genode Labs GmbH
*
* 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/printf.h>
#include <cap_session/cap_session.h>
namespace Genode {
/**
* 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 functions
* must be declared in 'RPC_INTERFACE'. In contrast, by explicitly specifying
* the 'SERVER' argument, the server-side dispatching performs direct function
* calls to the respective member functions of the 'SERVER' class and thereby
* omits virtual functions calls.
*/
template <typename RPC_INTERFACE, typename SERVER = RPC_INTERFACE>
class Rpc_dispatcher : public RPC_INTERFACE
{
/**
* Shortcut for the type list of RPC functions provided by this server
* component
*/
typedef typename RPC_INTERFACE::Rpc_functions Rpc_functions;
protected:
template <typename ARG_LIST>
void _read_args(Ipc_istream &is, ARG_LIST &args)
{
if (Trait::Rpc_direction<typename ARG_LIST::Head>::Type::IN)
is >> args._1;
_read_args(is, args._2);
}
void _read_args(Ipc_istream &, Meta::Empty) { }
template <typename ARG_LIST>
void _write_results(Ipc_ostream &os, ARG_LIST &args)
{
if (Trait::Rpc_direction<typename ARG_LIST::Head>::Type::OUT)
os << args._1;
_write_results(os, args._2);
}
void _write_results(Ipc_ostream &, Meta::Empty) { }
template <typename RPC_FUNCTION, typename EXC_TL>
Rpc_exception_code _do_serve(typename RPC_FUNCTION::Server_args &args,
typename RPC_FUNCTION::Ret_type &ret,
Meta::Overload_selector<RPC_FUNCTION, EXC_TL>)
{
enum { EXCEPTION_CODE = RPC_EXCEPTION_BASE - Meta::Length<EXC_TL>::Value };
try {
typedef typename EXC_TL::Tail Exc_tail;
return _do_serve(args, ret,
Meta::Overload_selector<RPC_FUNCTION, Exc_tail>());
} catch (typename EXC_TL::Head) { return EXCEPTION_CODE; }
}
template <typename RPC_FUNCTION>
Rpc_exception_code _do_serve(typename RPC_FUNCTION::Server_args &args,
typename RPC_FUNCTION::Ret_type &ret,
Meta::Overload_selector<RPC_FUNCTION, Meta::Empty>)
{
RPC_FUNCTION::serve(*static_cast<SERVER *>(this), args, ret);
return 0;
}
template <typename RPC_FUNCTIONS_TO_CHECK>
Rpc_exception_code _do_dispatch(Rpc_opcode opcode, Ipc_istream &is, Ipc_ostream &os,
Meta::Overload_selector<RPC_FUNCTIONS_TO_CHECK>)
{
using namespace Meta;
typedef typename RPC_FUNCTIONS_TO_CHECK::Head This_rpc_function;
if (opcode == Index_of<Rpc_functions, This_rpc_function>::Value) {
/*
* Argument receive buffer
*
* To prevent the compiler from complaining about the
* 'Server_args' data structure from being uninitialized,
* we instantiate the variable as volatile and strip away
* the volatile-ness when using it.
*/
struct {
typedef typename This_rpc_function::Server_args Data;
volatile Data _data;
Data &data() { return *(Data *)(&_data); }
} args;
/* read arguments from istream */
_read_args(is, args.data());
/*
* 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::Exceptions Exceptions;
typename This_rpc_function::Ret_type ret;
Rpc_exception_code exc;
exc = _do_serve(args.data(), ret, Overload_selector<This_rpc_function, Exceptions>());
os << ret;
/* write results to ostream 'os' */
_write_results(os, args.data());
return exc;
}
typedef typename RPC_FUNCTIONS_TO_CHECK::Tail Tail;
return _do_dispatch(opcode, is, os, Overload_selector<Tail>());
}
int _do_dispatch(int opcode, Ipc_istream &, Ipc_ostream &,
Meta::Overload_selector<Meta::Empty>)
{
PERR("invalid opcode %d\n", opcode);
return RPC_INVALID_OPCODE;
}
/**
* Handle corner case of having an RPC interface with no RPC functions
*/
Rpc_exception_code _do_dispatch(int opcode, Ipc_istream &, Ipc_ostream &,
Meta::Overload_selector<Meta::Type_list<> >)
{
return 0;
}
/**
* Protected constructor
*
* This class is only usable as base class.
*/
Rpc_dispatcher() { }
public:
Rpc_exception_code dispatch(int opcode, Ipc_istream &is, Ipc_ostream &os)
{
return _do_dispatch(opcode, is, os,
Meta::Overload_selector<Rpc_functions>());
}
};
class Rpc_object_base : public Object_pool<Rpc_object_base>::Entry
{
public:
virtual ~Rpc_object_base() { }
/**
* Interface to be implemented by a derived class
*
* \param op opcode of invoked method
* \param is Ipc_input stream with method arguments
* \param os Ipc_output stream for storing method results
*/
virtual int dispatch(int op, Ipc_istream &is, Ipc_ostream &os) = 0;
};
/**
* 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 Rpc_object : Rpc_object_base, Rpc_dispatcher<RPC_INTERFACE, SERVER>
{
/*****************************
** Server-object interface **
*****************************/
Rpc_exception_code dispatch(int opcode, Ipc_istream &is, Ipc_ostream &os)
{
return Rpc_dispatcher<RPC_INTERFACE, SERVER>::dispatch(opcode, is, os);
}
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()' function. 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 Rpc_entrypoint : Thread_base, 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 function 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>
{
int exit;
Exit_handler() : exit(false) { }
void _exit() { exit = true; }
};
protected:
Ipc_server *_ipc_server;
Rpc_object_base *_curr_obj; /* currently dispatched RPC object */
Lock _curr_obj_lock; /* for the protection of '_curr_obj' */
Lock _cap_valid; /* thread startup synchronization */
Lock _delay_start; /* delay start of request dispatching */
Lock _delay_exit; /* delay destructor until server settled */
Cap_session *_cap_session; /* for creating capabilities */
Exit_handler _exit_handler;
Capability<Exit> _exit_cap;
/**
* Back-end function to associate RPC object with the entry point
*/
Untyped_capability _manage(Rpc_object_base *obj);
/**
* Back-end function to Dissolve RPC object from entry point
*/
void _dissolve(Rpc_object_base *obj);
/**
* Force activation to cancel dispatching the specified server object
*/
void _leave_server_object(Rpc_object_base *obj);
/**
* Wait until the entrypoint activation is initialized
*/
void _block_until_cap_valid();
/**
* Thread interface
*/
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(Cap_session *cap_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));
}
/**
* Dissolve server object from entry point
*/
template <typename RPC_INTERFACE, typename RPC_SERVER>
void dissolve(Rpc_object<RPC_INTERFACE, RPC_SERVER> *obj)
{
_dissolve(obj);
}
/**
* Activate entrypoint, start processing RPC requests
*/
void activate();
/**
* Request reply capability for current call
*
* Note: This is a temporary API function, which is going to be
* removed. Please do not use this function.
*
* Typically, a capability obtained via this function is used as
* argument of 'intermediate_reply'.
*/
Untyped_capability reply_dst();
/**
* Prevent reply of current request
*
* Note: This is a temporary API function, which is going to be
* removed. Please do not use this function.
*
* This function 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' function.
*/
void omit_reply();
/**
* Send a reply out of the normal call-reply order
*
* Note: This is a temporary API function, which is going to be
* removed. Please do not use this function.
*
* In combination with the 'reply_dst' accessor functions, this
* function can be used to implement services that dispatch client
* requests out of order. In such cases, the server activation may
* send reply messages to multiple blocking clients before
* answering the original call.
*/
void explicit_reply(Untyped_capability reply_cap, int return_value);
/**
* Return true if the caller corresponds to the entrypoint called
*/
bool is_myself() const;
};
}
#endif /* _INCLUDE__BASE__RPC_SERVER_H_ */