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