PPI: avoid dynamic_cast in Connectors peer() by caching peer_ on connect()
[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         ActiveConnector * myPeer_;
284
285         typedef std::vector<ForwardingRoute*> Routes;
286         Routes routes_;
287
288         friend class senf::ppi::ForwardingRoute;
289     };
290
291     /** \brief Active connector base-class
292
293         An active connector is a connector which emits I/O requests. Active connectors receive
294         throttling notifications. Depending on the type of connector (input or output) the
295         respective throttling is called forward or backward throttling.
296
297         Active connectors do not handle any throttling state, they just receive the
298         notifications. These notifications should then either be processed by the module or be
299         forwarded to other connectors.
300      */
301     class ActiveConnector
302         : public virtual Connector
303     {
304         typedef ppi::detail::Callback<>::type Callback;
305     public:
306         ~ActiveConnector();
307
308         template <class Handler>
309         void onThrottle(Handler handler); ///< Register throttle notification handler
310                                         /**< The handler register here will be called, whenever a
311                                              throttle notification comes in. The \a handler argument
312                                              is either an arbitrary callable object or it is a
313                                              pointer-to-member to a member of the class which holds
314                                              this input. In the second case, the pointer will
315                                              automatically be bound to the containing instance.
316
317                                              \param[in] handler Handler to call on throttle
318                                                  notifications. */
319         void onThrottle();              ///< Clear throttle notification handler
320
321         template <class Handler>
322         void onUnthrottle(Handler handler); ///< Register unthrottle notification handler
323                                         /**< The handler register here will be called, whenever an
324                                              unthrottle notification comes in. The \a handler
325                                              argument is either an arbitrary callable object or it
326                                              is a pointer-to-member to a member of the class which
327                                              holds this input. In the second case, the pointer will
328                                              automatically be bound to the containing instance.
329
330                                              \param[in] handler Handler to call on unthrottle
331                                                  notifications. */
332         void onUnthrottle();            ///< Clear unthrottle notification handler
333
334         bool throttled() const;         ///< \c true, if peer() is throttled
335
336         PassiveConnector & peer() const;
337
338     protected:
339         ActiveConnector();
340
341         virtual void v_disconnected();
342         virtual void v_connected();
343
344     private:
345         virtual void v_init();
346
347         // called by the peer() to forward throttling notifications
348         void notifyThrottle();
349         void notifyUnthrottle();
350
351         // called by ForwardingRoute to register a new route
352         void registerRoute(ForwardingRoute & route);
353         void unregisterRoute(ForwardingRoute & route);
354
355         PassiveConnector * peer_;
356
357         Callback throttleCallback_;
358         Callback unthrottleCallback_;
359
360         typedef std::vector<ForwardingRoute*> NotifyRoutes;
361         NotifyRoutes notifyRoutes_;
362
363         bool throttled_;
364
365         friend class senf::ppi::ForwardingRoute;
366         friend class PassiveConnector;
367     };
368
369     /** \brief Input connector base-class
370
371         An input connector receives packets. It may be either an ActiveConnector or a
372         PassiveConnector. An input connector contains a packet queue. This queue enables processing
373         packets in batches or generating multiple output packets from a single input packet. The
374         queues have the potential to greatly simplify the module implementations.
375
376         \implementation Which container to use?
377             \li list has good insertion and deletion properties on both ends but it costs a dynamic
378                 memory allocation for every insertion. A very good property is, that iterators stay
379                 valid across insertions/deletions
380             \li vector is fast and has good amortized dynamic allocation properties. However, it is
381                 quite unusable as a queue
382             \li deque has comparable dynamic allocation properties as vector but also has good
383                 insertion/removal properties on both ends.
384
385             So probably we will use a deque. I'd like a container which keeps iterators intact on
386             insertion/deletion but I believe that list is just to expensive since every packet will
387             be added to the queue before it can be processed.
388      */
389     class InputConnector
390         : public virtual Connector
391     {
392         typedef std::deque<Packet> Queue;
393     public:
394         typedef Queue::const_iterator queue_iterator; ///< Iterator type of the embedded queue
395         typedef Queue::size_type size_type; ///< Unsigned type for counting queue elements
396
397
398         Packet operator()();            ///< Get a packet
399                                         /**< This member is the primary method to access received
400                                              data. On passive connectors, this operator will just
401                                              dequeue a packet from the packet queue. If the
402                                              connector is active, the connector will request new
403                                              packets from the connected module. If the packet
404                                              request cannot be fulfilled an in-valid Packet is
405                                              returned. */
406
407         Packet read();                  ///< Alias for operator()()
408
409         OutputConnector & peer() const;
410
411         queue_iterator begin() const;   ///< Access queue begin (head)
412         queue_iterator end() const;     ///< Access queue past-the-end (tail)
413         Packet peek() const;            ///< Return head element from the queue
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
434         friend class OutputConnector;
435     };
436
437     /** \brief Output connector base-class
438
439         An output connector sends out packets. It may be either an ActiveConnector or a
440         PassiveConnector. An output connector does \e not have an built-in queueing, it relies on
441         the queueing of the connected input.
442      */
443     class OutputConnector
444         : public virtual Connector
445     {
446     public:
447         void operator()(Packet const & p);      ///< Send out a packet
448
449         void write(Packet const & p);           ///< Alias for operator()(Packet p)
450
451         InputConnector & peer() const;
452
453     protected:
454         OutputConnector();
455
456         virtual void v_disconnected();
457         virtual void v_connected();
458
459     private:
460         InputConnector * peer_;
461     };
462
463     /** \brief Combination of PassiveConnector and InputConnector
464
465         The GenericPassiveInput automatically controls the connectors throttling state using a
466         queueing discipline. The standard queueing discipline is ThresholdQueueing, which throttles
467         the connection whenever the queue length reaches the high threshold and unthrottles the
468         connection when the queue reaches the low threshold. The default queueing discipline is
469         <tt>ThresholdQueueing(1,0)</tt> which will throttle the input whenever the queue is
470         non-empty.
471      */
472     class GenericPassiveInput
473         : public PassiveConnector, public InputConnector,
474           public safe_bool<GenericPassiveInput>
475     {
476     public:
477         GenericActiveOutput & peer() const;
478
479         bool boolean_test() const;      ///< \c true, if ! empty()
480
481         template <class QDisc>
482         void qdisc(QDisc const & disc); ///< Change the queueing discipline
483                                         /**< The queueing discipline is a class which provides the
484                                              QueueingDiscipline interface.
485
486                                              \param[in] disc New queueing discipline */
487         void qdisc(QueueingDiscipline::None_t);
488                                         ///< Disable queueing discipline
489
490     protected:
491         GenericPassiveInput();
492
493         virtual void v_disconnected();
494         virtual void v_connected();
495
496     private:
497         void v_enqueueEvent();
498         void v_dequeueEvent();
499         void v_unthrottleEvent();
500
501         GenericActiveOutput * peer_;
502         boost::scoped_ptr<QueueingDiscipline> qdisc_;
503     };
504
505     /** \brief Combination of PassiveConnector and OutputConnector
506      */
507     class GenericPassiveOutput
508         : public PassiveConnector, public OutputConnector,
509           public safe_bool<GenericPassiveOutput>
510     {
511     public:
512         GenericActiveInput & peer() const;
513
514         bool boolean_test() const;      ///< Always \c true
515
516         void connect(GenericActiveInput & target); ///< Internal: Use senf::ppi::connect() instead
517
518         friend class GenericActiveInput;
519
520     protected:
521         GenericPassiveOutput();
522
523         virtual void v_disconnected();
524         virtual void v_connected();
525
526     private:
527         GenericActiveInput * peer_;
528     };
529
530     /** \brief Combination of ActiveConnector and InputConnector
531      */
532     class GenericActiveInput
533         : public ActiveConnector, public InputConnector,
534           public safe_bool<GenericActiveInput>
535     {
536     public:
537         GenericPassiveOutput & peer() const;
538
539         bool boolean_test() const;      ///< \c true, if ! empty() or ! throttled()
540
541         void request();                 ///< request more packets without dequeuing any packet
542
543     protected:
544         GenericActiveInput();
545
546         virtual void v_disconnected();
547         virtual void v_connected();
548
549     private:
550         void v_requestEvent();
551
552         GenericPassiveOutput * peer_;
553     };
554
555     /** \brief Combination of ActiveConnector and OutputConnector
556      */
557     class GenericActiveOutput
558         : public ActiveConnector, public OutputConnector,
559           public safe_bool<GenericActiveOutput>
560     {
561     public:
562         GenericPassiveInput & peer() const;
563
564         bool boolean_test() const;      ///< \c true if peer() is ! throttled()
565
566         void connect(GenericPassiveInput & target); ///< Internal: Use senf::ppi::connect() instead
567
568     protected:
569         GenericActiveOutput();
570
571         virtual void v_disconnected();
572         virtual void v_connected();
573
574     private:
575         GenericPassiveInput * peer_;
576     };
577
578
579 #ifndef DOXYGEN
580
581 #   define TypedConnector_Input read
582 #   define TypedConnector_Output write
583 #   define TypedConnector(pType, dir)                                                             \
584         template <class PacketType>                                                               \
585         class pType ## dir                                                                        \
586             : public Generic ## pType ## dir,                                                     \
587               private detail::Typed ## dir ## Mixin<pType ## dir <PacketType>, PacketType>        \
588         {                                                                                         \
589             typedef detail::Typed ## dir ## Mixin<pType ## dir <PacketType>, PacketType> mixin;   \
590         public:                                                                                   \
591             using mixin::operator();                                                              \
592             using mixin::TypedConnector_ ## dir ;                                                 \
593         private:                                                                                  \
594             virtual std::type_info const & packetTypeID()                                         \
595                 { return typeid(typename PacketType::type); }                                     \
596             friend class detail::Typed ## dir ## Mixin<pType ## dir <PacketType>, PacketType>;    \
597         };                                                                                        \
598         template <>                                                                               \
599         class pType ## dir <Packet> : public Generic ## pType ## dir                              \
600         {}
601
602     TypedConnector( Passive, Input  );
603     TypedConnector( Passive, Output );
604     TypedConnector( Active,  Input  );
605     TypedConnector( Active,  Output );
606
607 #   undef TypedConnector
608 #   undef TypedConnector_Input
609 #   undef TypedConnector_Output
610
611 #else
612
613     /** \brief Connector actively reading packets
614
615         \tparam PacketType Type of packet to read. Defaults to senf::Packet
616
617         The %ActiveInput %connector template reads data actively from a connected %module. This
618         class is completely implemented via it's base-class, GenericActiveInput, the only
619         difference is that read packets are returned as \a PacketType instead of generic
620         senf::Packet references.
621
622         \see GenericActiveInput \n
623             senf::ppi::connector
624      */
625     template <class PacketType=Packet>
626     class ActiveInput : public GenericActiveInput
627     {
628     public:
629         PacketType operator()();        ///< Read packet
630                                         /**< \throws std::bad_cast if the %connector receives a
631                                              Packet which is not of type \a PacketType.
632                                              \returns newly read packet reference. */
633         PacketType read();              ///< Alias for operator()
634     };
635
636     /** \brief Connector passively receiving packets
637
638         \tparam PacketType Type of packet to read. Defaults to senf::Packet
639
640         The %PassiveInput %connector template receives packets sent to it from a connected
641         %module. This class is completely implemented via it's base-class, GenericPassiveInput,
642         the only difference is that read packets are returned as \a PacketType instead of generic
643         senf::Packet references.
644
645         \see GenericPassiveInput \n
646             senf::ppi::connector
647      */
648     template <class PacketType=Packet>
649     class PassiveInput : public GenericPassiveInput
650     {
651     public:
652         PacketType operator()();        ///< Read packet
653                                         /**< \throws std::bad_cast if the %connector receives a
654                                              Packet which is not of type \a PacketType.
655                                              \returns newly read packet reference. */
656         PacketType read();              ///< Alias for operator()
657     };
658
659     /** \brief Connector actively sending packets
660
661         \tparam PacketType Type of packet to send. Defaults to senf::Packet
662
663         The %ActiveOutput %connector template sends data actively to a connected %module. This
664         class is completely implemented via it's base-class, GenericActiveOutput, the only
665         difference is that it only sends packets of type \a PacketType.
666
667         \see GenericActiveOutput \n
668             senf::ppi::connector
669      */
670     template <class PacketType=Packet>
671     class ActiveOutput : public GenericActiveOutput
672     {
673     public:
674         operator()(PacketType packet);  ///< Send out a packet
675         void write(PacketType packet);  ///< Alias for operator()
676     };
677
678     /** \brief Connector passively providing packets
679
680         \tparam PacketType Type of packet to send. Defaults to senf::Packet
681
682         The %PassiveOutput %connector template provides data passively to a connected %module
683         whenever signaled. This class is completely implemented via it's base-class,
684         GenericPassiveOutput, the only difference is that it only sends packets of type
685         \a PacketType.
686
687         \see GenericPassiveOutput \n
688             senf::ppi::connector
689      */
690     template <class PacketType=Packet>
691     class PassiveOutput : public GenericPassiveOutput
692     {
693     public:
694         operator()(PacketType packet);  ///< Send out a packet
695         void write(PacketType packet);  ///< Alias for operator()
696     };
697
698 #endif
699
700 }}}
701
702 ///////////////////////////////hh.e////////////////////////////////////////
703 #include "Connectors.cci"
704 #include "Connectors.ct"
705 #include "Connectors.cti"
706 #endif
707
708 \f
709 // Local Variables:
710 // mode: c++
711 // fill-column: 100
712 // c-file-style: "senf"
713 // indent-tabs-mode: nil
714 // ispell-local-dictionary: "american"
715 // compile-command: "scons -u test"
716 // comment-column: 40
717 // End: