PPI: optimized internal connector packet handling (queuing only if necessary, pass...
[senf.git] / senf / PPI / Connectors.hh
1 // $Id$
2 //
3 // Copyright (C) 2007
4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 //
6 // The contents of this file are subject to the Fraunhofer FOKUS Public License
7 // Version 1.0 (the "License"); you may not use this file except in compliance
8 // with the License. You may obtain a copy of the License at 
9 // http://senf.berlios.de/license.html
10 //
11 // The Fraunhofer FOKUS Public License Version 1.0 is based on, 
12 // but modifies the Mozilla Public License Version 1.1.
13 // See the full license text for the amendments.
14 //
15 // Software distributed under the License is distributed on an "AS IS" basis, 
16 // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 
17 // for the specific language governing rights and limitations under the License.
18 //
19 // The Original Code is Fraunhofer FOKUS code.
20 //
21 // The Initial Developer of the Original Code is Fraunhofer-Gesellschaft e.V. 
22 // (registered association), Hansastraße 27 c, 80686 Munich, Germany.
23 // All Rights Reserved.
24 //
25 // Contributor(s):
26 //   Stefan Bund <g0dil@berlios.de>
27
28 /** \file
29     \brief Connectors public header */
30
31 #ifndef HH_SENF_PPI_Connectors_
32 #define HH_SENF_PPI_Connectors_ 1
33
34 // Custom includes
35 #include <deque>
36 #include <boost/utility.hpp>
37 #include <boost/scoped_ptr.hpp>
38 #include <senf/Utils/safe_bool.hh>
39 #include <senf/Utils/Exception.hh>
40 #include <senf/Packets/Packets.hh>
41 #include "predecl.hh"
42 #include "detail/Callback.hh"
43 #include "Queueing.hh"
44 #include "ModuleManager.hh"
45
46 //#include "Connectors.mpp"
47 //-/////////////////////////////////////////////////////////////////////////////////////////////////
48
49 namespace senf {
50 namespace ppi {
51 namespace connector {
52
53     /** \namespace senf::ppi::connector
54         \brief Connector classes
55
56         A connector has three independent properties
57         \li it may be \e active or \e passive
58         \li it may be an \e input or an \e output
59         \li it has an (optional) packet type
60
61         \e Active connectors are activated from within the module, \e passive connectors are
62         signaled by the external framework. \e Input connectors receive packets, \e output
63         connectors send packets.
64
65         All passive connectors call some onRequest callback whenever I/O needs to be performed. All
66         input connectors possess a packet queue.
67
68         We therefore have 4 connector types each of which is parameterized by the type of packet
69         traversing the connector:
70         \li senf::ppi::connector::ActiveInput
71         \li senf::ppi::connector::ActiveOutput
72         \li senf::ppi::connector::PassiveInput
73         \li senf::ppi::connector::PassiveOutput.
74
75         Connectors are declared as module data members and are then externally connected to other
76         modules.
77
78         The connectors each take an optional template argument. If this argument is specified, it
79         must be the type of packet expected or sent on this connector. If it is not specified,
80         packets will be passed using the generic Packet handle.
81
82         \code
83         class IpFilter : public senf::ppi::module::Module
84         {
85             SENF_PPI_MODULE(SomeModule);
86
87         public:
88             senf::ppi::connector::ActiveInput<senf::EthernetPacket> input;
89             senf::ppi::connector::PassiveOutput<senf::IpPacket> output;
90
91             IpFilter() {
92                 route(input, output);
93                 input.onRequest(&IpFilter::onRequest);
94             }
95
96         private:
97             void onRequest() {
98                 // 'input()' will return a senf::EthernetPacket packet handle
99                 try { output( input().find<senf::IpPacket>() ); }
100                 catch (senf::InvalidPacketChainException & ex) { ; }
101             }
102         };
103         \endcode
104
105
106         \section ppi_jacks Jacks
107
108         A Jack is a packet type aware and possibly packet type converting reference to an arbitrary
109         connector of the same type. Jacks are used in groups to indirectly declare the input's and
110         output's
111
112         \code
113         class MyGroup
114         {
115         private:
116             senf::ppi::module::PassiveQueue queue;
117             senf::ppi::module::RateAnalyzer analyzer;
118
119         public:
120             senf::ppi::connector::ActiveInputJack<senf::EthernetPacket> input;
121             senf::ppi::connector::ActiveOutputJack<senf::EthernetPacket> output;
122
123             MyGroup()
124                 : queue (), analyzer (), input (queue.input), output (analyzer.output)
125             {
126                 senf::ppi::connect(queue, analyzer);
127             }
128         };
129         \endcode
130
131         The jacks are initialized by passing an arbitrary compatible connector to the jack
132         constructor. A connector is compatible, if
133         \li It has the same input/output active/passive specification
134         \li Either the Jack or the Connector are generic (senf::Packet) or Jack and Connector have
135             the same packet type
136
137         Jacks can be used wherever connectors may be used. Jacks may be defined anywhere, not only
138         in modules. It is however important to ensure that the lifetime of the jack does not exceed
139         the lifetime of the referenced connector.
140
141         \see
142             senf::ppi::module::Module \n
143             senf::ppi::connect() \n
144             \ref ppi_connectors
145      */
146
147     /** \brief Incompatible connectors connected
148
149         This exception is thrown, when two incompatible connectors are connected. This happens if
150         both connectors of a senf::ppi::connect() statement declare a packet type (the connector
151         template argument) but they don't declare the same packet type.
152
153         You need to ensure, that both connectors use the same packet type.
154
155         \see senf::ppi::connect()
156      */
157     struct IncompatibleConnectorsException : public senf::Exception
158     { IncompatibleConnectorsException() : senf::Exception("Incompatible connectors") {} };
159
160     /** \brief Connector base-class
161
162         This connector provides access to the generic connector facilities. This includes the
163         connection management (access to the connected peer) and the containment management (access
164         to the containing module)
165      */
166     class Connector
167         : ModuleManager::Initializable, boost::noncopyable
168     {
169         SENF_LOG_CLASS_AREA();
170         SENF_LOG_DEFAULT_LEVEL(senf::log::NOTICE);
171     public:
172         Connector & peer() const;       ///< Get peer connected to this connector
173         module::Module & module() const; ///< Get this connectors containing module
174
175         bool connected() const;         ///< \c true, if connector connected, \c false otherwise
176
177         void disconnect();              ///< Disconnect connector from peer
178
179         enum TraceState { NO_TRACING, TRACE_IDS, TRACE_CONTENTS };
180
181         static void tracing(TraceState state);
182         static TraceState tracing();
183
184     protected:
185         Connector();
186         virtual ~Connector();
187
188         void connect(Connector & target);
189
190         void trace(Packet const & p, char const * label);
191         void throttleTrace(char const * label, char const * type);
192
193         void unregisterConnector();
194
195         virtual void v_disconnected();
196         virtual void v_connected();
197
198     private:
199         virtual std::type_info const & packetTypeID();
200
201         void setModule(module::Module & module);
202
203         Connector * peer_;
204         module::Module * module_;
205
206         static TraceState traceState_;
207
208         friend class module::Module;
209     };
210
211     /** \brief Passive connector base-class
212
213         A passive connector is a connector which is activated externally whenever an I/O request
214         occurs. Passive connectors are the origin of throttling notifications. Depending on the type
215         of connector (output or input) the respective throttling is called forward or backward
216         throttling.
217
218         Passive connectors always handle two throttling states:
219
220         - The \e native throttling state is set manually by the module. It is the throttling state
221             originating in the current module
222         - The \e forwarded throttling state is the state as it is received by throttling
223             notifications
224
225         The accumulative throttling state is generated by combining all sub-states.
226      */
227     class PassiveConnector
228         : public virtual Connector
229     {
230     public:
231         ~PassiveConnector();
232
233         template <class Handler>
234         void onRequest(Handler handler);///< Register I/O event handler
235                                         /**< The registered handler will be called, whenever packets
236                                              arrive or should be generated by the module depending
237                                              on the connector type (input or output). The \a handler
238                                              argument is either an arbitrary callable object or it
239                                              is a pointer-to-member to a member of the class which
240                                              holds this input. In the second case, the pointer will
241                                              automatically be bound to the containing instance.
242
243                                              \param[in] handler Handler to call, whenever an I/O
244                                                  operation is to be performed. */
245
246
247         bool throttled() const;         ///< Get accumulative throttling state
248         bool nativeThrottled() const;   ///< Get native throttling state
249
250         void throttle();                ///< Set native throttling
251         void unthrottle();              ///< Revoke native throttling
252
253         ActiveConnector & peer() const;
254
255     protected:
256         PassiveConnector();
257
258         void emit();
259
260         virtual void v_disconnected();
261         virtual void v_connected();
262
263     private:
264         virtual void v_init();
265
266         // Called by the routing to change the throttling state from forwarding routes
267         void notifyThrottle();          ///< Forward a throttle notification to this connector
268         void notifyUnthrottle();        ///< Forward an unthrottle notification to this connector
269
270         // Internal members to emit throttling notifications to the connected peer
271         void emitThrottle();
272         void emitUnthrottle();
273
274         // Called after unthrottling the connector
275         virtual void v_unthrottleEvent();
276
277         // called by ForwardingRoute to register a new route
278         void registerRoute(ForwardingRoute & route);
279         void unregisterRoute(ForwardingRoute & route);
280
281         ActiveConnector * peer_;
282
283         typedef ppi::detail::Callback<>::type Callback;
284         Callback callback_;
285
286         bool remoteThrottled_;
287         bool nativeThrottled_;
288
289         typedef std::vector<ForwardingRoute*> Routes;
290         Routes routes_;
291
292         friend class senf::ppi::ForwardingRoute;
293     };
294
295     /** \brief Active connector base-class
296
297         An active connector is a connector which emits I/O requests. Active connectors receive
298         throttling notifications. Depending on the type of connector (input or output) the
299         respective throttling is called forward or backward throttling.
300
301         Active connectors do not handle any throttling state, they just receive the
302         notifications. These notifications should then either be processed by the module or be
303         forwarded to other connectors.
304      */
305     class ActiveConnector
306         : public virtual Connector
307     {
308         typedef ppi::detail::Callback<>::type Callback;
309     public:
310         ~ActiveConnector();
311
312         template <class Handler>
313         void onThrottle(Handler handler); ///< Register throttle notification handler
314                                         /**< The handler register here will be called, whenever a
315                                              throttle notification comes in. The \a handler argument
316                                              is either an arbitrary callable object or it is a
317                                              pointer-to-member to a member of the class which holds
318                                              this input. In the second case, the pointer will
319                                              automatically be bound to the containing instance.
320
321                                              \param[in] handler Handler to call on throttle
322                                                  notifications. */
323         void onThrottle();              ///< Clear throttle notification handler
324
325         template <class Handler>
326         void onUnthrottle(Handler handler); ///< Register unthrottle notification handler
327                                         /**< The handler register here will be called, whenever an
328                                              unthrottle notification comes in. The \a handler
329                                              argument is either an arbitrary callable object or it
330                                              is a pointer-to-member to a member of the class which
331                                              holds this input. In the second case, the pointer will
332                                              automatically be bound to the containing instance.
333
334                                              \param[in] handler Handler to call on unthrottle
335                                                  notifications. */
336         void onUnthrottle();            ///< Clear unthrottle notification handler
337
338         bool throttled() const;         ///< \c true, if peer() is throttled
339
340         PassiveConnector & peer() const;
341
342     protected:
343         ActiveConnector();
344
345         virtual void v_disconnected();
346         virtual void v_connected();
347
348     private:
349         virtual void v_init();
350
351         // called by the peer() to forward throttling notifications
352         void notifyThrottle();
353         void notifyUnthrottle();
354
355         // called by ForwardingRoute to register a new route
356         void registerRoute(ForwardingRoute & route);
357         void unregisterRoute(ForwardingRoute & route);
358
359         PassiveConnector * peer_;
360
361         Callback throttleCallback_;
362         Callback unthrottleCallback_;
363
364         typedef std::vector<ForwardingRoute*> NotifyRoutes;
365         NotifyRoutes notifyRoutes_;
366
367         bool throttled_;
368
369         friend class senf::ppi::ForwardingRoute;
370         friend class PassiveConnector;
371     };
372
373     /** \brief Input connector base-class
374
375         An input connector receives packets. It may be either an ActiveConnector or a
376         PassiveConnector. An input connector contains a packet queue. This queue enables processing
377         packets in batches or generating multiple output packets from a single input packet. The
378         queues have the potential to greatly simplify the module implementations.
379
380         \implementation Which container to use?
381             \li list has good insertion and deletion properties on both ends but it costs a dynamic
382                 memory allocation for every insertion. A very good property is, that iterators stay
383                 valid across insertions/deletions
384             \li vector is fast and has good amortized dynamic allocation properties. However, it is
385                 quite unusable as a queue
386             \li deque has comparable dynamic allocation properties as vector but also has good
387                 insertion/removal properties on both ends.
388
389             So probably we will use a deque. I'd like a container which keeps iterators intact on
390             insertion/deletion but I believe that list is just to expensive since every packet will
391             be added to the queue before it can be processed.
392      */
393     class InputConnector
394         : public virtual Connector
395     {
396         typedef std::deque<Packet> Queue;
397     public:
398         typedef Queue::const_iterator queue_iterator; ///< Iterator type of the embedded queue
399         typedef Queue::size_type size_type; ///< Unsigned type for counting queue elements
400
401
402         Packet const & operator()();    ///< Get a packet
403                                         /**< This member is the primary method to access received
404                                              data. On passive connectors, this operator will just
405                                              dequeue a packet from the packet queue. If the
406                                              connector is active, the connector will request new
407                                              packets from the connected module. If the packet
408                                              request cannot be fulfilled an in-valid Packet is
409                                              returned. */
410
411         Packet const & read();          ///< Alias for operator()()
412
413         OutputConnector & peer() const;
414
415         size_type queueSize() const;    ///< Return number of elements in the queue
416         bool empty() const;             ///< Return queueSize() == 0
417
418     protected:
419         InputConnector();
420
421         virtual void v_disconnected();
422         virtual void v_connected();
423
424     private:
425         void enqueue(Packet const & p);
426
427         virtual void v_requestEvent();
428         virtual void v_enqueueEvent();
429         virtual void v_dequeueEvent();
430
431         OutputConnector * peer_;
432         Queue queue_;
433         Packet const * fastPacket_;
434         Packet slowPacket_;
435
436         friend class OutputConnector;
437     };
438
439     /** \brief Output connector base-class
440
441         An output connector sends out packets. It may be either an ActiveConnector or a
442         PassiveConnector. An output connector does \e not have an built-in queueing, it relies on
443         the queueing of the connected input.
444      */
445     class OutputConnector
446         : public virtual Connector
447     {
448     public:
449         void operator()(Packet const & p);      ///< Send out a packet
450
451         void write(Packet const & p);           ///< Alias for operator()(Packet p)
452
453         InputConnector & peer() const;
454
455     protected:
456         OutputConnector();
457
458         virtual void v_disconnected();
459         virtual void v_connected();
460
461     private:
462         InputConnector * peer_;
463     };
464
465     /** \brief Combination of PassiveConnector and InputConnector
466
467         The GenericPassiveInput automatically controls the connectors throttling state using a
468         queueing discipline. The standard queueing discipline is ThresholdQueueing, which throttles
469         the connection whenever the queue length reaches the high threshold and unthrottles the
470         connection when the queue reaches the low threshold. The default queueing discipline is
471         <tt>ThresholdQueueing(1,0)</tt> which will throttle the input whenever the queue is
472         non-empty.
473      */
474     class GenericPassiveInput
475         : public PassiveConnector, public InputConnector,
476           public safe_bool<GenericPassiveInput>
477     {
478     public:
479         GenericActiveOutput & peer() const;
480
481         bool boolean_test() const;      ///< \c true, if ! empty()
482
483         template <class QDisc>
484         void qdisc(QDisc const & disc); ///< Change the queueing discipline
485                                         /**< The queueing discipline is a class which provides the
486                                              QueueingDiscipline interface.
487
488                                              \param[in] disc New queueing discipline */
489         void qdisc(QueueingDiscipline::None_t);
490                                         ///< Disable queueing discipline
491
492     protected:
493         GenericPassiveInput();
494
495         virtual void v_disconnected();
496         virtual void v_connected();
497
498     private:
499         void v_enqueueEvent();
500         void v_dequeueEvent();
501         void v_unthrottleEvent();
502
503         GenericActiveOutput * peer_;
504         boost::scoped_ptr<QueueingDiscipline> qdisc_;
505     };
506
507     /** \brief Combination of PassiveConnector and OutputConnector
508      */
509     class GenericPassiveOutput
510         : public PassiveConnector, public OutputConnector,
511           public safe_bool<GenericPassiveOutput>
512     {
513     public:
514         GenericActiveInput & peer() const;
515
516         bool boolean_test() const;      ///< Always \c true
517
518         void connect(GenericActiveInput & target); ///< Internal: Use senf::ppi::connect() instead
519
520         friend class GenericActiveInput;
521
522     protected:
523         GenericPassiveOutput();
524
525         virtual void v_disconnected();
526         virtual void v_connected();
527
528     private:
529         GenericActiveInput * peer_;
530     };
531
532     /** \brief Combination of ActiveConnector and InputConnector
533      */
534     class GenericActiveInput
535         : public ActiveConnector, public InputConnector,
536           public safe_bool<GenericActiveInput>
537     {
538     public:
539         GenericPassiveOutput & peer() const;
540
541         bool boolean_test() const;      ///< \c true, if ! empty() or ! throttled()
542
543         void request();                 ///< request more packets without dequeuing any packet
544
545     protected:
546         GenericActiveInput();
547
548         virtual void v_disconnected();
549         virtual void v_connected();
550
551     private:
552         void v_requestEvent();
553
554         GenericPassiveOutput * peer_;
555     };
556
557     /** \brief Combination of ActiveConnector and OutputConnector
558      */
559     class GenericActiveOutput
560         : public ActiveConnector, public OutputConnector,
561           public safe_bool<GenericActiveOutput>
562     {
563     public:
564         GenericPassiveInput & peer() const;
565
566         bool boolean_test() const;      ///< \c true if peer() is ! throttled()
567
568         void connect(GenericPassiveInput & target); ///< Internal: Use senf::ppi::connect() instead
569
570     protected:
571         GenericActiveOutput();
572
573         virtual void v_disconnected();
574         virtual void v_connected();
575
576     private:
577         GenericPassiveInput * peer_;
578     };
579
580
581 #ifndef DOXYGEN
582
583 #   define TypedConnector_Input read
584 #   define TypedConnector_Output write
585 #   define TypedConnector(pType, dir)                                                             \
586         template <class PacketType>                                                               \
587         class pType ## dir                                                                        \
588             : public Generic ## pType ## dir,                                                     \
589               private detail::Typed ## dir ## Mixin<pType ## dir <PacketType>, PacketType>        \
590         {                                                                                         \
591             typedef detail::Typed ## dir ## Mixin<pType ## dir <PacketType>, PacketType> mixin;   \
592         public:                                                                                   \
593             using mixin::operator();                                                              \
594             using mixin::TypedConnector_ ## dir ;                                                 \
595         private:                                                                                  \
596             virtual std::type_info const & packetTypeID()                                         \
597                 { return typeid(typename PacketType::type); }                                     \
598             friend class detail::Typed ## dir ## Mixin<pType ## dir <PacketType>, PacketType>;    \
599         };                                                                                        \
600         template <>                                                                               \
601         class pType ## dir <Packet> : public Generic ## pType ## dir                              \
602         {}
603
604     TypedConnector( Passive, Input  );
605     TypedConnector( Passive, Output );
606     TypedConnector( Active,  Input  );
607     TypedConnector( Active,  Output );
608
609 #   undef TypedConnector
610 #   undef TypedConnector_Input
611 #   undef TypedConnector_Output
612
613 #else
614
615     /** \brief Connector actively reading packets
616
617         \tparam PacketType Type of packet to read. Defaults to senf::Packet
618
619         The %ActiveInput %connector template reads data actively from a connected %module. This
620         class is completely implemented via it's base-class, GenericActiveInput, the only
621         difference is that read packets are returned as \a PacketType instead of generic
622         senf::Packet references.
623
624         \see GenericActiveInput \n
625             senf::ppi::connector
626      */
627     template <class PacketType=Packet>
628     class ActiveInput : public GenericActiveInput
629     {
630     public:
631         PacketType operator()();        ///< Read packet
632                                         /**< \throws std::bad_cast if the %connector receives a
633                                              Packet which is not of type \a PacketType.
634                                              \returns newly read packet reference. */
635         PacketType read();              ///< Alias for operator()
636     };
637
638     /** \brief Connector passively receiving packets
639
640         \tparam PacketType Type of packet to read. Defaults to senf::Packet
641
642         The %PassiveInput %connector template receives packets sent to it from a connected
643         %module. This class is completely implemented via it's base-class, GenericPassiveInput,
644         the only difference is that read packets are returned as \a PacketType instead of generic
645         senf::Packet references.
646
647         \see GenericPassiveInput \n
648             senf::ppi::connector
649      */
650     template <class PacketType=Packet>
651     class PassiveInput : public GenericPassiveInput
652     {
653     public:
654         PacketType operator()();        ///< Read packet
655                                         /**< \throws std::bad_cast if the %connector receives a
656                                              Packet which is not of type \a PacketType.
657                                              \returns newly read packet reference. */
658         PacketType read();              ///< Alias for operator()
659     };
660
661     /** \brief Connector actively sending packets
662
663         \tparam PacketType Type of packet to send. Defaults to senf::Packet
664
665         The %ActiveOutput %connector template sends data actively to a connected %module. This
666         class is completely implemented via it's base-class, GenericActiveOutput, the only
667         difference is that it only sends packets of type \a PacketType.
668
669         \see GenericActiveOutput \n
670             senf::ppi::connector
671      */
672     template <class PacketType=Packet>
673     class ActiveOutput : public GenericActiveOutput
674     {
675     public:
676         void operator()(PacketType packet); ///< Send out a packet
677         void write(PacketType packet);      ///< Alias for operator()
678     };
679
680     /** \brief Connector passively providing packets
681
682         \tparam PacketType Type of packet to send. Defaults to senf::Packet
683
684         The %PassiveOutput %connector template provides data passively to a connected %module
685         whenever signaled. This class is completely implemented via it's base-class,
686         GenericPassiveOutput, the only difference is that it only sends packets of type
687         \a PacketType.
688
689         \see GenericPassiveOutput \n
690             senf::ppi::connector
691      */
692     template <class PacketType=Packet>
693     class PassiveOutput : public GenericPassiveOutput
694     {
695     public:
696         void operator()(PacketType packet); ///< Send out a packet
697         void write(PacketType packet);      ///< Alias for operator()
698     };
699
700 #endif
701
702 }}}
703
704 //-/////////////////////////////////////////////////////////////////////////////////////////////////
705 #include "Connectors.cci"
706 #include "Connectors.ct"
707 #include "Connectors.cti"
708 #endif
709
710 \f
711 // Local Variables:
712 // mode: c++
713 // fill-column: 100
714 // c-file-style: "senf"
715 // indent-tabs-mode: nil
716 // ispell-local-dictionary: "american"
717 // compile-command: "scons -u test"
718 // comment-column: 40
719 // End: