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