X-Git-Url: http://g0dil.de/git?a=blobdiff_plain;f=PPI%2FConnectors.hh;h=e6b8c07af00c67d44e45ce232f4841876907d891;hb=d5a72d0b3f6fee56dba6de1c54cafb448ebe3457;hp=e42bab9172e744f2ea7c6fdc1956f1b2d7a5f759;hpb=e84dd6c52a07fc9e283cbd72c7616f3523920387;p=senf.git diff --git a/PPI/Connectors.hh b/PPI/Connectors.hh index e42bab9..e6b8c07 100644 --- a/PPI/Connectors.hh +++ b/PPI/Connectors.hh @@ -1,6 +1,8 @@ -// Copyright (C) 2007 -// Fraunhofer Institut fuer offene Kommunikationssysteme (FOKUS) -// Kompetenzzentrum fuer Satelitenkommunikation (SatCom) +// $Id$ +// +// Copyright (C) 2007 +// Fraunhofer Institute for Open Communication Systems (FOKUS) +// Competence Center NETwork research (NET), St. Augustin, GERMANY // Stefan Bund // // This program is free software; you can redistribute it and/or modify @@ -21,18 +23,20 @@ /** \file \brief Connectors public header */ -#ifndef HH_Connectors_ -#define HH_Connectors_ 1 +#ifndef HH_SENF_PPI_Connectors_ +#define HH_SENF_PPI_Connectors_ 1 // Custom includes #include #include #include -#include "../Utils/SafeBool.hh" +#include "../Utils/safe_bool.hh" +#include "../Utils/Exception.hh" #include "../Packets/Packets.hh" #include "predecl.hh" #include "detail/Callback.hh" #include "Queueing.hh" +#include "ModuleManager.hh" //#include "Connectors.mpp" ///////////////////////////////hh.p//////////////////////////////////////// @@ -42,22 +46,111 @@ namespace ppi { namespace connector { /** \namespace senf::ppi::connector - \brief Connector classes + \brief Connector classes - A connector has two independent properties + A connector has three independent properties \li it may be \e active or \e passive \li it may be an \e input or an \e output - + \li it has an (optional) packet type + \e Active connectors are activated from within the module, \e passive connectors are - signaled by the external framework. \e Input modules receive packets, \e output modules send - packets. + signaled by the external framework. \e Input connectors receive packets, \e output + connectors send packets. All passive connectors call some onRequest callback whenever I/O needs to be performed. All - input modules possess a packet queue. + input connectors possess a packet queue. + + We therefore have 4 connector types each of which is parameterized by the type of packet + traversing the connector: + \li senf::ppi::connector::ActiveInput + \li senf::ppi::connector::ActiveOutput + \li senf::ppi::connector::PassiveInput + \li senf::ppi::connector::PassiveOutput. + + Connectors are declared as module data members and are then externally connected to other + modules. + + The connectors each take an optional template argument. If this argument is specified, it + must be the type of packet expected or sent on this connector. If it is not specified, + packets will be passed using the generic Packet handle. + + \code + class IpFilter : public senf::ppi::module::Module + { + SENF_PPI_MODULE(SomeModule); + + public: + senf::ppi::connector::ActiveInput input; + senf::ppi::connector::PassiveOutput output; + + IpFilter() { + route(input, output); + input.onRequest(&IpFilter::onRequest); + } + + private: + void onRequest() { + // 'input()' will return a senf::EthernetPacket packet handle + try { output( input().find() ); } + catch (senf::InvalidPacketChainException & ex) { ; } + } + }; + \endcode + + + \section ppi_jacks Jacks + + A Jack is a packet type aware and possibly packet type converting reference to an arbitrary + connector of the same type. Jacks are used in groups to indirectly declare the input's and + output's + + \code + class MyGroup + { + private: + senf::ppi::module::PassiveQueue queue; + senf::ppi::module::RateAnalyzer analyzer; + + public: + senf::ppi::connector::ActiveInputJack input; + senf::ppi::connector::ActiveOutputJack output; + + MyGroup() + : queue (), analyzer (), input (queue.input), output (analyzer.output) + { + senf::ppi::connect(queue, analyzer); + } + }; + \endcode + + The jacks are initialized by passing an arbitrary compatible connector to the jack + constructor. A connector is compatible, if + \li It has the same input/output active/passive specification + \li Either the Jack or the Connector are generic (senf::Packet) or Jack and Connector have + the same packet type + + Jacks can be used wherever connectors may be used. Jacks may be defined anywhere, not only + in modules. It is however important to ensure that the lifetime of the jack does not exceed + the lifetime of the referenced connector. + + \see + senf::ppi::module::Module \n + senf::ppi::connect() \n + \ref ppi_connectors + */ + + /** \brief Incompatible connectors connected + + This exception is thrown, when two incompatible connectors are connected. This happens if + both connectors of a senf::ppi::connect() statement declare a packet type (the connector + template argument) but they don't declare the same packet type. - We therefore have 4 connector types: senf::ppi::ActiveInput, senf::ppi::ActiveOutput, - senf::ppi::PassiveInput and senf::ppi::PassiveOutput. + You need to ensure, that both connectors use the same packet type. + + \see senf::ppi::connect() */ + struct IncompatibleConnectorsException : public senf::Exception + { IncompatibleConnectorsException() : senf::Exception("Incompatible connectors") {} }; /** \brief Connector base-class @@ -66,24 +159,42 @@ namespace connector { to the containing module) */ class Connector - : boost::noncopyable + : ModuleManager::Initializable, boost::noncopyable { + SENF_LOG_CLASS_AREA(); + SENF_LOG_DEFAULT_LEVEL(senf::log::NOTICE); public: Connector & peer() const; ///< Get peer connected to this connector module::Module & module() const; ///< Get this connectors containing module + bool connected() const; ///< \c true, if connector connected, \c false otherwise + + void disconnect(); ///< Disconnect connector from peer + + enum TraceState { NO_TRACING, TRACE_IDS, TRACE_CONTENTS }; + + static void tracing(TraceState state); + static TraceState tracing(); + protected: Connector(); virtual ~Connector(); void connect(Connector & target); + void trace(Packet const & p, char const * label); + void throttleTrace(char const * label, char const * type); + private: + virtual std::type_info const & packetTypeID(); + void setModule(module::Module & module); Connector * peer_; module::Module * module_; + static TraceState traceState_; + friend class module::Module; }; @@ -94,16 +205,16 @@ namespace connector { of connector (output or input) the respective throttling is called forward or backward throttling. - Passive connectors always handle two throttling states: - - \li The \e native throttling state is set manually by the module. It is the throttling state + Passive connectors always handle two throttling states: + + - The \e native throttling state is set manually by the module. It is the throttling state originating in the current module - \li The \e forwarded throttling state is the state as it is received by throttling + - The \e forwarded throttling state is the state as it is received by throttling notifications The accumulative throttling state is generated by combining all sub-states. */ - class PassiveConnector + class PassiveConnector : public virtual Connector { public: @@ -116,17 +227,17 @@ namespace connector { is a pointer-to-member to a member of the class which holds this input. In the second case, the pointer will automatically be bound to the containing instance. - + \param[in] handler Handler to call, whenever an I/O operation is to be performed. */ - + bool throttled() const; ///< Get accumulative throttling state bool nativeThrottled() const; ///< Get native throttling state void throttle(); ///< Set native throttling void unthrottle(); ///< Revoke native throttling - + ActiveConnector & peer() const; protected: @@ -135,11 +246,13 @@ namespace connector { void emit(); private: - // Called by the routing to change the remote throttling state + virtual void v_init(); + + // Called by the routing to change the throttling state from forwarding routes void notifyThrottle(); ///< Forward a throttle notification to this connector void notifyUnthrottle(); ///< Forward an unthrottle notification to this connector - // Internal members to emit throttling notifications + // Internal members to emit throttling notifications to the connected peer void emitThrottle(); void emitUnthrottle(); @@ -149,7 +262,7 @@ namespace connector { // called by ForwardingRoute to register a new route void registerRoute(ForwardingRoute & route); - typedef detail::Callback<>::type Callback; + typedef ppi::detail::Callback<>::type Callback; Callback callback_; bool remoteThrottled_; @@ -171,10 +284,10 @@ namespace connector { notifications. These notifications should then either be processed by the module or be forwarded to other connectors. */ - class ActiveConnector + class ActiveConnector : public virtual Connector { - typedef detail::Callback<>::type Callback; + typedef ppi::detail::Callback<>::type Callback; public: template void onThrottle(Handler handler); ///< Register throttle notification handler @@ -210,6 +323,8 @@ namespace connector { ActiveConnector(); private: + virtual void v_init(); + // called by the peer() to forward throttling notifications void notifyThrottle(); void notifyUnthrottle(); @@ -223,6 +338,8 @@ namespace connector { typedef std::vector NotifyRoutes; NotifyRoutes notifyRoutes_; + bool throttled_; + friend class senf::ppi::ForwardingRoute; friend class PassiveConnector; }; @@ -247,7 +364,7 @@ namespace connector { insertion/deletion but I believe that list is just to expensive since every packet will be added to the queue before it can be processed. */ - class InputConnector + class InputConnector : public virtual Connector { typedef std::deque Queue; @@ -266,7 +383,7 @@ namespace connector { logic error in the module implementation and an exception is raised. */ - Packet read(); ///< Alias for \ref operator()() + Packet read(); ///< Alias for operator()() OutputConnector & peer() const; @@ -279,10 +396,10 @@ namespace connector { protected: InputConnector(); - + private: - void enqueue(Packet p); - + void enqueue(Packet const & p); + virtual void v_requestEvent(); virtual void v_enqueueEvent(); virtual void v_dequeueEvent(); @@ -291,44 +408,42 @@ namespace connector { friend class OutputConnector; }; - + /** \brief Output connector base-class - + An output connector sends out packets. It may be either an ActiveConnector or a PassiveConnector. An output connector does \e not have an built-in queueing, it relies on the queueing of the connected input. */ - class OutputConnector + class OutputConnector : public virtual Connector { public: - void operator()(Packet p); ///< Send out a packet + void operator()(Packet const & p); ///< Send out a packet - void write(Packet p); ///< Alias for \ref operator()() + void write(Packet const & p); ///< Alias for operator()(Packet p) InputConnector & peer() const; protected: OutputConnector(); }; - + /** \brief Combination of PassiveConnector and InputConnector - The PassiveInput automatically controls the connectors throttling state using a queueing - discipline. The standard queueing discipline is ThresholdQueueing, which throttles the - connection whenever the queue length reaches the high threshold and unthrottles the + The GenericPassiveInput automatically controls the connectors throttling state using a + queueing discipline. The standard queueing discipline is ThresholdQueueing, which throttles + the connection whenever the queue length reaches the high threshold and unthrottles the connection when the queue reaches the low threshold. The default queueing discipline is ThresholdQueueing(1,0) which will throttle the input whenever the queue is non-empty. */ - class PassiveInput + class GenericPassiveInput : public PassiveConnector, public InputConnector, - public SafeBool + public safe_bool { public: - PassiveInput(); - - ActiveOutput & peer() const; + GenericActiveOutput & peer() const; bool boolean_test() const; ///< \c true, if ! empty() @@ -336,9 +451,12 @@ namespace connector { void qdisc(QDisc const & disc); ///< Change the queueing discipline /**< The queueing discipline is a class which provides the QueueingDiscipline interface. - + \param[in] disc New queueing discipline */ + protected: + GenericPassiveInput(); + private: void v_enqueueEvent(); void v_dequeueEvent(); @@ -349,51 +467,183 @@ namespace connector { /** \brief Combination of PassiveConnector and OutputConnector */ - class PassiveOutput + class GenericPassiveOutput : public PassiveConnector, public OutputConnector, - public SafeBool + public safe_bool { public: - ActiveInput & peer() const; + GenericActiveInput & peer() const; bool boolean_test() const; ///< Always \c true - void connect(ActiveInput & target); ///< Internal: Use senf::ppi::connect() instead + void connect(GenericActiveInput & target); ///< Internal: Use senf::ppi::connect() instead + + friend class GenericActiveInput; + + protected: + GenericPassiveOutput(); - friend class ActiveInput; }; /** \brief Combination of ActiveConnector and InputConnector */ - class ActiveInput + class GenericActiveInput : public ActiveConnector, public InputConnector, - public SafeBool + public safe_bool { public: - PassiveOutput & peer() const; + GenericPassiveOutput & peer() const; bool boolean_test() const; ///< \c true, if ! empty() or ! throttled() void request(); ///< request more packets without dequeuing any packet + protected: + GenericActiveInput(); + private: void v_requestEvent(); }; /** \brief Combination of ActiveConnector and OutputConnector */ - class ActiveOutput + class GenericActiveOutput : public ActiveConnector, public OutputConnector, - public SafeBool + public safe_bool { public: - PassiveInput & peer() const; + GenericPassiveInput & peer() const; bool boolean_test() const; ///< \c true if peer() is ! throttled() - void connect(PassiveInput & target); ///< Internal: Use senf::ppi::connect() instead + void connect(GenericPassiveInput & target); ///< Internal: Use senf::ppi::connect() instead + + protected: + GenericActiveOutput(); + + }; + +#ifndef DOXYGEN + +# define TypedConnector_Input read +# define TypedConnector_Output write +# define TypedConnector(pType, dir) \ + template \ + class pType ## dir \ + : public Generic ## pType ## dir, \ + private detail::Typed ## dir ## Mixin, PacketType> \ + { \ + typedef detail::Typed ## dir ## Mixin, PacketType> mixin; \ + public: \ + using mixin::operator(); \ + using mixin::TypedConnector_ ## dir ; \ + private: \ + virtual std::type_info const & packetTypeID() \ + { return typeid(typename PacketType::type); } \ + friend class detail::Typed ## dir ## Mixin, PacketType>; \ + }; \ + template <> \ + class pType ## dir : public Generic ## pType ## dir \ + {} + + TypedConnector( Passive, Input ); + TypedConnector( Passive, Output ); + TypedConnector( Active, Input ); + TypedConnector( Active, Output ); + +# undef TypedConnector +# undef TypedConnector_Input +# undef TypedConnector_Output + +#else + + /** \brief Connector actively reading packets + + \tparam PacketType Type of packet to read. Defaults to senf::Packet + + The %ActiveInput %connector template reads data actively from a connected %module. This + class is completely implemented via it's base-class, GenericActiveInput, the only + difference is that read packets are returned as \a PacketType instead of generic + senf::Packet references. + + \see GenericActiveInput \n + senf::ppi::connector + */ + template + class ActiveInput : public GenericActiveInput + { + public: + PacketType operator()(); ///< Read packet + /**< \throws std::bad_cast if the %connector receives a + Packet which is not of type \a PacketType. + \returns newly read packet reference. */ + PacketType read(); ///< Alias for operator() + }; + + /** \brief Connector passively receiving packets + + \tparam PacketType Type of packet to read. Defaults to senf::Packet + + The %PassiveInput %connector template receives packets sent to it from a connected + %module. This class is completely implemented via it's base-class, GenericPassiveInput, + the only difference is that read packets are returned as \a PacketType instead of generic + senf::Packet references. + + \see GenericPassiveInput \n + senf::ppi::connector + */ + template + class PassiveInput : public GenericPassiveInput + { + public: + PacketType operator()(); ///< Read packet + /**< \throws std::bad_cast if the %connector receives a + Packet which is not of type \a PacketType. + \returns newly read packet reference. */ + PacketType read(); ///< Alias for operator() + }; + + /** \brief Connector actively sending packets + + \tparam PacketType Type of packet to send. Defaults to senf::Packet + + The %ActiveOutput %connector template sends data actively to a connected %module. This + class is completely implemented via it's base-class, GenericActiveOutput, the only + difference is that it only sends packets of type \a PacketType. + + \see GenericActiveOutput \n + senf::ppi::connector + */ + template + class ActiveOutput : public GenericActiveOutput + { + public: + operator()(PacketType packet); ///< Send out a packet + void write(PacketType packet); ///< Alias for operator() }; + /** \brief Connector passively providing packets + + \tparam PacketType Type of packet to send. Defaults to senf::Packet + + The %PassiveOutput %connector template provides data passively to a connected %module + whenever signaled. This class is completely implemented via it's base-class, + GenericPassiveOutput, the only difference is that it only sends packets of type + \a PacketType. + + \see GenericPassiveOutput \n + senf::ppi::connector + */ + template + class PassiveOutput : public GenericPassiveOutput + { + public: + operator()(PacketType packet); ///< Send out a packet + void write(PacketType packet); ///< Alias for operator() + }; + +#endif + }}} ///////////////////////////////hh.e////////////////////////////////////////