// -*- c-basic-offset: 4; related-file-name: "../include/click/notifier.hh" -*-
/*
* notifier.{cc,hh} -- activity notification
* Eddie Kohler
*
* Copyright (c) 2002 International Computer Science Institute
* Copyright (c) 2004-2005 Regents of the University of California
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, subject to the conditions
* listed in the Click LICENSE file. These conditions include: you must
* preserve this copyright notice, and you cannot mention the copyright
* holders in advertising related to the Software without their permission.
* The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
* notice is a summary of the Click LICENSE file; the license in that file is
* legally binding.
*/
#include <click/config.h>
#include <click/notifier.hh>
#include <click/router.hh>
#include <click/element.hh>
#include <click/elemfilter.hh>
#include <click/bitvector.hh>
CLICK_DECLS
// should be const, but we need to explicitly initialize it
atomic_uint32_t NotifierSignal::static_value;
const char Notifier::EMPTY_NOTIFIER[] = "Notifier.EMPTY";
const char Notifier::FULL_NOTIFIER[] = "Notifier.FULL";
/** @class NotifierSignal
* @brief An activity signal.
*
* Activity signals in Click let one element determine whether another element
* is active. For example, consider an element @e X pulling from a @e Queue.
* If the @e Queue is empty, there's no point in @e X trying to pull from it.
* Thus, the @e Queue has an activity signal that's active when it contains
* packets and inactive when it's empty. @e X can check the activity signal
* before pulling, and do something else if it's inactive. Combined with the
* sleep/wakeup functionality of ActiveNotifier, this can greatly reduce CPU
* load due to polling.
*
* A "basic activity signal" is essentially a bit that's either on or off.
* When it's on, the signal is active. NotifierSignal can represent @e
* derived activity signals as well. A derived signal combines information
* about @e N basic signals using the following invariant: If any of the basic
* signals is active, then the derived signal is also active. There are no
* other guarantees; in particular, the derived signal might be active even if
* @e none of the basic signals are active.
*
* Click elements construct NotifierSignal objects in four ways:
*
* - idle_signal() returns a signal that's never active.
* - busy_signal() returns a signal that's always active.
* - Router::new_notifier_signal() creates a new basic signal. This method
* should be preferred to NotifierSignal's own constructors.
* - operator+(NotifierSignal, const NotifierSignal &) creates a derived signal.
*/
/** @class Notifier
* @brief A basic activity signal and notification provider.
*
* The Notifier class represents a basic activity signal associated with an
* element. Elements that contain a Notifier object will override
* Element::cast() to return that Notifier when given the proper name. This
* lets other parts of the configuration find the Notifiers. See
* upstream_empty_signal() and downstream_full_signal().
*
* The ActiveNotifier class, which derives from Notifier, can wake up clients
* when its activity signal becomes active.
*/
/** @class ActiveNotifier
* @brief A basic activity signal and notification provider that can
* reschedule any dependent Task objects.
*
* ActiveNotifier, whose base class is Notifier, combines a basic activity
* signal with the ability to wake up any dependent Task objects when that
* signal becomes active. Notifier clients are called @e listeners. Each
* listener corresponds to a Task object. The listener generally goes to
* sleep -- i.e., becomes unscheduled -- when it runs out of work and the
* corresponding activity signal is inactive. The ActiveNotifier class will
* wake up the listener when it becomes active by rescheduling the relevant
* Task.
*
* Elements that contain ActiveNotifier objects will generally override
* Element::cast(), allowing other parts of the configuration to find the
* Notifiers.
*/
/** @brief Initialize the NotifierSignal implementation.
*
* This function must be called before NotifierSignal functionality is used.
* It is safe to call it multiple times.
*
* @note Elements don't need to worry about static_initialize(); Click drivers
* have already called it for you.
*/
void
NotifierSignal::static_initialize()
{
static_value = TRUE_MASK | OVERDERIVED_MASK;
}
/** @brief Make this signal derived by adding information from @a a.
* @param a the signal to add
*
* Creates a derived signal that combines information from this signal and @a
* a. Equivalent to "*this = (*this + @a a)".
*
* @sa operator+(NotifierSignal, const NotifierSignal&)
*/
NotifierSignal&
NotifierSignal::operator+=(const NotifierSignal& a)
{
if (!_mask)
_value = a._value;
// preserve busy_signal(); adding other incompatible signals
// leads to overderived_signal()
if (*this == busy_signal())
/* do nothing */;
else if (a == busy_signal())
*this = a;
else if (_value == a._value || !a._mask)
_mask |= a._mask;
else
*this = overderived_signal();
return *this;
}
/** @brief Return a human-readable representation of the signal.
*
* Only useful for signal debugging.
*/
String
NotifierSignal::unparse() const
{
char buf[40];
sprintf(buf, "%p/%x:%x", _value, _mask, (*_value)&_mask);
return String(buf);
}
/** @brief Destruct a Notifier. */
Notifier::~Notifier()
{
}
/** @brief Called to register a listener with this Notifier.
* @param task the listener's Task
*
* This notifier should register @a task as a listener, if appropriate.
* Later, when the signal is activated, the Notifier should reschedule @a task
* along with the other listeners. Not all types of Notifier need to provide
* this functionality, however. The default implementation does nothing.
*/
int
Notifier::add_listener(Task* task)
{
(void) task;
return 0;
}
/** @brief Called to unregister a listener with this Notifier.
* @param task the listener's Task
*
* Undoes the effect of any prior add_listener(@a task). Should do nothing if
* @a task was never added. The default implementation does nothing.
*/
void
Notifier::remove_listener(Task* task)
{
(void) task;
}
/** @brief Initialize the associated NotifierSignal, if necessary.
* @param r the associated router
*
* Initialize the Notifier's associated NotifierSignal by calling @a r's
* Router::new_notifier_signal() method, obtaining a new basic activity
* signal. Does nothing if the signal is already initialized.
*/
int
Notifier::initialize(Router* r)
{
if (!_signal.initialized())
return r->new_notifier_signal(_signal);
else
return 0;
}
/** @brief Construct an ActiveNotifier.
* @param search_op controls notifier path search
*
* Constructs an ActiveNotifier object, analogous to the
* Notifier::Notifier(SearchOp) constructor. (See that constructor for more
* informaiton on @a search_op.)
*/
ActiveNotifier::ActiveNotifier(SearchOp search_op)
: Notifier(search_op), _listener1(0), _listeners(0)
{
}
/** @brief Destroy an ActiveNotifier. */
ActiveNotifier::~ActiveNotifier()
{
delete[] _listeners;
}
/** @brief Add a listener to this notifier.
* @param task the listener to add
*
* Adds @a task to this element's listener list (the clients interested in
* notification). Whenever the ActiveNotifier activates its signal, @a task
* will be rescheduled.
*/
int
ActiveNotifier::add_listener(Task* task)
{
if (!_listeners && (!_listener1 || _listener1 == task)) {
_listener1 = task;
return 0;
} else if (task == 0)
return 0;
int n = (_listener1 ? 1 : 0);
if (!_listener1)
for (Task **t = _listeners; *t; t++) {
if (*t == task)
return 0;
n++;
}
Task** old_list = (_listener1 ? &_listener1 : _listeners);
Task** new_list = new Task *[n + 2];
if (!new_list) {
click_chatter("out of memory in Notifier::add_listener!");
return -1;
}
memcpy(new_list, old_list, sizeof(Task *) * 1);
new_list[n] = task;
new_list[n + 1] = _listener1 = 0;
delete[] _listeners;
_listeners = new_list;
return 0;
}
/** @brief Remove a listener from this notifier.
* @param task the listener to remove
*
* Removes @a task from this element's listener list (the clients interested
* in notification). @a task will not be rescheduled when the Notifier is
* activated.
*/
void
ActiveNotifier::remove_listener(Task* task)
{
if (!task)
/* nada */;
else if (_listener1 == task)
_listener1 = 0;
else if (_listeners) {
int n = 0, which = -1;
for (Task** l = _listeners; *l; l++) {
if (*l == task)
which = n;
n++;
}
if (which >= 0) {
_listeners[which] = _listeners[n - 1];
_listeners[n - 1] = 0;
}
}
}
/** @brief Return the listener list.
* @param[out] v collects listener tasks
*
* Pushes all listener Task objects onto the end of @a v.
*/
void
ActiveNotifier::listeners(Vector<Task*>& v) const
{
if (_listener1)
v.push_back(_listener1);
else if (_listeners)
for (Task** l = _listeners; *l; l++)
v.push_back(*l);
}
namespace {
class NotifierElementFilter : public ElementFilter { public:
NotifierElementFilter(const char* name);
bool check_match(Element*, int, PortType);
Vector<Notifier*> _notifiers;
NotifierSignal _signal;
bool _pass2;
bool _need_pass2;
const char* _name;
};
NotifierElementFilter::NotifierElementFilter(const char* name)
: _signal(NotifierSignal::idle_signal()),
_pass2(false), _need_pass2(false), _name(name)
{
}
bool
NotifierElementFilter::check_match(Element* e, int port, PortType pt)
{
if (Notifier* n = (Notifier*) (e->cast(_name))) {
_notifiers.push_back(n);
if (!n->signal().initialized())
n->initialize(e->router());
_signal += n->signal();
Notifier::SearchOp search_op = n->search_op();
if (search_op == Notifier::SEARCH_CONTINUE_WAKE) {
_need_pass2 = true;
return !_pass2;
} else
return search_op == Notifier::SEARCH_STOP;
} else if (pt != NONE) {
Bitvector flow;
if (e->port_active(pt, port) // went from pull <-> push
|| (e->port_flow(pt, port, &flow), flow.zero())) {
_signal = NotifierSignal::busy_signal();
return true;
} else
return false;
} else
return false;
}
}
/** @brief Calculate and return the NotifierSignal derived from all empty
* notifiers upstream of element @a e's input @a port, and optionally register
* @a task as a listener.
* @param e an element
* @param port the input port of @a e at which to start the upstream search
* @param task Task to register as a listener, or null
*
* Searches the configuration upstream of element @a e's input @a port for @e
* empty @e notifiers. These notifiers are associated with packet storage,
* and should be true when packets are available (or likely to be available
* quite soon), and false when they are not. All notifiers found are combined
* into a single derived signal. Thus, if any of the base notifiers are
* active, indicating that at least one packet is available upstream, the
* derived signal will also be active. Element @a e's code generally uses the
* resulting signal to decide whether or not to reschedule itself.
*
* If @a task is nonnull, then @a task becomes a listener for each located
* notifier. Thus, when a notifier becomes active (when packets become
* available), @a task will be rescheduled.
*
* <h3>Supporting upstream_empty_signal()</h3>
*
* Elements that have an empty notifier must override the Element::cast()
* method. When passed the @a name Notifier::EMPTY_NOTIFIER, this method
* should return a pointer to the corresponding Notifier object.
*/
NotifierSignal
Notifier::upstream_empty_signal(Element* e, int port, Task* task)
{
NotifierElementFilter filter(EMPTY_NOTIFIER);
Vector<Element*> v;
int ok = e->router()->upstream_elements(e, port, &filter, v);
NotifierSignal signal = filter._signal;
// maybe run another pass
if (ok >= 0 && signal != NotifierSignal() && filter._need_pass2) {
filter._pass2 = true;
ok = e->router()->upstream_elements(e, port, &filter, v);
}
// All bets are off if filter ran into a push output. That means there was
// a regular Queue in the way (for example).
if (ok < 0 || signal == NotifierSignal())
return NotifierSignal();
if (task)
for (int i = 0; i < filter._notifiers.size(); i++)
filter._notifiers[i]->add_listener(task);
return signal;
}
/** @brief Calculate and return the NotifierSignal derived from all full
* notifiers downstream of element @a e's output @a port, and optionally
* register @a task as a listener.
* @param e an element
* @param port the output port of @a e at which to start the downstream search
* @param task Task to register as a listener, or null
*
* Searches the configuration downstream of element @a e's output @a port for
* @e full @e notifiers. These notifiers are associated with packet storage,
* and should be true when there is space for at least one packet, and false
* when there is not. All notifiers found are combined into a single derived
* signal. Thus, if any of the base notifiers are active, indicating that at
* least one path has available space, the derived signal will also be active.
* Element @a e's code generally uses the resulting signal to decide whether
* or not to reschedule itself.
*
* If @a task is nonnull, then @a task becomes a listener for each located
* notifier. Thus, when a notifier becomes active (when space become
* available), @a task will be rescheduled.
*
* <h3>Supporting downstream_full_signal()</h3>
*
* Elements that have a full notifier must override the Element::cast()
* method. When passed the @a name Notifier::FULL_NOTIFIER, this method
* should return a pointer to the corresponding Notifier object.
*/
NotifierSignal
Notifier::downstream_full_signal(Element* e, int port, Task* task)
{
NotifierElementFilter filter(FULL_NOTIFIER);
Vector<Element*> v;
int ok = e->router()->downstream_elements(e, port, &filter, v);
NotifierSignal signal = filter._signal;
// maybe run another pass
if (ok >= 0 && signal != NotifierSignal() && filter._need_pass2) {
filter._pass2 = true;
ok = e->router()->downstream_elements(e, port, &filter, v);
}
// All bets are off if filter ran into a pull input. That means there was
// a regular Queue in the way (for example).
if (ok < 0 || signal == NotifierSignal())
return NotifierSignal();
if (task)
for (int i = 0; i < filter._notifiers.size(); i++)
filter._notifiers[i]->add_listener(task);
return signal;
}
CLICK_ENDDECLS
syntax highlighted by Code2HTML, v. 0.9.1