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