PPI: Move generic packet typeid check into connector base-class
[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_Connectors_
27 #define HH_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
40 //#include "Connectors.mpp"
41 ///////////////////////////////hh.p////////////////////////////////////////
42
43 namespace senf {
44 namespace ppi {
45 namespace connector {
46
47     /** \namespace senf::ppi::connector
48         \brief Connector classes
49
50         A connector has two independent properties
51         - it may be \e active or \e passive
52         - it may be an \e input or an \e output
53
54         \e Active connectors are activated from within the module, \e passive connectors are
55         signaled by the external framework. \e Input modules receive packets, \e output modules send
56         packets.
57
58         All passive connectors call some onRequest callback whenever I/O needs to be performed. All
59         input modules possess a packet queue.
60
61         We therefore have 4 connector types:
62         - senf::ppi::connector::ActiveInput
63         - senf::ppi::connector::ActiveOutput
64         - senf::ppi::connector::PassiveInput
65         - senf::ppi::connector::PassiveOutput.
66
67         Connectors are declared as module data members and are then externally connected to other
68         modules.
69
70         The connectors each take an optional template argument. If this argument is specified, it
71         must be the type of packet expected or sent on this connector. If it is not specified,
72         packets will be passed using the generic Packet handle.
73
74         \code
75         class IpFilter : public senf::ppi::module::Module
76         {
77             SENF_PPI_MODULE(SomeModule);
78
79         public:
80             senf::ppi::connector::ActiveInput<senf::EthernetPacket> input;
81             senf::ppi::connector::PassiveOutput<senf::IpPacket> output;
82
83             IpFilter() {
84                 route(input, output);
85                 input.onRequest(&IpFilter::onRequest);
86             }
87
88         private:
89             void onRequest() {
90                 // 'input()' will return a senf::EthernetPacket packet handle
91                 try { output( input().find<IpPacket>() ); }
92                 catch (senf::InvalidPacketChainException & ex) { ; }
93             }
94         };
95         \endcode
96
97         \see
98             senf::ppi::module::Module \n
99             senf::ppi::connect() \n
100             \ref ppi_connectors
101      */
102
103     struct IncompatibleConnectorsException : public senf::Exception
104     { IncompatibleConnectorsException() : senf::Exception("Incompatible connectors") {} };
105
106     /** \brief Connector base-class
107
108         This connector provides access to the generic connector facilities. This includes the
109         connection management (access to the connected peer) and the containment management (access
110         to the containing module)
111      */
112     class Connector
113         : boost::noncopyable
114     {
115     public:
116         Connector & peer() const;       ///< Get peer connected to this connector
117         module::Module & module() const; ///< Get this connectors containing module
118
119     protected:
120         Connector();
121         virtual ~Connector();
122
123         void connect(Connector & target);
124
125     private:
126         virtual std::type_info const & packetTypeID();
127
128         void setModule(module::Module & module);
129
130         Connector * peer_;
131         module::Module * module_;
132
133         friend class module::Module;
134     };
135
136     /** \brief Passive connector base-class
137
138         A passive connector is a connector which is activated externally whenever an I/O request
139         occurs. Passive connectors are the origin of throttling notifications. Depending on the type
140         of connector (output or input) the respective throttling is called forward or backward
141         throttling.
142
143         Passive connectors always handle two throttling states:
144
145         - The \e native throttling state is set manually by the module. It is the throttling state
146             originating in the current module
147         - The \e forwarded throttling state is the state as it is received by throttling
148             notifications
149
150         The accumulative throttling state is generated by combining all sub-states.
151      */
152     class PassiveConnector
153         : public virtual Connector
154     {
155     public:
156         template <class Handler>
157         void onRequest(Handler handler);///< Register I/O event handler
158                                         /**< The registered handler will be called, whenever packets
159                                              arrive or should be generated by the module depending
160                                              on the connector type (input or output). The \a handler
161                                              argument is either an arbitrary callable object or it
162                                              is a pointer-to-member to a member of the class which
163                                              holds this input. In the second case, the pointer will
164                                              automatically be bound to the containing instance.
165
166                                              \param[in] handler Handler to call, whenever an I/O
167                                                  operation is to be performed. */
168
169
170         bool throttled() const;         ///< Get accumulative throttling state
171         bool nativeThrottled() const;   ///< Get native throttling state
172
173         void throttle();                ///< Set native throttling
174         void unthrottle();              ///< Revoke native throttling
175
176         ActiveConnector & peer() const;
177
178     protected:
179         PassiveConnector();
180
181         void emit();
182
183     private:
184         // Called by the routing to change the remote throttling state
185         void notifyThrottle();          ///< Forward a throttle notification to this connector
186         void notifyUnthrottle();        ///< Forward an unthrottle notification to this connector
187
188         // Internal members to emit throttling notifications
189         void emitThrottle();
190         void emitUnthrottle();
191
192         // Called after unthrottling the connector
193         virtual void v_unthrottleEvent();
194
195         // called by ForwardingRoute to register a new route
196         void registerRoute(ForwardingRoute & route);
197
198         typedef ppi::detail::Callback<>::type Callback;
199         Callback callback_;
200
201         bool remoteThrottled_;
202         bool nativeThrottled_;
203
204         typedef std::vector<ForwardingRoute*> Routes;
205         Routes routes_;
206
207         friend class senf::ppi::ForwardingRoute;
208     };
209
210     /** \brief Active connector base-class
211
212         An active connector is a connector which emits I/O requests. Active connectors receive
213         throttling notifications. Depending on the type of connector (input or output) the
214         respective throttling is called forward or backward throttling.
215
216         Active connectors do not handle any throttling state, they just receive the
217         notifications. These notifications should then either be processed by the module or be
218         forwarded to other connectors.
219      */
220     class ActiveConnector
221         : public virtual Connector
222     {
223         typedef ppi::detail::Callback<>::type Callback;
224     public:
225         template <class Handler>
226         void onThrottle(Handler handler); ///< Register throttle notification handler
227                                         /**< The handler register here will be called, whenever a
228                                              throttle notification comes in. The \a handler argument
229                                              is either an arbitrary callable object or it is a
230                                              pointer-to-member to a member of the class which holds
231                                              this input. In the second case, the pointer will
232                                              automatically be bound to the containing instance.
233
234                                              \param[in] handler Handler to call on throttle
235                                                  notifications. */
236         void onThrottle();              ///< Clear throttle notification handler
237
238         template <class Handler>
239         void onUnthrottle(Handler handler); ///< Register unthrottle notification handler
240                                         /**< The handler register here will be called, whenever an
241                                              unthrottle notification comes in. The \a handler
242                                              argument is either an arbitrary callable object or it
243                                              is a pointer-to-member to a member of the class which
244                                              holds this input. In the second case, the pointer will
245                                              automatically be bound to the containing instance.
246
247                                              \param[in] handler Handler to call on unthrottle
248                                                  notifications. */
249         void onUnthrottle();            ///< Clear unthrottle notification handler
250
251         bool throttled() const;         ///< \c true, if peer() is throttled
252
253         PassiveConnector & peer() const;
254
255     protected:
256         ActiveConnector();
257
258     private:
259         // called by the peer() to forward throttling notifications
260         void notifyThrottle();
261         void notifyUnthrottle();
262
263         // called by ForwardingRoute to register a new route
264         void registerRoute(ForwardingRoute & route);
265
266         Callback throttleCallback_;
267         Callback unthrottleCallback_;
268
269         typedef std::vector<ForwardingRoute*> NotifyRoutes;
270         NotifyRoutes notifyRoutes_;
271
272         friend class senf::ppi::ForwardingRoute;
273         friend class PassiveConnector;
274     };
275
276     /** \brief Input connector base-class
277
278         An input connector receives packets. It may be either an ActiveConnector or a
279         PassiveConnector. An input connector contains a packet queue. This queue enables processing
280         packets in batches or generating multiple output packets from a single input packet. The
281         queues have the potential to greatly simplify the module implementations.
282
283         \implementation Which container to use?
284             \li list has good insertion and deletion properties on both ends but it costs a dynamic
285                 memory allocation for every insertion. A very good property is, that iterators stay
286                 valid across insertions/deletions
287             \li vector is fast and has good amortized dynamic allocation properties. However, it is
288                 quite unusable as a queue
289             \li deque has comparable dynamic allocation properties as vector but also has good
290                 insertion/removal properties on both ends.
291
292             So probably we will use a deque. I'd like a container which keeps iterators intact on
293             insertion/deletion but I believe that list is just to expensive since every packet will
294             be added to the queue before it can be processed.
295      */
296     class InputConnector
297         : public virtual Connector
298     {
299         typedef std::deque<Packet> Queue;
300     public:
301         typedef Queue::const_iterator queue_iterator; ///< Iterator type of the embedded queue
302         typedef Queue::size_type size_type; ///< Unsigned type for counting queue elements
303
304
305         Packet operator()();            ///< Get a packet
306                                         /**< This member is the primary method to access received
307                                              data. On passive connectors, this operator will just
308                                              dequeue a packet from the packet queue. If the
309                                              connector is active, the connector will request new
310                                              packets from the connected module. If the packet
311                                              request cannot be fulfilled, this is considered to be a
312                                              logic error in the module implementation and an
313                                              exception is raised. */
314
315         Packet read();                  ///< Alias for operator()()
316
317         OutputConnector & peer() const;
318
319         queue_iterator begin() const;   ///< Access queue begin (head)
320         queue_iterator end() const;     ///< Access queue past-the-end (tail)
321         Packet peek() const;            ///< Return head element from the queue
322
323         size_type queueSize() const;    ///< Return number of elements in the queue
324         bool empty() const;             ///< Return queueSize() == 0
325
326     protected:
327         InputConnector();
328
329     private:
330         void enqueue(Packet p);
331
332         virtual void v_requestEvent();
333         virtual void v_enqueueEvent();
334         virtual void v_dequeueEvent();
335
336         Queue queue_;
337
338         friend class OutputConnector;
339     };
340
341     /** \brief Output connector base-class
342
343         An output connector sends out packets. It may be either an ActiveConnector or a
344         PassiveConnector. An output connector does \e not have an built-in queueing, it relies on
345         the queueing of the connected input.
346      */
347     class OutputConnector
348         : public virtual Connector
349     {
350     public:
351         void operator()(Packet p);      ///< Send out a packet
352
353         void write(Packet p);           ///< Alias for operator()(Packet p)
354
355         InputConnector & peer() const;
356
357     protected:
358         OutputConnector();
359     };
360
361     /** \brief Combination of PassiveConnector and InputConnector
362
363         The GenericPassiveInput automatically controls the connectors throttling state using a
364         queueing discipline. The standard queueing discipline is ThresholdQueueing, which throttles
365         the connection whenever the queue length reaches the high threshold and unthrottles the
366         connection when the queue reaches the low threshold. The default queueing discipline is
367         <tt>ThresholdQueueing(1,0)</tt> which will throttle the input whenever the queue is
368         non-empty.
369      */
370     class GenericPassiveInput
371         : public PassiveConnector, public InputConnector,
372           public safe_bool<GenericPassiveInput>
373     {
374     public:
375         GenericActiveOutput & peer() const;
376
377         bool boolean_test() const;      ///< \c true, if ! empty()
378
379         template <class QDisc>
380         void qdisc(QDisc const & disc); ///< Change the queueing discipline
381                                         /**< The queueing discipline is a class which provides the
382                                              QueueingDiscipline interface.
383
384                                              \param[in] disc New queueing discipline */
385
386     protected:
387         GenericPassiveInput();
388
389     private:
390         void v_enqueueEvent();
391         void v_dequeueEvent();
392         void v_unthrottleEvent();
393
394         boost::scoped_ptr<QueueingDiscipline> qdisc_;
395     };
396
397     /** \brief Combination of PassiveConnector and OutputConnector
398      */
399     class GenericPassiveOutput
400         : public PassiveConnector, public OutputConnector,
401           public safe_bool<GenericPassiveOutput>
402     {
403     public:
404         GenericActiveInput & peer() const;
405
406         bool boolean_test() const;      ///< Always \c true
407
408         void connect(GenericActiveInput & target); ///< Internal: Use senf::ppi::connect() instead
409
410         friend class GenericActiveInput;
411
412     protected:
413         GenericPassiveOutput();
414
415     };
416
417     /** \brief Combination of ActiveConnector and InputConnector
418      */
419     class GenericActiveInput
420         : public ActiveConnector, public InputConnector,
421           public safe_bool<GenericActiveInput>
422     {
423     public:
424         GenericPassiveOutput & peer() const;
425
426         bool boolean_test() const;      ///< \c true, if ! empty() or ! throttled()
427
428         void request();                 ///< request more packets without dequeuing any packet
429
430     protected:
431         GenericActiveInput();
432
433     private:
434         void v_requestEvent();
435     };
436
437     /** \brief Combination of ActiveConnector and OutputConnector
438      */
439     class GenericActiveOutput
440         : public ActiveConnector, public OutputConnector,
441           public safe_bool<GenericActiveOutput>
442     {
443     public:
444         GenericPassiveInput & peer() const;
445
446         bool boolean_test() const;      ///< \c true if peer() is ! throttled()
447
448         void connect(GenericPassiveInput & target); ///< Internal: Use senf::ppi::connect() instead
449
450     protected:
451         GenericActiveOutput();
452
453     };
454
455 #ifndef DOXYGEN
456
457 #   define TypedConnector_Input read
458 #   define TypedConnector_Output write
459 #   define TypedConnector(pType, dir)                                                             \
460         template <class PacketType>                                                               \
461         class pType ## dir                                                                        \
462             : public Generic ## pType ## dir,                                                     \
463               private detail::Typed ## dir ## Mixin<pType ## dir <PacketType>, PacketType>        \
464         {                                                                                         \
465             typedef detail::Typed ## dir ## Mixin<pType ## dir <PacketType>, PacketType> mixin;   \
466         public:                                                                                   \
467             using mixin::operator();                                                              \
468             using mixin::TypedConnector_ ## dir ;                                                 \
469         private:                                                                                  \
470             virtual std::type_info const & packetTypeID()                                         \
471                 { return typeid(typename PacketType::type); }                                     \
472             friend class detail::Typed ## dir ## Mixin<pType ## dir <PacketType>, PacketType>;    \
473         };                                                                                        \
474         template <>                                                                               \
475         class pType ## dir <Packet> : public Generic ## pType ## dir                              \
476         {}
477
478     TypedConnector( Passive, Input  );
479     TypedConnector( Passive, Output );
480     TypedConnector( Active,  Input  );
481     TypedConnector( Active,  Output );
482
483 #   undef TypedConnector
484 #   undef TypedConnector_Input
485 #   undef TypedConnector_Output
486
487 #else
488
489     /** \brief Connector actively reading packets
490         
491         \tparam PacketType Type of packet to read. Defaults to senf::Packet
492
493         The ActiveInput connector template reads data actively from a connected module. This class
494         is completely implemented via it's base-class, GenericActiveInput, the only difference is
495         that read packets are returned as \a PacketType instead of generic senf::Packet references.
496
497         \see GenericActiveInput \n
498             senf::ppi::connector
499      */
500     template <class PacketType=Packet>
501     class ActiveInput : public GenericActiveInput
502     {
503     public:
504         PacketType operator()();        ///< Read packet
505                                         /**< \throws std::bad_cast, if the connector receives a
506                                              Packet which is not of type \a PacketType.
507                                              \returns newly read packet reference. */
508         PacketType read();              ///< Alias for operator()
509     };
510
511     /** \brief Connector passively receiving packets
512
513         \tparam PacketType Type of packet to read. Defaults to senf::Packet
514
515         The PassiveInput connector template receives packets sent to it from a connected
516         module. This class is completely implemented via it's base-class, GenericPassiveInput, the
517         only difference is that read packets are returned as \a PacketType instead of generic
518         senf::Packet references.
519
520         \see GenericPassiveInput \n
521             senf::ppi::connector
522      */
523     template <class PacketType=Packet>
524     class PassiveInput : public GenericPassiveInput
525     {
526     public:
527         PacketType operator()();        ///< Read packet
528                                         /**< \throws std::bad_cast, if the connector receives a
529                                              Packet which is not of type \a PacketType.
530                                              \returns newly read packet reference. */
531         PacketType read();              ///< Alias for operator()
532     };
533
534     /** \brief Connector actively sending packets
535
536         \tparam PacketType Type of packet to send. Defaults to senf::Packet
537
538         The ActiveOutput connector template sends data actively to a connected module. This class is
539         completely implemented via it's base-class, GenericActiveOutput, the only difference is that
540         it only sends packets of type \a PacketType.
541
542         \see GenericActiveOutput \n
543             senf::ppi::connector
544      */
545     template <class PacketType=Packet>
546     class ActiveOutput : public GenericActiveOutput
547     {
548     public:
549         operator()(PacketType packet);  ///< Send out a packet
550         write(PacketType packet);       ///< Alias for operator()
551     };
552
553     /** \brief Connector passively providing packets
554
555         \tparam PacketType Type of packet to send. Defaults to senf::Packet
556
557         The PassiveOutput connector template provides data passively to a connected module whenever
558         signaled. This class is completely implemented via it's base-class, GenericPassiveOutput, the
559         only difference is that it only sends packets of type \a PacketType.
560
561         \see GenericPassiveOutput \n
562             senf::ppi::connector
563      */
564     template <class PacketType=Packet>
565     class PassiveOutput : public GenericPassiveOutput
566     {
567     public:
568         operator()(PacketType packet);  ///< Send out a packet
569         write(PacketType packet);       ///< Alias for operator()
570     };
571
572 #endif
573
574 }}}
575
576 ///////////////////////////////hh.e////////////////////////////////////////
577 #include "Connectors.cci"
578 //#include "Connectors.ct"
579 #include "Connectors.cti"
580 #endif
581
582 \f
583 // Local Variables:
584 // mode: c++
585 // fill-column: 100
586 // c-file-style: "senf"
587 // indent-tabs-mode: nil
588 // ispell-local-dictionary: "american"
589 // compile-command: "scons -u test"
590 // comment-column: 40
591 // End: