ff44b48afd30a9abee004bfc22a5dcf4df220dba
[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 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 read();                  ///< Alias for operator()()
412
413         OutputConnector & peer() const;
414
415         queue_iterator begin() const;   ///< Access queue begin (head)
416         queue_iterator end() const;     ///< Access queue past-the-end (tail)
417         Packet peek() const;            ///< Return head element from the queue
418
419         size_type queueSize() const;    ///< Return number of elements in the queue
420         bool empty() const;             ///< Return queueSize() == 0
421
422     protected:
423         InputConnector();
424
425         virtual void v_disconnected();
426         virtual void v_connected();
427
428     private:
429         void enqueue(Packet const & p);
430
431         virtual void v_requestEvent();
432         virtual void v_enqueueEvent();
433         virtual void v_dequeueEvent();
434
435         OutputConnector * peer_;
436         Queue queue_;
437
438         friend class OutputConnector;
439     };
440
441     /** \brief Output connector base-class
442
443         An output connector sends out packets. It may be either an ActiveConnector or a
444         PassiveConnector. An output connector does \e not have an built-in queueing, it relies on
445         the queueing of the connected input.
446      */
447     class OutputConnector
448         : public virtual Connector
449     {
450     public:
451         void operator()(Packet const & p);      ///< Send out a packet
452
453         void write(Packet const & p);           ///< Alias for operator()(Packet p)
454
455         InputConnector & peer() const;
456
457     protected:
458         OutputConnector();
459
460         virtual void v_disconnected();
461         virtual void v_connected();
462
463     private:
464         InputConnector * peer_;
465     };
466
467     /** \brief Combination of PassiveConnector and InputConnector
468
469         The GenericPassiveInput automatically controls the connectors throttling state using a
470         queueing discipline. The standard queueing discipline is ThresholdQueueing, which throttles
471         the connection whenever the queue length reaches the high threshold and unthrottles the
472         connection when the queue reaches the low threshold. The default queueing discipline is
473         <tt>ThresholdQueueing(1,0)</tt> which will throttle the input whenever the queue is
474         non-empty.
475      */
476     class GenericPassiveInput
477         : public PassiveConnector, public InputConnector,
478           public safe_bool<GenericPassiveInput>
479     {
480     public:
481         GenericActiveOutput & peer() const;
482
483         bool boolean_test() const;      ///< \c true, if ! empty()
484
485         template <class QDisc>
486         void qdisc(QDisc const & disc); ///< Change the queueing discipline
487                                         /**< The queueing discipline is a class which provides the
488                                              QueueingDiscipline interface.
489
490                                              \param[in] disc New queueing discipline */
491         void qdisc(QueueingDiscipline::None_t);
492                                         ///< Disable queueing discipline
493
494     protected:
495         GenericPassiveInput();
496
497         virtual void v_disconnected();
498         virtual void v_connected();
499
500     private:
501         void v_enqueueEvent();
502         void v_dequeueEvent();
503         void v_unthrottleEvent();
504
505         GenericActiveOutput * peer_;
506         boost::scoped_ptr<QueueingDiscipline> qdisc_;
507     };
508
509     /** \brief Combination of PassiveConnector and OutputConnector
510      */
511     class GenericPassiveOutput
512         : public PassiveConnector, public OutputConnector,
513           public safe_bool<GenericPassiveOutput>
514     {
515     public:
516         GenericActiveInput & peer() const;
517
518         bool boolean_test() const;      ///< Always \c true
519
520         void connect(GenericActiveInput & target); ///< Internal: Use senf::ppi::connect() instead
521
522         friend class GenericActiveInput;
523
524     protected:
525         GenericPassiveOutput();
526
527         virtual void v_disconnected();
528         virtual void v_connected();
529
530     private:
531         GenericActiveInput * peer_;
532     };
533
534     /** \brief Combination of ActiveConnector and InputConnector
535      */
536     class GenericActiveInput
537         : public ActiveConnector, public InputConnector,
538           public safe_bool<GenericActiveInput>
539     {
540     public:
541         GenericPassiveOutput & peer() const;
542
543         bool boolean_test() const;      ///< \c true, if ! empty() or ! throttled()
544
545         void request();                 ///< request more packets without dequeuing any packet
546
547     protected:
548         GenericActiveInput();
549
550         virtual void v_disconnected();
551         virtual void v_connected();
552
553     private:
554         void v_requestEvent();
555
556         GenericPassiveOutput * peer_;
557     };
558
559     /** \brief Combination of ActiveConnector and OutputConnector
560      */
561     class GenericActiveOutput
562         : public ActiveConnector, public OutputConnector,
563           public safe_bool<GenericActiveOutput>
564     {
565     public:
566         GenericPassiveInput & peer() const;
567
568         bool boolean_test() const;      ///< \c true if peer() is ! throttled()
569
570         void connect(GenericPassiveInput & target); ///< Internal: Use senf::ppi::connect() instead
571
572     protected:
573         GenericActiveOutput();
574
575         virtual void v_disconnected();
576         virtual void v_connected();
577
578     private:
579         GenericPassiveInput * peer_;
580     };
581
582
583 #ifndef DOXYGEN
584
585 #   define TypedConnector_Input read
586 #   define TypedConnector_Output write
587 #   define TypedConnector(pType, dir)                                                             \
588         template <class PacketType>                                                               \
589         class pType ## dir                                                                        \
590             : public Generic ## pType ## dir,                                                     \
591               private detail::Typed ## dir ## Mixin<pType ## dir <PacketType>, PacketType>        \
592         {                                                                                         \
593             typedef detail::Typed ## dir ## Mixin<pType ## dir <PacketType>, PacketType> mixin;   \
594         public:                                                                                   \
595             using mixin::operator();                                                              \
596             using mixin::TypedConnector_ ## dir ;                                                 \
597         private:                                                                                  \
598             virtual std::type_info const & packetTypeID()                                         \
599                 { return typeid(typename PacketType::type); }                                     \
600             friend class detail::Typed ## dir ## Mixin<pType ## dir <PacketType>, PacketType>;    \
601         };                                                                                        \
602         template <>                                                                               \
603         class pType ## dir <Packet> : public Generic ## pType ## dir                              \
604         {}
605
606     TypedConnector( Passive, Input  );
607     TypedConnector( Passive, Output );
608     TypedConnector( Active,  Input  );
609     TypedConnector( Active,  Output );
610
611 #   undef TypedConnector
612 #   undef TypedConnector_Input
613 #   undef TypedConnector_Output
614
615 #else
616
617     /** \brief Connector actively reading packets
618
619         \tparam PacketType Type of packet to read. Defaults to senf::Packet
620
621         The %ActiveInput %connector template reads data actively from a connected %module. This
622         class is completely implemented via it's base-class, GenericActiveInput, the only
623         difference is that read packets are returned as \a PacketType instead of generic
624         senf::Packet references.
625
626         \see GenericActiveInput \n
627             senf::ppi::connector
628      */
629     template <class PacketType=Packet>
630     class ActiveInput : public GenericActiveInput
631     {
632     public:
633         PacketType operator()();        ///< Read packet
634                                         /**< \throws std::bad_cast if the %connector receives a
635                                              Packet which is not of type \a PacketType.
636                                              \returns newly read packet reference. */
637         PacketType read();              ///< Alias for operator()
638     };
639
640     /** \brief Connector passively receiving packets
641
642         \tparam PacketType Type of packet to read. Defaults to senf::Packet
643
644         The %PassiveInput %connector template receives packets sent to it from a connected
645         %module. This class is completely implemented via it's base-class, GenericPassiveInput,
646         the only difference is that read packets are returned as \a PacketType instead of generic
647         senf::Packet references.
648
649         \see GenericPassiveInput \n
650             senf::ppi::connector
651      */
652     template <class PacketType=Packet>
653     class PassiveInput : public GenericPassiveInput
654     {
655     public:
656         PacketType operator()();        ///< Read packet
657                                         /**< \throws std::bad_cast if the %connector receives a
658                                              Packet which is not of type \a PacketType.
659                                              \returns newly read packet reference. */
660         PacketType read();              ///< Alias for operator()
661     };
662
663     /** \brief Connector actively sending packets
664
665         \tparam PacketType Type of packet to send. Defaults to senf::Packet
666
667         The %ActiveOutput %connector template sends data actively to a connected %module. This
668         class is completely implemented via it's base-class, GenericActiveOutput, the only
669         difference is that it only sends packets of type \a PacketType.
670
671         \see GenericActiveOutput \n
672             senf::ppi::connector
673      */
674     template <class PacketType=Packet>
675     class ActiveOutput : public GenericActiveOutput
676     {
677     public:
678         void operator()(PacketType packet); ///< Send out a packet
679         void write(PacketType packet);      ///< Alias for operator()
680     };
681
682     /** \brief Connector passively providing packets
683
684         \tparam PacketType Type of packet to send. Defaults to senf::Packet
685
686         The %PassiveOutput %connector template provides data passively to a connected %module
687         whenever signaled. This class is completely implemented via it's base-class,
688         GenericPassiveOutput, the only difference is that it only sends packets of type
689         \a PacketType.
690
691         \see GenericPassiveOutput \n
692             senf::ppi::connector
693      */
694     template <class PacketType=Packet>
695     class PassiveOutput : public GenericPassiveOutput
696     {
697     public:
698         void operator()(PacketType packet); ///< Send out a packet
699         void write(PacketType packet);      ///< Alias for operator()
700     };
701
702 #endif
703
704 }}}
705
706 //-/////////////////////////////////////////////////////////////////////////////////////////////////
707 #include "Connectors.cci"
708 #include "Connectors.ct"
709 #include "Connectors.cti"
710 #endif
711
712 \f
713 // Local Variables:
714 // mode: c++
715 // fill-column: 100
716 // c-file-style: "senf"
717 // indent-tabs-mode: nil
718 // ispell-local-dictionary: "american"
719 // compile-command: "scons -u test"
720 // comment-column: 40
721 // End: