730 lines
22 KiB
C++
730 lines
22 KiB
C++
// -*- c-basic-offset: 4; related-file-name: "../../lib/element.cc" -*-
|
|
#ifndef CLICK_ELEMENT_HH
|
|
#define CLICK_ELEMENT_HH
|
|
#include <click/glue.hh>
|
|
#include <click/vector.hh>
|
|
#include <click/string.hh>
|
|
#include <click/packet.hh>
|
|
#include <click/handler.hh>
|
|
CLICK_DECLS
|
|
class Router;
|
|
class Master;
|
|
class RouterThread;
|
|
class Task;
|
|
class Timer;
|
|
class NotifierSignal;
|
|
class Element;
|
|
class ErrorHandler;
|
|
class Bitvector;
|
|
class EtherAddress;
|
|
|
|
/** @file <click/element.hh>
|
|
* @brief Click's Element class.
|
|
*/
|
|
|
|
#ifndef CLICK_ELEMENT_DEPRECATED
|
|
# define CLICK_ELEMENT_DEPRECATED CLICK_DEPRECATED
|
|
#endif
|
|
|
|
class Element { public:
|
|
|
|
Element();
|
|
virtual ~Element();
|
|
static int nelements_allocated;
|
|
|
|
// RUNTIME
|
|
virtual void push(int port, Packet *p);
|
|
virtual Packet *pull(int port) CLICK_WARN_UNUSED_RESULT;
|
|
virtual Packet *simple_action(Packet *p);
|
|
|
|
virtual bool run_task(Task *task); // return true iff did useful work
|
|
virtual void run_timer(Timer *timer);
|
|
#if CLICK_USERLEVEL
|
|
enum { SELECT_READ = 1, SELECT_WRITE = 2 };
|
|
virtual void selected(int fd, int mask);
|
|
virtual void selected(int fd);
|
|
#endif
|
|
|
|
inline void checked_output_push(int port, Packet *p) const;
|
|
inline Packet* checked_input_pull(int port) const;
|
|
|
|
// ELEMENT CHARACTERISTICS
|
|
virtual const char *class_name() const = 0;
|
|
|
|
virtual const char *port_count() const;
|
|
static const char PORTS_0_0[];
|
|
static const char PORTS_0_1[];
|
|
static const char PORTS_1_0[];
|
|
static const char PORTS_1_1[];
|
|
static const char PORTS_1_1X2[];
|
|
|
|
virtual const char *processing() const;
|
|
static const char AGNOSTIC[];
|
|
static const char PUSH[];
|
|
static const char PULL[];
|
|
static const char PUSH_TO_PULL[];
|
|
static const char PULL_TO_PUSH[];
|
|
static const char PROCESSING_A_AH[];
|
|
|
|
virtual const char *flow_code() const;
|
|
static const char COMPLETE_FLOW[];
|
|
|
|
virtual const char *flags() const;
|
|
int flag_value(int flag) const;
|
|
|
|
virtual void *cast(const char *name);
|
|
virtual void *port_cast(bool isoutput, int port, const char *name);
|
|
|
|
// CONFIGURATION, INITIALIZATION, AND CLEANUP
|
|
enum ConfigurePhase {
|
|
CONFIGURE_PHASE_FIRST = 0,
|
|
CONFIGURE_PHASE_INFO = 20,
|
|
CONFIGURE_PHASE_PRIVILEGED = 90,
|
|
CONFIGURE_PHASE_DEFAULT = 100,
|
|
CONFIGURE_PHASE_LAST = 2000
|
|
};
|
|
virtual int configure_phase() const;
|
|
|
|
virtual int configure(Vector<String> &conf, ErrorHandler *errh);
|
|
|
|
virtual void add_handlers();
|
|
|
|
virtual int initialize(ErrorHandler *errh);
|
|
|
|
virtual void take_state(Element *old_element, ErrorHandler *errh);
|
|
virtual Element *hotswap_element() const;
|
|
|
|
enum CleanupStage {
|
|
CLEANUP_NO_ROUTER,
|
|
CLEANUP_BEFORE_CONFIGURE = CLEANUP_NO_ROUTER,
|
|
CLEANUP_CONFIGURE_FAILED,
|
|
CLEANUP_CONFIGURED,
|
|
CLEANUP_INITIALIZE_FAILED,
|
|
CLEANUP_INITIALIZED,
|
|
CLEANUP_ROUTER_INITIALIZED,
|
|
CLEANUP_MANUAL
|
|
};
|
|
virtual void cleanup(CleanupStage stage);
|
|
|
|
static inline void static_initialize();
|
|
static inline void static_cleanup();
|
|
|
|
// ELEMENT ROUTER CONNECTIONS
|
|
String name() const;
|
|
virtual String declaration() const;
|
|
|
|
inline Router *router() const;
|
|
inline int eindex() const;
|
|
inline int eindex(Router *r) const;
|
|
|
|
/** @brief Return the element's master. */
|
|
inline Master *master() const;
|
|
|
|
inline void attach_router(Router *r, int eindex) {
|
|
assert(!_router);
|
|
_router = r;
|
|
_eindex = eindex;
|
|
}
|
|
|
|
// INPUTS AND OUTPUTS
|
|
inline int nports(bool isoutput) const;
|
|
inline int ninputs() const;
|
|
inline int noutputs() const;
|
|
|
|
class Port;
|
|
inline const Port &port(bool isoutput, int port) const;
|
|
inline const Port &input(int port) const;
|
|
inline const Port &output(int port) const;
|
|
|
|
inline bool port_active(bool isoutput, int port) const;
|
|
inline bool input_is_push(int port) const;
|
|
inline bool input_is_pull(int port) const;
|
|
inline bool output_is_push(int port) const;
|
|
inline bool output_is_pull(int port) const;
|
|
void port_flow(bool isoutput, int port, Bitvector*) const;
|
|
|
|
// LIVE RECONFIGURATION
|
|
String configuration() const;
|
|
|
|
virtual bool can_live_reconfigure() const;
|
|
virtual int live_reconfigure(Vector<String>&, ErrorHandler*);
|
|
|
|
RouterThread *home_thread() const;
|
|
|
|
#if CLICK_USERLEVEL
|
|
// SELECT
|
|
int add_select(int fd, int mask);
|
|
int remove_select(int fd, int mask);
|
|
#endif
|
|
|
|
// HANDLERS
|
|
void add_read_handler(const String &name, ReadHandlerCallback read_callback, const void *user_data = 0, uint32_t flags = 0);
|
|
void add_read_handler(const String &name, ReadHandlerCallback read_callback, int user_data, uint32_t flags = 0);
|
|
void add_read_handler(const char *name, ReadHandlerCallback read_callback, int user_data = 0, uint32_t flags = 0);
|
|
void add_write_handler(const String &name, WriteHandlerCallback write_callback, const void *user_data = 0, uint32_t flags = 0);
|
|
void add_write_handler(const String &name, WriteHandlerCallback write_callback, int user_data, uint32_t flags = 0);
|
|
void add_write_handler(const char *name, WriteHandlerCallback write_callback, int user_data = 0, uint32_t flags = 0);
|
|
void set_handler(const String &name, int flags, HandlerCallback callback, const void *read_user_data = 0, const void *write_user_data = 0);
|
|
void set_handler(const String &name, int flags, HandlerCallback callback, int read_user_data, int write_user_data = 0);
|
|
void set_handler(const char *name, int flags, HandlerCallback callback, int read_user_data = 0, int write_user_data = 0);
|
|
int set_handler_flags(const String &name, int set_flags, int clear_flags = 0);
|
|
enum { TASKHANDLER_WRITE_SCHEDULED = 1,
|
|
TASKHANDLER_WRITE_TICKETS = 2,
|
|
TASKHANDLER_WRITE_HOME_THREAD = 4,
|
|
TASKHANDLER_WRITE_ALL = 7,
|
|
TASKHANDLER_DEFAULT = 6 };
|
|
void add_task_handlers(Task *task, NotifierSignal *signal, int flags, const String &prefix = String());
|
|
inline void add_task_handlers(Task *task, NotifierSignal *signal, const String &prefix = String()) {
|
|
add_task_handlers(task, signal, TASKHANDLER_DEFAULT, prefix);
|
|
}
|
|
inline void add_task_handlers(Task *task, const String &prefix = String()) {
|
|
add_task_handlers(task, 0, TASKHANDLER_DEFAULT, prefix);
|
|
}
|
|
|
|
void add_data_handlers(const char *name, int flags, uint8_t *data);
|
|
void add_data_handlers(const char *name, int flags, bool *data);
|
|
void add_data_handlers(const char *name, int flags, uint16_t *data);
|
|
void add_data_handlers(const char *name, int flags, int *data);
|
|
void add_data_handlers(const char *name, int flags, unsigned *data);
|
|
void add_data_handlers(const char *name, int flags, atomic_uint32_t *data);
|
|
void add_data_handlers(const char *name, int flags, long *data);
|
|
void add_data_handlers(const char *name, int flags, unsigned long *data);
|
|
#if HAVE_LONG_LONG
|
|
void add_data_handlers(const char *name, int flags, long long *data);
|
|
void add_data_handlers(const char *name, int flags, unsigned long long *data);
|
|
#endif
|
|
void add_net_order_data_handlers(const char *name, int flags, uint16_t *data);
|
|
void add_net_order_data_handlers(const char *name, int flags, uint32_t *data);
|
|
#if HAVE_FLOAT_TYPES
|
|
void add_data_handlers(const char *name, int flags, double *data);
|
|
#endif
|
|
void add_data_handlers(const char *name, int flags, String *data);
|
|
void add_data_handlers(const char *name, int flags, IPAddress *data);
|
|
void add_data_handlers(const char *name, int flags, EtherAddress *data);
|
|
void add_data_handlers(const char *name, int flags, Timestamp *data, bool is_interval = false);
|
|
|
|
static String read_positional_handler(Element*, void*);
|
|
static String read_keyword_handler(Element*, void*);
|
|
static int reconfigure_positional_handler(const String&, Element*, void*, ErrorHandler*);
|
|
static int reconfigure_keyword_handler(const String&, Element*, void*, ErrorHandler*);
|
|
|
|
virtual int llrpc(unsigned command, void* arg);
|
|
int local_llrpc(unsigned command, void* arg);
|
|
|
|
class Port { public:
|
|
|
|
inline bool active() const;
|
|
inline Element* element() const;
|
|
inline int port() const;
|
|
|
|
inline void push(Packet* p) const;
|
|
inline Packet* pull() const;
|
|
|
|
#if CLICK_STATS >= 1
|
|
unsigned npackets() const { return _packets; }
|
|
#endif
|
|
|
|
inline void assign(bool isoutput, Element *e, int port);
|
|
|
|
private:
|
|
|
|
Element* _e;
|
|
int _port;
|
|
#if HAVE_BOUND_PORT_TRANSFER
|
|
union {
|
|
void (*push)(Element *e, int port, Packet *p);
|
|
Packet *(*pull)(Element *e, int port);
|
|
} _bound;
|
|
#endif
|
|
|
|
#if CLICK_STATS >= 1
|
|
mutable unsigned _packets; // How many packets have we moved?
|
|
#endif
|
|
#if CLICK_STATS >= 2
|
|
Element* _owner; // Whose input or output are we?
|
|
#endif
|
|
|
|
inline Port();
|
|
inline void assign(bool isoutput, Element *owner, Element *e, int port);
|
|
|
|
friend class Element;
|
|
|
|
};
|
|
|
|
// DEPRECATED
|
|
/** @cond never */
|
|
String id() const CLICK_DEPRECATED;
|
|
String landmark() const CLICK_DEPRECATED;
|
|
/** @endcond never */
|
|
|
|
private:
|
|
|
|
enum { INLINE_PORTS = 4 };
|
|
|
|
Port* _ports[2];
|
|
Port _inline_ports[INLINE_PORTS];
|
|
|
|
int _nports[2];
|
|
|
|
Router* _router;
|
|
int _eindex;
|
|
|
|
#if CLICK_STATS >= 2
|
|
// STATISTICS
|
|
unsigned _xfer_calls; // Push and pull calls into this element.
|
|
click_cycles_t _xfer_own_cycles; // Cycles spent in self from push and pull.
|
|
click_cycles_t _child_cycles; // Cycles spent in children.
|
|
|
|
unsigned _task_calls; // Calls to tasks owned by this element.
|
|
click_cycles_t _task_own_cycles; // Cycles spent in self from tasks.
|
|
|
|
unsigned _timer_calls; // Calls to timers owned by this element.
|
|
click_cycles_t _timer_own_cycles; // Cycles spent in self from timers.
|
|
|
|
inline void reset_cycles() {
|
|
_xfer_calls = _task_calls = _timer_calls = 0;
|
|
_xfer_own_cycles = _task_own_cycles = _timer_own_cycles = _child_cycles = 0;
|
|
}
|
|
static String read_cycles_handler(Element *, void *);
|
|
static int write_cycles_handler(const String &, Element *, void *, ErrorHandler *);
|
|
#endif
|
|
|
|
Element(const Element &);
|
|
Element &operator=(const Element &);
|
|
|
|
// METHODS USED BY ROUTER
|
|
int set_nports(int, int);
|
|
int notify_nports(int, int, ErrorHandler *);
|
|
enum Processing { VAGNOSTIC, VPUSH, VPULL };
|
|
static int next_processing_code(const char*& p, ErrorHandler* errh);
|
|
void processing_vector(int* input_codes, int* output_codes, ErrorHandler*) const;
|
|
|
|
void initialize_ports(const int* input_codes, const int* output_codes);
|
|
int connect_port(bool isoutput, int port, Element*, int);
|
|
|
|
static String read_handlers_handler(Element *e, void *user_data);
|
|
void add_default_handlers(bool writable_config);
|
|
inline void add_data_handlers(const char *name, int flags, HandlerCallback callback, void *data);
|
|
|
|
friend class Router;
|
|
#if CLICK_STATS >= 2
|
|
friend class Task;
|
|
friend class Master;
|
|
friend class TimerSet;
|
|
# if CLICK_USERLEVEL
|
|
friend class SelectSet;
|
|
# endif
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
/** @brief Initialize static data for this element class.
|
|
*
|
|
* Place initialization code for an element class's shared global state in the
|
|
* static_initialize() static member function. (For example, the IPFilter
|
|
* element class uses static_initialize() to set up various parsing tables.)
|
|
* Click drivers will call this function when the element code is loaded,
|
|
* before any elements of the class are created.
|
|
*
|
|
* static_initialize functions are called in an arbitrary and unpredictable
|
|
* order (not, for example, the configure_phase() order). Element authors are
|
|
* responsible for handling static initialization dependencies.
|
|
*
|
|
* For Click to find a static_initialize declaration, it must appear inside
|
|
* the element class's class declaration on its own line and have the
|
|
* following prototype:
|
|
*
|
|
* @code
|
|
* static void static_initialize();
|
|
* @endcode
|
|
*
|
|
* It must also have public accessibility.
|
|
*
|
|
* @note In most cases you should also define a static_cleanup() function to
|
|
* clean up state initialized by static_initialize().
|
|
*
|
|
* @sa Element::static_cleanup
|
|
*/
|
|
inline void
|
|
Element::static_initialize()
|
|
{
|
|
}
|
|
|
|
/** @brief Clean up static data for this element class.
|
|
*
|
|
* Place cleanup code for an element class's shared global state in the
|
|
* static_cleanup() static member function. Click drivers will call this
|
|
* function before unloading the element code.
|
|
*
|
|
* static_cleanup functions are called in an arbitrary and unpredictable order
|
|
* (not, for example, the configure_phase() order, and not the reverse of the
|
|
* static_initialize order). Element authors are responsible for handling
|
|
* static cleanup dependencies.
|
|
*
|
|
* For Click to find a static_cleanup declaration, it must appear inside the
|
|
* element class's class declaration on its own line and have the following
|
|
* prototype:
|
|
*
|
|
* @code
|
|
* static void static_cleanup();
|
|
* @endcode
|
|
*
|
|
* It must also have public accessibility.
|
|
*
|
|
* @sa Element::static_initialize
|
|
*/
|
|
inline void
|
|
Element::static_cleanup()
|
|
{
|
|
}
|
|
|
|
/** @brief Return the element's router. */
|
|
inline Router*
|
|
Element::router() const
|
|
{
|
|
return _router;
|
|
}
|
|
|
|
/** @brief Return the element's index within its router.
|
|
* @invariant this == router()->element(eindex())
|
|
*/
|
|
inline int
|
|
Element::eindex() const
|
|
{
|
|
return _eindex;
|
|
}
|
|
|
|
/** @brief Return the element's index within router @a r.
|
|
*
|
|
* Returns -1 if @a r != router(). */
|
|
inline int
|
|
Element::eindex(Router* r) const
|
|
{
|
|
return (router() == r ? _eindex : -1);
|
|
}
|
|
|
|
/** @brief Return the number of input or output ports.
|
|
* @param isoutput false for input ports, true for output ports */
|
|
inline int
|
|
Element::nports(bool isoutput) const
|
|
{
|
|
return _nports[isoutput];
|
|
}
|
|
|
|
/** @brief Return the number of input ports. */
|
|
inline int
|
|
Element::ninputs() const
|
|
{
|
|
return _nports[0];
|
|
}
|
|
|
|
/** @brief Return the number of output ports. */
|
|
inline int
|
|
Element::noutputs() const
|
|
{
|
|
return _nports[1];
|
|
}
|
|
|
|
/** @brief Return one of the element's ports.
|
|
* @param isoutput false for input ports, true for output ports
|
|
* @param port port number
|
|
*
|
|
* An assertion fails if @a p is out of range. */
|
|
inline const Element::Port&
|
|
Element::port(bool isoutput, int port) const
|
|
{
|
|
assert((unsigned) port < (unsigned) _nports[isoutput]);
|
|
return _ports[isoutput][port];
|
|
}
|
|
|
|
/** @brief Return one of the element's input ports.
|
|
* @param port port number
|
|
*
|
|
* An assertion fails if @a port is out of range.
|
|
*
|
|
* @sa Port, port */
|
|
inline const Element::Port&
|
|
Element::input(int port) const
|
|
{
|
|
return Element::port(false, port);
|
|
}
|
|
|
|
/** @brief Return one of the element's output ports.
|
|
* @param port port number
|
|
*
|
|
* An assertion fails if @a port is out of range.
|
|
*
|
|
* @sa Port, port */
|
|
inline const Element::Port&
|
|
Element::output(int port) const
|
|
{
|
|
return Element::port(true, port);
|
|
}
|
|
|
|
/** @brief Check whether a port is active.
|
|
* @param isoutput false for input ports, true for output ports
|
|
* @param port port number
|
|
*
|
|
* Returns true iff @a port is in range and @a port is active. Push outputs
|
|
* and pull inputs are active; pull outputs and push inputs are not.
|
|
*
|
|
* @sa Element::Port::active */
|
|
inline bool
|
|
Element::port_active(bool isoutput, int port) const
|
|
{
|
|
return (unsigned) port < (unsigned) nports(isoutput)
|
|
&& _ports[isoutput][port].active();
|
|
}
|
|
|
|
/** @brief Check whether output @a port is push.
|
|
*
|
|
* Returns true iff output @a port exists and is push. @sa port_active */
|
|
inline bool
|
|
Element::output_is_push(int port) const
|
|
{
|
|
return port_active(true, port);
|
|
}
|
|
|
|
/** @brief Check whether output @a port is pull.
|
|
*
|
|
* Returns true iff output @a port exists and is pull. */
|
|
inline bool
|
|
Element::output_is_pull(int port) const
|
|
{
|
|
return (unsigned) port < (unsigned) nports(true)
|
|
&& !_ports[1][port].active();
|
|
}
|
|
|
|
/** @brief Check whether input @a port is pull.
|
|
*
|
|
* Returns true iff input @a port exists and is pull. @sa port_active */
|
|
inline bool
|
|
Element::input_is_pull(int port) const
|
|
{
|
|
return port_active(false, port);
|
|
}
|
|
|
|
/** @brief Check whether input @a port is push.
|
|
*
|
|
* Returns true iff input @a port exists and is push. */
|
|
inline bool
|
|
Element::input_is_push(int port) const
|
|
{
|
|
return (unsigned) port < (unsigned) nports(false)
|
|
&& !_ports[0][port].active();
|
|
}
|
|
|
|
#if CLICK_STATS >= 2
|
|
# define PORT_ASSIGN(o) _packets = 0; _owner = (o)
|
|
#elif CLICK_STATS >= 1
|
|
# define PORT_ASSIGN(o) _packets = 0; (void) (o)
|
|
#else
|
|
# define PORT_ASSIGN(o) (void) (o)
|
|
#endif
|
|
|
|
inline
|
|
Element::Port::Port()
|
|
: _e(0), _port(-2)
|
|
{
|
|
PORT_ASSIGN(0);
|
|
}
|
|
|
|
inline void
|
|
Element::Port::assign(bool isoutput, Element *e, int port)
|
|
{
|
|
_e = e;
|
|
_port = port;
|
|
(void) isoutput;
|
|
#if HAVE_BOUND_PORT_TRANSFER
|
|
if (e) {
|
|
if (isoutput) {
|
|
void (Element::*pusher)(int, Packet *) = &Element::push;
|
|
_bound.push = (void (*)(Element *, int, Packet *)) (e->*pusher);
|
|
} else {
|
|
Packet *(Element::*puller)(int) = &Element::pull;
|
|
_bound.pull = (Packet *(*)(Element *, int)) (e->*puller);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
inline void
|
|
Element::Port::assign(bool isoutput, Element *owner, Element *e, int port)
|
|
{
|
|
PORT_ASSIGN(owner);
|
|
assign(isoutput, e, port);
|
|
}
|
|
|
|
/** @brief Returns whether this port is active (a push output or a pull input).
|
|
*
|
|
* @sa Element::port_active
|
|
*/
|
|
inline bool
|
|
Element::Port::active() const
|
|
{
|
|
return _port >= 0;
|
|
}
|
|
|
|
/** @brief Returns the element connected to this active port.
|
|
*
|
|
* Returns 0 if this port is not active(). */
|
|
inline Element*
|
|
Element::Port::element() const
|
|
{
|
|
return _e;
|
|
}
|
|
|
|
/** @brief Returns the port number of the port connected to this active port.
|
|
*
|
|
* Returns < 0 if this port is not active(). */
|
|
inline int
|
|
Element::Port::port() const
|
|
{
|
|
return _port;
|
|
}
|
|
|
|
/** @brief Push packet @a p over this port.
|
|
*
|
|
* Pushes packet @a p downstream through the router configuration by passing
|
|
* it to the next element's @link Element::push() push() @endlink function.
|
|
* Returns when the rest of the router finishes processing @a p.
|
|
*
|
|
* This port must be an active() push output port. Usually called from
|
|
* element code like @link Element::output output(i) @endlink .push(p).
|
|
*
|
|
* When element code calls Element::Port::push(@a p), it relinquishes control
|
|
* of packet @a p. When push() returns, @a p may have been altered or even
|
|
* freed by downstream elements. Thus, you must not use @a p after pushing it
|
|
* downstream. To push a copy and keep a copy, see Packet::clone().
|
|
*
|
|
* output(i).push(p) basically behaves like the following code, although it
|
|
* maintains additional statistics depending on how CLICK_STATS is defined:
|
|
*
|
|
* @code
|
|
* output(i).element()->push(output(i).port(), p);
|
|
* @endcode
|
|
*/
|
|
inline void
|
|
Element::Port::push(Packet* p) const
|
|
{
|
|
assert(_e && p);
|
|
#if CLICK_STATS >= 1
|
|
++_packets;
|
|
#endif
|
|
#if CLICK_STATS >= 2
|
|
++_e->input(_port)._packets;
|
|
click_cycles_t start_cycles = click_get_cycles(),
|
|
start_child_cycles = _e->_child_cycles;
|
|
# if HAVE_BOUND_PORT_TRANSFER
|
|
_bound.push(_e, _port, p);
|
|
# else
|
|
_e->push(_port, p);
|
|
# endif
|
|
click_cycles_t all_delta = click_get_cycles() - start_cycles,
|
|
own_delta = all_delta - (_e->_child_cycles - start_child_cycles);
|
|
_e->_xfer_calls += 1;
|
|
_e->_xfer_own_cycles += own_delta;
|
|
_owner->_child_cycles += all_delta;
|
|
#else
|
|
# if HAVE_BOUND_PORT_TRANSFER
|
|
_bound.push(_e, _port, p);
|
|
# else
|
|
_e->push(_port, p);
|
|
# endif
|
|
#endif
|
|
}
|
|
|
|
/** @brief Pull a packet over this port and return it.
|
|
*
|
|
* Pulls a packet from upstream in the router configuration by calling the
|
|
* previous element's @link Element::pull() pull() @endlink function. When
|
|
* the router finishes processing, returns the result.
|
|
*
|
|
* This port must be an active() pull input port. Usually called from element
|
|
* code like @link Element::input input(i) @endlink .pull().
|
|
*
|
|
* input(i).pull() basically behaves like the following code, although it
|
|
* maintains additional statistics depending on how CLICK_STATS is defined:
|
|
*
|
|
* @code
|
|
* input(i).element()->pull(input(i).port())
|
|
* @endcode
|
|
*/
|
|
inline Packet*
|
|
Element::Port::pull() const
|
|
{
|
|
assert(_e);
|
|
#if CLICK_STATS >= 2
|
|
click_cycles_t start_cycles = click_get_cycles(),
|
|
old_child_cycles = _e->_child_cycles;
|
|
# if HAVE_BOUND_PORT_TRANSFER
|
|
Packet *p = _bound.pull(_e, _port);
|
|
# else
|
|
Packet *p = _e->pull(_port);
|
|
# endif
|
|
if (p)
|
|
_e->output(_port)._packets += 1;
|
|
click_cycles_t all_delta = click_get_cycles() - start_cycles,
|
|
own_delta = all_delta - (_e->_child_cycles - old_child_cycles);
|
|
_e->_xfer_calls += 1;
|
|
_e->_xfer_own_cycles += own_delta;
|
|
_owner->_child_cycles += all_delta;
|
|
#else
|
|
# if HAVE_BOUND_PORT_TRANSFER
|
|
Packet *p = _bound.pull(_e, _port);
|
|
# else
|
|
Packet *p = _e->pull(_port);
|
|
# endif
|
|
#endif
|
|
#if CLICK_STATS >= 1
|
|
if (p)
|
|
++_packets;
|
|
#endif
|
|
return p;
|
|
}
|
|
|
|
/** @brief Push packet @a p to output @a port, or kill it if @a port is out of
|
|
* range.
|
|
*
|
|
* @param port output port number
|
|
* @param p packet to push
|
|
*
|
|
* If @a port is in range (>= 0 and < noutputs()), then push packet @a p
|
|
* forward using output(@a port).push(@a p). Otherwise, kill @a p with @a p
|
|
* ->kill().
|
|
*
|
|
* @note It is invalid to call checked_output_push() on a pull output @a port.
|
|
*/
|
|
inline void
|
|
Element::checked_output_push(int port, Packet* p) const
|
|
{
|
|
if ((unsigned) port < (unsigned) noutputs())
|
|
_ports[1][port].push(p);
|
|
else
|
|
p->kill();
|
|
}
|
|
|
|
/** @brief Pull a packet from input @a port, or return 0 if @a port is out of
|
|
* range.
|
|
*
|
|
* @param port input port number
|
|
*
|
|
* If @a port is in range (>= 0 and < ninputs()), then return the result
|
|
* of input(@a port).pull(). Otherwise, return null.
|
|
*
|
|
* @note It is invalid to call checked_input_pull() on a push input @a port.
|
|
*/
|
|
inline Packet*
|
|
Element::checked_input_pull(int port) const
|
|
{
|
|
if ((unsigned) port < (unsigned) ninputs())
|
|
return _ports[0][port].pull();
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
#undef PORT_ASSIGN
|
|
CLICK_ENDDECLS
|
|
#endif
|