2ca48bee7a821e387021f292c67ca8bff2e751ed
[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     private:
191         virtual std::type_info const & packetTypeID();
192
193         virtual void v_disconnected() const;
194
195         void setModule(module::Module & module);
196
197         Connector * peer_;
198         module::Module * module_;
199
200         static TraceState traceState_;
201
202         friend class module::Module;
203     };
204
205     /** \brief Passive connector base-class
206
207         A passive connector is a connector which is activated externally whenever an I/O request
208         occurs. Passive connectors are the origin of throttling notifications. Depending on the type
209         of connector (output or input) the respective throttling is called forward or backward
210         throttling.
211
212         Passive connectors always handle two throttling states:
213
214         - The \e native throttling state is set manually by the module. It is the throttling state
215             originating in the current module
216         - The \e forwarded throttling state is the state as it is received by throttling
217             notifications
218
219         The accumulative throttling state is generated by combining all sub-states.
220      */
221     class PassiveConnector
222         : public virtual Connector
223     {
224     public:
225         ~PassiveConnector();
226
227         template <class Handler>
228         void onRequest(Handler handler);///< Register I/O event handler
229                                         /**< The registered handler will be called, whenever packets
230                                              arrive or should be generated by the module depending
231                                              on the connector type (input or output). The \a handler
232                                              argument is either an arbitrary callable object or it
233                                              is a pointer-to-member to a member of the class which
234                                              holds this input. In the second case, the pointer will
235                                              automatically be bound to the containing instance.
236
237                                              \param[in] handler Handler to call, whenever an I/O
238                                                  operation is to be performed. */
239
240
241         bool throttled() const;         ///< Get accumulative throttling state
242         bool nativeThrottled() const;   ///< Get native throttling state
243
244         void throttle();                ///< Set native throttling
245         void unthrottle();              ///< Revoke native throttling
246
247         ActiveConnector & peer() const;
248
249     protected:
250         PassiveConnector();
251
252         void emit();
253
254     private:
255         virtual void v_init();
256
257         // Called by the routing to change the throttling state from forwarding routes
258         void notifyThrottle();          ///< Forward a throttle notification to this connector
259         void notifyUnthrottle();        ///< Forward an unthrottle notification to this connector
260
261         // Internal members to emit throttling notifications to the connected peer
262         void emitThrottle();
263         void emitUnthrottle();
264
265         // Called after unthrottling the connector
266         virtual void v_unthrottleEvent();
267
268         // called by ForwardingRoute to register a new route
269         void registerRoute(ForwardingRoute & route);
270         void unregisterRoute(ForwardingRoute & route);
271
272         typedef ppi::detail::Callback<>::type Callback;
273         Callback callback_;
274
275         bool remoteThrottled_;
276         bool nativeThrottled_;
277
278         typedef std::vector<ForwardingRoute*> Routes;
279         Routes routes_;
280
281         friend class senf::ppi::ForwardingRoute;
282     };
283
284     /** \brief Active connector base-class
285
286         An active connector is a connector which emits I/O requests. Active connectors receive
287         throttling notifications. Depending on the type of connector (input or output) the
288         respective throttling is called forward or backward throttling.
289
290         Active connectors do not handle any throttling state, they just receive the
291         notifications. These notifications should then either be processed by the module or be
292         forwarded to other connectors.
293      */
294     class ActiveConnector
295         : public virtual Connector
296     {
297         typedef ppi::detail::Callback<>::type Callback;
298     public:
299         ~ActiveConnector();
300
301         template <class Handler>
302         void onThrottle(Handler handler); ///< Register throttle notification handler
303                                         /**< The handler register here will be called, whenever a
304                                              throttle notification comes in. The \a handler argument
305                                              is either an arbitrary callable object or it is a
306                                              pointer-to-member to a member of the class which holds
307                                              this input. In the second case, the pointer will
308                                              automatically be bound to the containing instance.
309
310                                              \param[in] handler Handler to call on throttle
311                                                  notifications. */
312         void onThrottle();              ///< Clear throttle notification handler
313
314         template <class Handler>
315         void onUnthrottle(Handler handler); ///< Register unthrottle notification handler
316                                         /**< The handler register here will be called, whenever an
317                                              unthrottle notification comes in. The \a handler
318                                              argument is either an arbitrary callable object or it
319                                              is a pointer-to-member to a member of the class which
320                                              holds this input. In the second case, the pointer will
321                                              automatically be bound to the containing instance.
322
323                                              \param[in] handler Handler to call on unthrottle
324                                                  notifications. */
325         void onUnthrottle();            ///< Clear unthrottle notification handler
326
327         bool throttled() const;         ///< \c true, if peer() is throttled
328
329         PassiveConnector & peer() const;
330
331     protected:
332         ActiveConnector();
333
334     private:
335         virtual void v_init();
336
337         // called by the peer() to forward throttling notifications
338         void notifyThrottle();
339         void notifyUnthrottle();
340
341         // called by ForwardingRoute to register a new route
342         void registerRoute(ForwardingRoute & route);
343         void unregisterRoute(ForwardingRoute & route);
344
345         Callback throttleCallback_;
346         Callback unthrottleCallback_;
347
348         typedef std::vector<ForwardingRoute*> NotifyRoutes;
349         NotifyRoutes notifyRoutes_;
350
351         bool throttled_;
352
353         friend class senf::ppi::ForwardingRoute;
354         friend class PassiveConnector;
355     };
356
357     /** \brief Input connector base-class
358
359         An input connector receives packets. It may be either an ActiveConnector or a
360         PassiveConnector. An input connector contains a packet queue. This queue enables processing
361         packets in batches or generating multiple output packets from a single input packet. The
362         queues have the potential to greatly simplify the module implementations.
363
364         \implementation Which container to use?
365             \li list has good insertion and deletion properties on both ends but it costs a dynamic
366                 memory allocation for every insertion. A very good property is, that iterators stay
367                 valid across insertions/deletions
368             \li vector is fast and has good amortized dynamic allocation properties. However, it is
369                 quite unusable as a queue
370             \li deque has comparable dynamic allocation properties as vector but also has good
371                 insertion/removal properties on both ends.
372
373             So probably we will use a deque. I'd like a container which keeps iterators intact on
374             insertion/deletion but I believe that list is just to expensive since every packet will
375             be added to the queue before it can be processed.
376      */
377     class InputConnector
378         : public virtual Connector
379     {
380         typedef std::deque<Packet> Queue;
381     public:
382         typedef Queue::const_iterator queue_iterator; ///< Iterator type of the embedded queue
383         typedef Queue::size_type size_type; ///< Unsigned type for counting queue elements
384
385
386         Packet operator()();            ///< Get a packet
387                                         /**< This member is the primary method to access received
388                                              data. On passive connectors, this operator will just
389                                              dequeue a packet from the packet queue. If the
390                                              connector is active, the connector will request new
391                                              packets from the connected module. If the packet
392                                              request cannot be fulfilled, this is considered to be a
393                                              logic error in the module implementation and an
394                                              exception is raised. */
395
396         Packet read();                  ///< Alias for operator()()
397
398         OutputConnector & peer() const;
399
400         queue_iterator begin() const;   ///< Access queue begin (head)
401         queue_iterator end() const;     ///< Access queue past-the-end (tail)
402         Packet peek() const;            ///< Return head element from the queue
403
404         size_type queueSize() const;    ///< Return number of elements in the queue
405         bool empty() const;             ///< Return queueSize() == 0
406
407     protected:
408         InputConnector();
409
410     private:
411         void enqueue(Packet const & p);
412
413         virtual void v_requestEvent();
414         virtual void v_enqueueEvent();
415         virtual void v_dequeueEvent();
416
417         Queue queue_;
418
419         friend class OutputConnector;
420     };
421
422     /** \brief Output connector base-class
423
424         An output connector sends out packets. It may be either an ActiveConnector or a
425         PassiveConnector. An output connector does \e not have an built-in queueing, it relies on
426         the queueing of the connected input.
427      */
428     class OutputConnector
429         : public virtual Connector
430     {
431     public:
432         void operator()(Packet const & p);      ///< Send out a packet
433
434         void write(Packet const & p);           ///< Alias for operator()(Packet p)
435
436         InputConnector & peer() const;
437
438     protected:
439         OutputConnector();
440     };
441
442     /** \brief Combination of PassiveConnector and InputConnector
443
444         The GenericPassiveInput automatically controls the connectors throttling state using a
445         queueing discipline. The standard queueing discipline is ThresholdQueueing, which throttles
446         the connection whenever the queue length reaches the high threshold and unthrottles the
447         connection when the queue reaches the low threshold. The default queueing discipline is
448         <tt>ThresholdQueueing(1,0)</tt> which will throttle the input whenever the queue is
449         non-empty.
450      */
451     class GenericPassiveInput
452         : public PassiveConnector, public InputConnector,
453           public safe_bool<GenericPassiveInput>
454     {
455     public:
456         GenericActiveOutput & peer() const;
457
458         bool boolean_test() const;      ///< \c true, if ! empty()
459
460         template <class QDisc>
461         void qdisc(QDisc const & disc); ///< Change the queueing discipline
462                                         /**< The queueing discipline is a class which provides the
463                                              QueueingDiscipline interface.
464
465                                              \param[in] disc New queueing discipline */
466         void qdisc(QueueingDiscipline::None_t);
467                                         ///< Disable queueing discipline
468
469
470     protected:
471         GenericPassiveInput();
472
473     private:
474         void v_enqueueEvent();
475         void v_dequeueEvent();
476         void v_unthrottleEvent();
477
478         boost::scoped_ptr<QueueingDiscipline> qdisc_;
479     };
480
481     /** \brief Combination of PassiveConnector and OutputConnector
482      */
483     class GenericPassiveOutput
484         : public PassiveConnector, public OutputConnector,
485           public safe_bool<GenericPassiveOutput>
486     {
487     public:
488         GenericActiveInput & peer() const;
489
490         bool boolean_test() const;      ///< Always \c true
491
492         void connect(GenericActiveInput & target); ///< Internal: Use senf::ppi::connect() instead
493
494         friend class GenericActiveInput;
495
496     protected:
497         GenericPassiveOutput();
498
499     };
500
501     /** \brief Combination of ActiveConnector and InputConnector
502      */
503     class GenericActiveInput
504         : public ActiveConnector, public InputConnector,
505           public safe_bool<GenericActiveInput>
506     {
507     public:
508         GenericPassiveOutput & peer() const;
509
510         bool boolean_test() const;      ///< \c true, if ! empty() or ! throttled()
511
512         void request();                 ///< request more packets without dequeuing any packet
513
514     protected:
515         GenericActiveInput();
516
517     private:
518         void v_requestEvent();
519     };
520
521     /** \brief Combination of ActiveConnector and OutputConnector
522      */
523     class GenericActiveOutput
524         : public ActiveConnector, public OutputConnector,
525           public safe_bool<GenericActiveOutput>
526     {
527     public:
528         GenericPassiveInput & peer() const;
529
530         bool boolean_test() const;      ///< \c true if peer() is ! throttled()
531
532         void connect(GenericPassiveInput & target); ///< Internal: Use senf::ppi::connect() instead
533
534     protected:
535         GenericActiveOutput();
536
537     };
538
539 #ifndef DOXYGEN
540
541 #   define TypedConnector_Input read
542 #   define TypedConnector_Output write
543 #   define TypedConnector(pType, dir)                                                             \
544         template <class PacketType>                                                               \
545         class pType ## dir                                                                        \
546             : public Generic ## pType ## dir,                                                     \
547               private detail::Typed ## dir ## Mixin<pType ## dir <PacketType>, PacketType>        \
548         {                                                                                         \
549             typedef detail::Typed ## dir ## Mixin<pType ## dir <PacketType>, PacketType> mixin;   \
550         public:                                                                                   \
551             using mixin::operator();                                                              \
552             using mixin::TypedConnector_ ## dir ;                                                 \
553         private:                                                                                  \
554             virtual std::type_info const & packetTypeID()                                         \
555                 { return typeid(typename PacketType::type); }                                     \
556             friend class detail::Typed ## dir ## Mixin<pType ## dir <PacketType>, PacketType>;    \
557         };                                                                                        \
558         template <>                                                                               \
559         class pType ## dir <Packet> : public Generic ## pType ## dir                              \
560         {}
561
562     TypedConnector( Passive, Input  );
563     TypedConnector( Passive, Output );
564     TypedConnector( Active,  Input  );
565     TypedConnector( Active,  Output );
566
567 #   undef TypedConnector
568 #   undef TypedConnector_Input
569 #   undef TypedConnector_Output
570
571 #else
572
573     /** \brief Connector actively reading packets
574
575         \tparam PacketType Type of packet to read. Defaults to senf::Packet
576
577         The %ActiveInput %connector template reads data actively from a connected %module. This
578         class is completely implemented via it's base-class, GenericActiveInput, the only
579         difference is that read packets are returned as \a PacketType instead of generic
580         senf::Packet references.
581
582         \see GenericActiveInput \n
583             senf::ppi::connector
584      */
585     template <class PacketType=Packet>
586     class ActiveInput : public GenericActiveInput
587     {
588     public:
589         PacketType operator()();        ///< Read packet
590                                         /**< \throws std::bad_cast if the %connector receives a
591                                              Packet which is not of type \a PacketType.
592                                              \returns newly read packet reference. */
593         PacketType read();              ///< Alias for operator()
594     };
595
596     /** \brief Connector passively receiving packets
597
598         \tparam PacketType Type of packet to read. Defaults to senf::Packet
599
600         The %PassiveInput %connector template receives packets sent to it from a connected
601         %module. This class is completely implemented via it's base-class, GenericPassiveInput,
602         the only difference is that read packets are returned as \a PacketType instead of generic
603         senf::Packet references.
604
605         \see GenericPassiveInput \n
606             senf::ppi::connector
607      */
608     template <class PacketType=Packet>
609     class PassiveInput : public GenericPassiveInput
610     {
611     public:
612         PacketType operator()();        ///< Read packet
613                                         /**< \throws std::bad_cast if the %connector receives a
614                                              Packet which is not of type \a PacketType.
615                                              \returns newly read packet reference. */
616         PacketType read();              ///< Alias for operator()
617     };
618
619     /** \brief Connector actively sending packets
620
621         \tparam PacketType Type of packet to send. Defaults to senf::Packet
622
623         The %ActiveOutput %connector template sends data actively to a connected %module. This
624         class is completely implemented via it's base-class, GenericActiveOutput, the only
625         difference is that it only sends packets of type \a PacketType.
626
627         \see GenericActiveOutput \n
628             senf::ppi::connector
629      */
630     template <class PacketType=Packet>
631     class ActiveOutput : public GenericActiveOutput
632     {
633     public:
634         operator()(PacketType packet);  ///< Send out a packet
635         void write(PacketType packet);  ///< Alias for operator()
636     };
637
638     /** \brief Connector passively providing packets
639
640         \tparam PacketType Type of packet to send. Defaults to senf::Packet
641
642         The %PassiveOutput %connector template provides data passively to a connected %module
643         whenever signaled. This class is completely implemented via it's base-class,
644         GenericPassiveOutput, the only difference is that it only sends packets of type
645         \a PacketType.
646
647         \see GenericPassiveOutput \n
648             senf::ppi::connector
649      */
650     template <class PacketType=Packet>
651     class PassiveOutput : public GenericPassiveOutput
652     {
653     public:
654         operator()(PacketType packet);  ///< Send out a packet
655         void write(PacketType packet);  ///< Alias for operator()
656     };
657
658 #endif
659
660 }}}
661
662 ///////////////////////////////hh.e////////////////////////////////////////
663 #include "Connectors.cci"
664 #include "Connectors.ct"
665 #include "Connectors.cti"
666 #endif
667
668 \f
669 // Local Variables:
670 // mode: c++
671 // fill-column: 100
672 // c-file-style: "senf"
673 // indent-tabs-mode: nil
674 // ispell-local-dictionary: "american"
675 // compile-command: "scons -u test"
676 // comment-column: 40
677 // End: