From: g0dil Date: Thu, 3 May 2007 09:32:50 +0000 (+0000) Subject: PPI Module and Connector documentation X-Git-Url: http://g0dil.de/git?a=commitdiff_plain;h=a4887e674af3fce4180cf7e14bedace928962025;p=senf.git PPI Module and Connector documentation git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@237 270642c3-0616-0410-b53a-bc976706d245 --- diff --git a/PPI/Connectors.hh b/PPI/Connectors.hh new file mode 100644 index 0000000..907d5a9 --- /dev/null +++ b/PPI/Connectors.hh @@ -0,0 +1,316 @@ +// Copyright (C) 2007 +// Fraunhofer Institut fuer offene Kommunikationssysteme (FOKUS) +// Kompetenzzentrum fuer Satelitenkommunikation (SatCom) +// Stefan Bund +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the +// Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** \file + \brief Conenctors public header */ + +/** \defgroup connectors Connector classes + + A connector has two independent properties + \li it may be \e active or \e passive + \li it may be an \e input or an \e output + + \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. + + All passive connectors call some onRequest callback whenever I/O needs to be performed. All + input modules possess a packet queue. + + We therefore have 4 connector types: senf::ppi::ActiveInput, senf::ppi::ActiveOutput, + senf::ppi::PassiveInput and senf::ppi::PassiveOutput. + */ + +#ifndef HH_Conenctors_ +#define HH_Conenctors_ 1 + +// Custom includes +#include + +//#include "Conenctors.mpp" +///////////////////////////////hh.p//////////////////////////////////////// + +namespace senf { +namespace ppi { + + /** \brief Connector baseclass + + This connector provides access to the generic connector facilities. This includes the + connection management (access to the connected peer) and the containment management (access + to the containing module) + */ + class Connector + : boost::noncopyable + { + public: + Connector & peer(); ///< Get peer connected to this connector + Module & module(); ///< Get this connectors containing module + + protected: + // here to protect + Connector(); + ~Connector(); + }; + + /** \brief Passive connector baseclass + + A passive connector is a connector which is activated externally whenever an I/O request + occurs. Passive connectors are the origin of throttling notifications. Depending on the type + 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 + originating in the current module + \li 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. + + \ingroup connectors + */ + class PassiveConnector + : public virtual Connector + { + public: + template + void onRequest(Handler handler);///< Register I/O event handler + /**< The registered handler will be called, whenever packets + arrive or should be generated by the module depending + on the connector type (input or output). The \a handler + argument is either an arbitrary callable object or it + 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(); ///< Get accumulative throttling state + bool nativeThrottled(); ///< Get native throttling state + + void throttle(); ///< Set native throttling + void unthrottle(); ///< Revoke native throttling + + void notifyThrottle(); ///< Forward a throttling notification to this connector + void notifyUnthrottle(); ///< Forward an unthrottling notification to this connector + + ActiveConnector & peer(); + + protected: + // here to protect + PassiveConnector(); + ~PassiveConnector(); + }; + + /** \brief Active connector baseclass + + An active connector is a connector which emits I/O requests. Active connectors receive + throttling notifications. Depending on the type of connector (input or output) the + respective throttling is called forward or backward throttling. + + Active connectors do not handle any throttling state, they just receive the + notifications. These notifications should then either be processed by the module or be + forwarded to other connectors. + + \ingroup connectors + */ + class ActiveConnector + : public virtual Connector + { + public: + template + void onThrottle(Handler); ///< Register throttle notification handler + /**< The handler register here will be called, whenever a + throttle notification comes in. The \a handler argument + is either an arbitrary callable object or it 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 on throttle + notifications. */ + + template + void onUnthrottle(Handler); ///< Register unthrottle notification handler + /**< The handler register here will be called, whenever an + unthrottle notification comes in. The \a handler + argument is either an arbitrary callable object or it + 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 on unthrottle + notifications. */ + + PassiveConnector & peer(); + + protected: + // here to protect + PassiveConnector(); + ~PassiveConnector(); + }; + + /** \brief Input connector baseclass + + An input connector receives packets. It may be either an ActiveConnector or a + PassiveConnector. An input connector contains a packet queue. This queue enables processing + packets in batches or generating multiple output packets from a single input packet. The + queues have the potential to greatly simplify the module implementations. + + \ingroup connectors + */ + class InputConnector + : public virtual Connector + { + public: + typedef unspecified queue_iterator; ///< Iterator type of the embedded queue + typedef unspecified size_type; ///< Unsigned type representing the number of queue elements + + Packet::ptr operator(); ///< Get a packet + /**< This member is the primary method to access received + data. On passive connectors, this operator will just + dequeue a packet from the packet queue. If the + connector is active, the connector will request new + packets from the connected module. If the packet + request cannot be fulfilled, this is considered to be a + logic error in the module implementation and an + exception is raised. */ + operator unspecified_boolean_type (); ///< Check packet availability + /**< Using any input connector in a boolean context will + check, wether an input request can be fulfilled. This + is always possible if the queue is non-empty. If the + input is active, it also returns when the connected + passive output is not throttled so new packets can be + requested. + + Calling the operator() member is an error if this test + returns \c false + + \returns \c true if operator() can be called, \c false + otherwise */ + operator ! (); ///< Check packet availability + /**< Inverse of the boolean conversion operator + \returns \c false if operator() can be called, \c true + otherwise */ + + OutputConnector & peer(); + + queue_iterator begin(); ///< Access queue begin (head) + queue_iterator end(); ///< Access queue past-the-end (tail) + Packet::ptr head(); ///< Return head element from the queue + + size_type queueSize(); ///< Return number of elements in the queue + bool empty(); ///< Return queueSize() == 0 + + protected: + // here to protect + PassiveConnector(); + ~PassiveConnector(); + }; + + /** \brief Output connector baseclass + + 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. + + \ingroup connectors + */ + class OutputConnector + : public virtual Connector + { + public: + void operator(Packet::ptr); ///< Send out a packet + + InputConnector & peer(); + + protected: + // here to protect + PassiveConnector(); + ~PassiveConnector(); + }; + + /** \brief Combination of PassiveConnector and InputConnector + + \ingroup connectors + */ + class PassiveInput + : public PassiveConnector, public InputConnector + { + public: + ActiveOutput & peer(); + + template + QDisc const & qdisc(QDisc const & disc); + }; + + /** \brief Combination of PassiveConnector and OutputConnector + + \ingroup connectors + */ + class PassiveOutput + : public PassiveConnector, public OutputConnector + { + public: + ActiveInput & peer(); + }; + + /** \brief Combination of ActiveConnector and InputConnector + + \ingroup connectors + */ + class ActiveInput + : public ActiveConnector, public InputConnector + { + public: + void request(); ///< request more packets without dequeing any packet + + PassiveOutput & peer(); + }; + + /** \brief Combination of ActiveConnector and OutputConnector + + \ingroup connectors + */ + class ActiveOutput + : public ActiveConnector, public OutputConnector + { + public: + ActiveInput & peer(); + }; + +}} + +///////////////////////////////hh.e//////////////////////////////////////// +//#include "Conenctors.cci" +//#include "Conenctors.ct" +//#include "Conenctors.cti" +#endif + + +// Local Variables: +// mode: c++ +// fill-column: 100 +// c-file-style: "senf" +// indent-tabs-mode: nil +// ispell-local-dictionary: "american" +// End: diff --git a/PPI/Doxyfile b/PPI/Doxyfile index ae7a8ae..d68c399 100644 --- a/PPI/Doxyfile +++ b/PPI/Doxyfile @@ -2,3 +2,5 @@ PROJECT_NAME = libPPI GENERATE_TAGFILE = doc/ppi.tag + +TAGFILES = "$(TOPDIR)/Packets/doc/Packets.tag" "$(TOPDIR)/Socket/doc/Socket.tag" diff --git a/PPI/Mainpage.dox b/PPI/Mainpage.dox index bb234bb..6438694 100644 --- a/PPI/Mainpage.dox +++ b/PPI/Mainpage.dox @@ -28,12 +28,12 @@ The PPI concept is built around some key concepts - \li The PPI is based on processing \e packets. It does not handle stream oriented channels. - \li The PPI is built around reusable \e modules. Each module is completely independent. - \li Each module has an arbitrary number of \e connectors, inputs and outputs. - \li The modules are connected to each other using flexible \e connections. - \li Data flow throughout the network is governed via flexible automatic or manual \e throttling. - \li Modules may register additional external \e events (file descriptor events or timers). + \li The PPI is based on processing \ref packets. It does not handle stream oriented channels. + \li The PPI is built around reusable \ref modules. Each module is completely independent. + \li Each module has an arbitrary number of \ref connectors, inputs and outputs. + \li The modules are connected to each other using flexible \ref connections. + \li Data flow throughout the network is governed via flexible automatic or manual \ref throttling. + \li Modules may register additional external \ref events (file descriptor events or timers). The PPI thereby builds on the facilities provided by the other components of the SENF framework. @@ -79,7 +79,8 @@ route(payload, output); route(stuffing, output); - registerEvent(&RateStuffer::tick, IntervalTimer(1000u, packetsPerSecond)); + registerEvent(&RateStuffer::tick, + senf::ppi::IntervalTimer(1000u, packetsPerSecond)); } private: @@ -134,7 +135,7 @@ This module just produces a copy of a given packet whenever output is requested. - \subsection connectors Connectors + \section connectors Connectors Inputs and Outputs can be active and passive. An \e active I/O is activated by the module to send data or to poll for available packets. A \e passive I/O is signaled by @@ -145,40 +146,40 @@ iteration. However, reading will only succeed, as long as packets are available from the connection. - A module might want to queue incoming packets within a passive input or outgoing packets within - an active output. This is possible by either not reading any packet even though a new packet has - been scheduled on the input or by writing to the output while it is still throttled. To - facilitate this use, the connectors provide accessors to access the attached connection and it's - queue. This allows to analyze all packets available in the queue and act accordingly. + Since a module is free to generate more than a single packet on incoming packet requests, all + input connectors incorporate a packet queue. This queue is exposed to the module and allows the + module to process packets in batches. \section connections Connections To make use of the modules, they have to be instantiated and connections have to be created between the I/O connectors. It is possible to connect any pair of input/output connectors as - long as at least one of them is active - - Every connection contains an internal packet queue. Under normal operating conditions (without - throttling) the queue will mostly be empty since packets will be processed directly. If a - connection is throttled, it can still receive new packets on it's input which will then be - queued. This is necessary even though the throttling will be propagated backwards (so no new - packets should arrive) since a module may produce more then one result packet from a single - incoming packet. + long as one of them is active and the other is passive. + + It is possible to connect two active connectors with each other using a special adaptor + module. This Module has a passive input and a passive output. It will queue any incoming packets + and automatically handle throttling events (see below). This adaptor is automatically added by + the connect method if needed. - To complete our simplified example: Lets say we have a UdpInput module and a - UdpOutput module. We can then use our RateStuffer module to build an + To complete our simplified example: Lets say we have an ActiveSocketInput and a + PassiveUdpOutput module. We can then use our RateStuffer module to build an application which will create a fixed-rate UDP stream: \code RateStuffer rateStuffer (10); - CopyPacketGenerator generator (some_packet_ptr); + + senf::Packet::ptr stuffingPacket = senf::Packet::create<...>(...); + CopyPacketGenerator generator (stuffingPacket); + senf::UDPv4ClientSocketHandle inputSocket (1111); - senf::ppi::SocketInput udpInput (inputSocket); + senf::ppi::ActiveSocketInput udpInput (inputSocket); + senf::UDPv4ClientSocketHandle outputSocket ("2.3.4.5:2222"); - senf::ppi::SocketOutput udpOutput (outputSocket); + senf::ppi::PassiveSocketOutput udpOutput (outputSocket); - senf::ppi::connect(udpInput.output, rateStuffer.payload) - .bufferHighThresh(10) - .bufferLowThresh(5); + senf::ppi::connect(udpInput.output, rateStuffer.payload, + dynamicModule() + -> qdisc(ThresholdQueueing(10,5)) ); senf::ppi::connect(generator.output, rateStuffer.stuffing); senf::ppi::connect(rateStuffer.output, udpOutput.input); @@ -186,46 +187,39 @@ \endcode First all necessary modules are created. Then the connections between these modules are set - up. The buffering of the udpInput <-> rateStuffer connection is changed so the queue will begin - to throttle only if more than 10 packets are in the queue. The connection will be unthrottled as + up. The buffering on the udpInput <-> rateStuffer adaptor is changed so the queue will begin to + throttle only if more than 10 packets are in the queue. The connection will be unthrottled as soon as there are no more than 5 packets left in the queue. This application will read udp-packts coming in on port 1111 and will forward them to port 2222 on host 2.3.4.5 with a - fixed rate of 10 packets / second. + fixed rate of 10 packets / second. \section throttling Throttling - If a connection cannot pass packets in it's current state, the connection is \e throttled. In - simple cases, throttling is handled automatically by - \li the connection if the queue is exceeds the buffering threshold - \li I/O modules whenever the external source or sink of the module is not ready - - Throttling is handled separately in each direction: - \li Forward throttling will throttle in the direction of the data flow. Example: No new packets - are available from an input. This will activate forward throttling until new data arrives - \li Backward throttling will throttle in the direction to the data source. Example: an output - device (e.g. serial interface) has no more room for data. This event will activate backward - throttling so no new data will arrive until the device can send data again - - The throttling state is managed within the Connection. Automatic throttling utilizes the routing - information (provided in the modules constructor) to forward throttling events across - modules. However, automatic throttling can be disabled for each connector. Modules may also - register event handlers whenever a throttling event occurs. - - Whenever a connection is throttled (in the corresponding direction), passive connectors will \e - not be called by the framework. This may lead to packets being accumulated in the connection - queue. These packets will be sent as soon as the connection is unthrottled. The unthrottle event - will hoewever only be forwarded when the queue is empty (or has reached it's lower buffering - threshold). - - \code - passiveConnector.autoForwardThrottling(false); - passiveConnector.autoBackwardThrotttling(true); - passiveConnector.onForwardThrottle(...); - passiveConnector.onBackwardUnthrottle(...); - \endcode - - Throttling is not enforced: especially a throttled output may still be called, the - excessive packets will be queued in the connection queue. + If a passive connector cannot handle incoming requests, this connector may be \e + throttled. Throttling a request will forward a throttle notification to the module connected + to that connector. The module then must handle this throttle notification. If automatic + throttling is enabled for the module (which is the default), the notification will automatically + be forwarded to all dependent connectors as taken from the flow information. For there it will + be forwarded to further modules and so on. + + A throttle notification reaching an I/O module will normally disable the input/output by + disabling any external I/O events registered by the module. When the passive connector which + originated the notification becomes active again, it creates an unthrottle notification which + will be forwarded in the same way. This notification will re-enable any registered I/O events. + + The above discussion shows, that throttle events are always generated on passive connectors and + received on active connectors. To differentiate further, the throttling originating from a + passive input is called backward throttling since it is forwarded in the direction \e + opposite to the data flow. Backward throttling notifications are sent towards the input + modules. On the other hand, the throttling originating from a passive output is called + forward throttling since it is forwarded along the \e same direction the data + is. Forward throttling notifications are therefore sent towards the output modules. + + Since throttling a passive input may not disable all further packet delivery immediately, any + passive input contains an input queue. In it's default configuration, the queue will send out + throttle notifications when it becomes non-empty and unthrottle notifications when it becomes + empty again. This automatic behavior may however be disabled. This allows a module to collect + incoming packets in it's input queue before processing a bunch of them in one go. \section events Events @@ -233,6 +227,81 @@ the PPI framework. Possible event sources are \li time based events \li file descriptors. + + Here some example code implementing the ActiveSocketInput Module: + + \code + class ActiveSocketInput + : public senf::ppi::Module + { + static PacketParser defaultParser_; + + public: + ActiveOutput output; + + typedef senf::ClientSocketHandle< + senf::MakeSocketPolicy< senf::ReadablePolicy, + senf::DatagramFramingPolicy > > Socket; + + // I hestitate taking parser by const & since a const & can be bound to + // a temporary even though a const & is all we need. The real implementation + // will probably make this a template arg. This simplifies the memory management + // from the users pov. + ActiveSocketInput(Socket socket, DataParser & parser = SocketInput::defaultParser_) + : socket_ (socket), + parser_ (parser) + event_ (registerEvent( &ActiveSocketInput::data, + senf::ppi::IOSignaler(socket, senf::ppi::IOSignaler::Read) )) + { + route(event_, output); + } + + private: + Socket socket_; + DataParser const & parser_; + senf::ppi:IOSignaler::EventBinding event_; + + void data() + { + std::string data; + socket_.read(data); + output(parser_(data)); + } + }; + \endcode + + First we declare our own socket handle type which allows us to read packets. The constructor + then takes two arguments: A compatible socket and a parser object. This parser object gets + passed the packet data as read from the socket (an \c std::string) and returns a + senf::Packet::ptr. The \c PacketParser is a simple parser which interprets the data as specified + by the template argument. + + We register an IOSignaler event. This event will be signaled whenever the socket is + readable. This event is routet to the output. This routing automates throttling for the socket: + Whenever the output receives a throttle notifications, the event will be temporarily disabled. + + Processing arriving packets happens in the \c data() member: This member simple reads a packet + from the socket. It passes this packet to the \c parser_ and sends the generated packet out. + + \implementation Generation of throttle notifications: Backward throttling notifications are + automatically generated (if this is not disabled) whenever the input queue is non-empty \e + after the event handler has finished processing. Forward throttling notifications are not + generated automatically within the connector. However, the Passive-Passive adaptor will + generate Forward-throttling notifications whenever the input queue is empty. + + \note Open Issues + \li We need to clearly differentiate between auto-throttling and auto-throttle-forwarding, + between a connectors own throttling state and the forwarded state. + \li Exception handling + \li ActiveInputs also need a queue: This is necessary to allow a PassiveOutput to create more + than a single packet from a single 'onRequest' event. This greatly simplifies writing + modules which produce multiple output packets for a single input packet. + \li We need to clear up the throttled() member semantics: If the connector is throttled, does + it return so if there are still packets in the queue? Probably yes. However, it does not + forward throttling notifications until instructed by the qdisc. Throttling notifications are + also bound to onThrottle/onUnThrottle callbacks. The semantics are then clear: An active + connector emitting onThrottle cannot process any further request (for inputs, no data will + be available, for outputs the data will be queued in the peer input) */ diff --git a/PPI/Module.hh b/PPI/Module.hh new file mode 100644 index 0000000..aefe9a1 --- /dev/null +++ b/PPI/Module.hh @@ -0,0 +1,142 @@ +// Copyright (C) 2007 +// Fraunhofer Institut fuer offene Kommunikationssysteme (FOKUS) +// Kompetenzzentrum fuer Satelitenkommunikation (SatCom) +// Stefan Bund +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the +// Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** \file + \brief Module public header */ + +#ifndef HH_Module_ +#define HH_Module_ 1 + +// Custom includes +#include + +//#include "Module.mpp" +///////////////////////////////hh.p//////////////////////////////////////// + +namespace senf { +namespace ppi { + + /** \brief Module baseclass + + senf::ppi::Module is the baseclass of all PPI modules. It provides the module implementation + with interfaces to several PPI facilities: + + \li Connector management + \li Flow management + \li Event handling + + To provide internal bookkeeping, most access to the PPI infrastructure is managed through + this base class. + + Instances of this class may be allocated either statically or dynamically. Dynamic instances + are automatically managed using the dynamicModule adaptor. + */ + class Module + : boost::noncopyable + { + protected: + Module(); + ~Module(); + + template + Route route(Source const & source, Target const & target); ///< Define flow information + /**< Using the route() and noroute() members, the + information flow within the module is defined. Routing + may be specified either between inputs, outputs and + events. The routing information is used to perform + automatic throttling. The throttling behavior may + however be controlled manually. + + Even if no automatic throttling is desired it is + vital to define the flow information for all inputs and + outputs. Without flow information important + internal state of the module cannot be + initialized. This includes, explicitly defining + terminal inputs and outputs using noroute. Event + routing however is optional. + + The return value may be used to alter routing + parameters like throttling parameters. + + \param[in] source Data source, object which controlls + incoming data + \param[in] target Data target, object which controlls + outgoing data + \returns Route instance describing this route */ + + template + void noroute(Connector const & connector); ///It is mandatory to define routing + information for terminal connectors. + + See the route() documentation for more on routing + + \param[in] connector Terminal connector to declare */ + + template + typename Descriptor::EventBinding const registerEvent(Target target, + Descriptor const & descriptor); + ///< Register an external event + /**< The \a target argument may be either an arbitrary + callable object or it may be a member function pointer + pointing to a member function of the Module derived + classed. The handler may \e optionally take an Argument + of type typename Descriptor::Event const + &. This object allows to access detailed + information on the event delivered. + + The \a descriptor describes the event to signal. This + may be a timer event or some type of I/O event on a + file descriptor or socket. + + The return value may be used to modify the + binding. This allows to temporarily inhibit event + delivery or to remove the binding explicitly. Depending + on the type of event, other operations may be + possible. See the event descriptor documentation. + + \param[in] target The handler to call whenever the event + is signaled + \param[in] descriptor The type of event to register + \returns An event binding instance of the appropriate + type. */ + }; + +}} + +///////////////////////////////hh.e//////////////////////////////////////// +//#include "Module.cci" +//#include "Module.ct" +//#include "Module.cti" +#endif + + +// Local Variables: +// mode: c++ +// fill-column: 100 +// c-file-style: "senf" +// indent-tabs-mode: nil +// ispell-local-dictionary: "american" +// End: diff --git a/doclib/SConscript b/doclib/SConscript index fd5ba39..3a8e265 100644 --- a/doclib/SConscript +++ b/doclib/SConscript @@ -118,7 +118,11 @@ env.Alias('all_docs', env.Command('search.php', 'html-munge.xsl', [ writeTemplate, 'xsltproc --nonet --html --stringparam topdir .. -o - $SOURCE $TARGET 2>/dev/null' - + r'| sed -e "s/\[\[//g" > ${TARGET}.tmp', + + "| sed" + + r" -e 's/\[\[//g'" + + r" -e 's/\$$projectname/Overview/g'" + + r" -e 's/\$$title/Search results/g'" + + "> ${TARGET}.tmp", 'mv ${TARGET}.tmp ${TARGET}' ], TEMPLATE = Literal(HEADER + OVERVIEW_EXTRA_HEADER