Files
NE_YuR/openflow/include/click/notifier.hh

731 lines
25 KiB
C++

// -*- c-basic-offset: 4; related-file-name: "../../lib/notifier.cc" -*-
#ifndef CLICK_NOTIFIER_HH
#define CLICK_NOTIFIER_HH
#include <click/task.hh>
#include <click/atomic.hh>
#include <click/algorithm.hh>
#if HAVE_CXX_PRAGMA_INTERFACE
# pragma interface "click/notifier.hh"
#endif
CLICK_DECLS
class NotifierSignal {
public:
typedef bool (NotifierSignal::*unspecified_bool_type)() const;
inline NotifierSignal();
inline NotifierSignal(atomic_uint32_t* value, uint32_t mask);
inline NotifierSignal(const NotifierSignal& x);
inline ~NotifierSignal();
static inline NotifierSignal idle_signal();
static inline NotifierSignal busy_signal();
static inline NotifierSignal overderived_signal();
static inline NotifierSignal uninitialized_signal();
inline bool active() const;
inline operator unspecified_bool_type() const;
inline bool set_active(bool active);
inline bool idle() const;
inline bool busy() const;
inline bool overderived() const;
inline bool initialized() const;
friend bool operator==(const NotifierSignal &a, const NotifierSignal &b);
friend bool operator!=(const NotifierSignal &a, const NotifierSignal &b);
NotifierSignal& operator=(const NotifierSignal& x);
NotifierSignal& operator+=(const NotifierSignal& x);
friend NotifierSignal operator+(NotifierSignal a, const NotifierSignal &b);
inline void swap(NotifierSignal& x);
String unparse(Router *router) const;
static void static_initialize();
private:
struct vmpair {
atomic_uint32_t *value;
uint32_t mask;
};
union vmvalue {
atomic_uint32_t *v1;
vmpair *vm;
};
vmvalue _v;
uint32_t _mask;
enum {
true_mask = 1, false_mask = 2, overderived_mask = 4,
uninitialized_mask = 8
};
static atomic_uint32_t static_value;
void hard_assign_vm(const NotifierSignal &x);
void hard_derive_one(atomic_uint32_t *value, uint32_t mask);
static bool hard_equals(const vmpair *a, const vmpair *b);
};
class Notifier { public:
enum SearchOp { SEARCH_STOP = 0, SEARCH_CONTINUE, SEARCH_CONTINUE_WAKE };
typedef void (*callback_type)(void *, Notifier *);
inline Notifier(SearchOp op = SEARCH_STOP);
inline Notifier(const NotifierSignal &signal, SearchOp op = SEARCH_STOP);
virtual ~Notifier();
/** @brief Return whether the Notifier is initialized. */
inline bool initialized() const {
return _signal.initialized();
}
int initialize(const char *name, Router *router);
inline const NotifierSignal &signal() const;
inline SearchOp search_op() const;
inline bool active() const;
inline bool set_active(bool active);
inline void wake();
inline void sleep();
virtual int add_activate_callback(callback_type f, void *user_data);
virtual void remove_activate_callback(callback_type f, void *user_data);
inline int add_listener(Task *task);
inline void remove_listener(Task *task);
inline int add_dependent_signal(NotifierSignal *signal);
inline void remove_dependent_signal(NotifierSignal *signal);
static const char EMPTY_NOTIFIER[];
static const char FULL_NOTIFIER[];
static inline NotifierSignal upstream_empty_signal(Element *e, int port);
static inline NotifierSignal upstream_empty_signal(Element *e, int port, Task *task);
static inline NotifierSignal upstream_empty_signal(Element *e, int port, Notifier *dependent_notifier);
static NotifierSignal upstream_empty_signal(Element *e, int port, callback_type f, void *user_data);
static inline NotifierSignal downstream_full_signal(Element *e, int port);
static inline NotifierSignal downstream_full_signal(Element *e, int port, Task *task);
static inline NotifierSignal downstream_full_signal(Element *e, int port, Notifier *dependent_notifier);
static NotifierSignal downstream_full_signal(Element *e, int port, callback_type f, void *user_data);
static inline NotifierSignal upstream_empty_signal(Element *e, int port, int) CLICK_DEPRECATED;
static inline NotifierSignal upstream_empty_signal(Element *e, int port, int, Notifier *) CLICK_DEPRECATED;
static inline NotifierSignal downstream_full_signal(Element *e, int port, int) CLICK_DEPRECATED;
static inline NotifierSignal downstream_full_signal(Element *e, int port, int, Notifier *) CLICK_DEPRECATED;
private:
NotifierSignal _signal;
SearchOp _search_op;
static void dependent_signal_callback(void *, Notifier *);
};
class ActiveNotifier : public Notifier { public:
ActiveNotifier(SearchOp op = SEARCH_STOP);
~ActiveNotifier();
int add_activate_callback(callback_type f, void *v);
void remove_activate_callback(callback_type f, void *v);
void listeners(Vector<Task*> &v) const CLICK_DEPRECATED;
inline void set_active(bool active, bool schedule = true);
inline void wake();
inline void sleep();
#if CLICK_DEBUG_SCHEDULING
String unparse(Router *router) const;
#endif
private:
typedef union {
Task *t;
callback_type f;
void *v;
uintptr_t p;
} task_or_signal_t;
Task* _listener1;
task_or_signal_t* _listeners;
int listener_add(callback_type f, void *v);
int listener_remove(callback_type f, void *v);
ActiveNotifier(const ActiveNotifier&); // does not exist
ActiveNotifier& operator=(const ActiveNotifier&); // does not exist
};
/** @brief Construct a busy signal.
*
* The returned signal is always active. */
inline NotifierSignal::NotifierSignal()
: _mask(true_mask) {
_v.v1 = &static_value;
}
/** @brief Construct an activity signal.
*
* Elements should not use this constructor directly.
* @sa Router::new_notifier_signal */
inline NotifierSignal::NotifierSignal(atomic_uint32_t* value, uint32_t mask)
: _mask(mask) {
_v.v1 = value;
}
/** @brief Copy construct a signal. */
inline NotifierSignal::NotifierSignal(const NotifierSignal& x)
: _mask(x._mask) {
if (likely(_mask))
_v.v1 = x._v.v1;
else
hard_assign_vm(x);
}
/** @brief Destroy a signal. */
inline NotifierSignal::~NotifierSignal() {
if (unlikely(_mask == 0))
delete[] _v.vm;
}
/** @brief Return an idle signal.
*
* The returned signal is never active. */
inline NotifierSignal NotifierSignal::idle_signal() {
return NotifierSignal(&static_value, false_mask);
}
/** @brief Return a busy signal.
*
* The returned signal is always active. */
inline NotifierSignal NotifierSignal::busy_signal() {
return NotifierSignal(&static_value, true_mask);
}
/** @brief Return an overderived busy signal.
*
* Overderived signals replace derived signals that are too complex to
* represent. An overderived signal, like a busy signal, is always
* active. */
inline NotifierSignal NotifierSignal::overderived_signal() {
return NotifierSignal(&static_value, overderived_mask | true_mask);
}
/** @brief Return an uninitialized signal.
*
* Uninitialized signals may be used occasionally as placeholders for true
* signals to be added later. Uninitialized signals are never active. */
inline NotifierSignal NotifierSignal::uninitialized_signal() {
return NotifierSignal(&static_value, uninitialized_mask);
}
/** @brief Test if the signal is active. */
inline bool NotifierSignal::active() const {
// 2012.May.16 This fence is necessary; consider, for example,
// InfiniteSource's checking of nonfull notifiers.
click_fence();
if (likely(_mask))
return (*_v.v1 & _mask) != 0;
else {
for (vmpair *vm = _v.vm; vm->mask; ++vm)
if ((*vm->value & vm->mask) != 0)
return true;
return false;
}
}
/** @brief Test if the signal is active. */
inline NotifierSignal::operator unspecified_bool_type() const {
return active() ? &NotifierSignal::active : 0;
}
/** @brief Test if the signal is idle.
* @return true iff the signal is idle, i.e. it will never be active. */
inline bool NotifierSignal::idle() const {
return (_mask == false_mask && _v.v1 == &static_value);
}
/** @brief Test if the signal is busy.
* @return true iff the signal is busy, i.e. it will always be active.
*
* @note An overderived_signal() is busy(), but a busy_signal() is not
* overderived(). */
inline bool NotifierSignal::busy() const {
return ((_mask & true_mask) && _v.v1 == &static_value);
}
/** @brief Test if the signal is overderived.
* @return true iff the signal equals overderived_signal().
*
* @note An overderived_signal() is busy(), but a busy_signal() is not
* overderived(). */
inline bool NotifierSignal::overderived() const {
return ((_mask & overderived_mask) && _v.v1 == &static_value);
}
/** @brief Test if the signal is initialized.
* @return true iff the signal doesn't equal uninitialized_signal(). */
inline bool NotifierSignal::initialized() const {
return (!(_mask & uninitialized_mask) || _v.v1 != &static_value);
}
/** @brief Set whether the basic signal is active.
* @param active true iff the basic signal is active
* @return previous active state
*
* Use this function to set whether a basic signal is active.
*
* It is illegal to call set_active() on derived, idle, busy, or
* overderived signals. Some of these actions may cause an assertion
* failure. */
inline bool NotifierSignal::set_active(bool active) {
assert(_v.v1 != &static_value && !(_mask & (_mask - 1)));
uint32_t expected = *_v.v1;
#if !CLICK_USERLEVEL || HAVE_MULTITHREAD
while (_mask) {
uint32_t desired = (active ? expected | _mask : expected & ~_mask);
uint32_t actual = _v.v1->compare_swap(expected, desired);
if (expected == actual)
break;
expected = actual;
}
#else
*_v.v1 = (active ? expected | _mask : expected & ~_mask);
#endif
return expected & _mask;
}
/** @brief Assign a signal. */
inline NotifierSignal& NotifierSignal::operator=(const NotifierSignal& x) {
if (likely(this != &x)) {
if (unlikely(_mask == 0))
delete[] _v.vm;
_mask = x._mask;
if (likely(_mask))
_v.v1 = x._v.v1;
else
hard_assign_vm(x);
}
return *this;
}
/** @brief Exchange the values of this signal and @a x. */
inline void NotifierSignal::swap(NotifierSignal& x) {
click_swap(_v, x._v);
click_swap(_mask, x._mask);
}
/** @relates NotifierSignal
* @brief Test if two NotifierSignals are equal.
*
* Returns true iff the two NotifierSignals are the same -- i.e., they
* combine information about exactly the same sets of basic signals.
*
* All idle() signals compare equal. busy_signal() and
* overderived_signal() do not compare equal, however. */
inline bool operator==(const NotifierSignal& a, const NotifierSignal& b) {
if (a._mask == b._mask) {
if (likely(a._mask))
return a._v.v1 == b._v.v1;
else
return NotifierSignal::hard_equals(a._v.vm, b._v.vm);
} else
return false;
}
/** @relates NotifierSignal
* @brief Test if two NotifierSignals are unequal.
*
* Returns true iff !(@a a == @a b). */
inline bool operator!=(const NotifierSignal& a, const NotifierSignal& b) {
return !(a == b);
}
/** @relates NotifierSignal
* @brief Return a derived signal.
*
* Returns a derived signal that combines information from its arguments.
* The result will be active whenever @a a and/or @a b is active. If the
* combination of @a a and @a b is too complex to represent, returns an
* overderived signal; this trivially follows the invariant since it is
* always active.
*
* Signal derivation is commutative and associative. The following
* special combinations are worth remembering:
*
* - An uninitialized signal plus any other signal is uninitialized.
* - An idle signal plus any signal @a a equals @a a.
* - A busy signal plus any other initialized signal is busy.
* - overderived_signal() plus busy_signal() equals busy_signal().
*
* @sa NotifierSignal::operator+= */
inline NotifierSignal operator+(NotifierSignal a, const NotifierSignal& b) {
return a += b;
}
/** @brief Constructs a Notifier.
* @param op controls notifier path search
*
* This function constructs a Notifier object. The Notifier's associated
* NotifierSignal is initially idle; it becomes associated with a signal after
* initialize() is called.
*
* The @a op argument controls path search. The rest of this entry
* describes it further.
*
* Elements interested in notification generally search for Notifier objects
* along all possible packet paths upstream (or downstream) of one of their
* ports. When a Notifier is found along a path, further searching along that
* path is cut off, so only the closest Notifiers are found. Sometimes,
* however, it makes more sense to continue searching for more Notifiers. The
* correct behavior is Notifier-specific, and is controlled by this method.
* When the search encounters a Notifier, it consults the Notifier's @a
* op variable supplied to the constructor. It should equal one of
* three SearchOp constants, which correspond to the following behavior:
*
* <dl>
* <dt>SEARCH_STOP</dt>
* <dd>Stop searching along this path. This is the default.</dd>
* <dt>SEARCH_CONTINUE</dt>
* <dd>Continue searching along this path.</dd>
* <dt>SEARCH_CONTINUE_WAKE</dt>
* <dd>Continue searching along this path, but any further Notifiers should
* only be used for adding and removing listeners; ignore their NotifierSignal
* objects. This operation is useful, for example, for schedulers that store
* packets temporarily. Such schedulers provide their own NotifierSignal,
* since the scheduler may still hold a packet even when all upstream sources
* are empty, but since they aren't packet sources, they don't know when
* new packets arrive and can't wake up sleeping listeners. During
* initialization, such schedulers should call Notifier::upstream_empty_signal,
* passing their own Notifier as the fourth argument. This will ensure that
* their signal is turned on appropriately whenever an upstream queue becomes
* nonempty.</dd>
* </dl>
*/
inline Notifier::Notifier(SearchOp op)
: _signal(NotifierSignal::uninitialized_signal()), _search_op(op) {
}
/** @brief Constructs a Notifier associated with a given signal.
* @param signal the associated NotifierSignal
* @param op controls notifier path search
*
* This function constructs a Notifier object associated with a specific
* NotifierSignal, such as NotifierSignal::idle_signal(). Calling
* initialize() on this Notifier will not change the associated
* NotifierSignal. The @a op argument is as in
* Notifier::Notifier(SearchOp), above.
*/
inline Notifier::Notifier(const NotifierSignal &signal, SearchOp op)
: _signal(signal), _search_op(op) {
}
/** @brief Return this Notifier's associated NotifierSignal.
*
* Every Notifier object corresponds to one NotifierSignal; this method
* returns it. The signal is @link NotifierSignal::idle idle() @endlink
* before initialize() is called.
*/
inline const NotifierSignal& Notifier::signal() const {
return _signal;
}
/** @brief Return this Notifier's search operation.
*
* @sa Notifier() for a detailed explanation of search operations.
*/
inline Notifier::SearchOp Notifier::search_op() const {
return _search_op;
}
/** @brief Returns whether the associated signal is active.
*
* Same as signal().active().
*/
inline bool Notifier::active() const {
return _signal.active();
}
/** @brief Set the associated signal's activity.
* @param active true iff the signal should be active
* @return previous active state
*/
inline bool Notifier::set_active(bool active) {
return _signal.set_active(active);
}
/** @brief Set the associated signal to active.
* @sa set_active
*/
inline void Notifier::wake() {
set_active(true);
}
/** @brief Set the associated signal to inactive.
* @sa set_active
*/
inline void Notifier::sleep() {
set_active(false);
}
/** @brief Register a listener with this Notifier.
* @param task Task to reschedule when this Notifier becomes active
*
* When this Notifier's associated signal is activated, the Notifier should
* schedule @a task. Not all types of Notifier provide this functionality. The
* default implementation does nothing.
*
* @sa remove_listener, add_activate_callback, add_dependent_signal
*/
inline int Notifier::add_listener(Task* task) {
return add_activate_callback(0, task);
}
/** @brief Unregister a listener with this Notifier.
* @param task listener Task
*
* Undoes the effect of all prior add_listener(@a task) calls. Does nothing if
* @a task was never added. The default implementation does nothing.
*
* @sa add_listener
*/
inline void Notifier::remove_listener(Task* task) {
remove_activate_callback(0, task);
}
/** @brief Register a dependent signal with this Notifier.
* @param signal dependent signal
*
* When this Notifier's associated signal is activated, the Notifier should
* also activate @a signal. Not all types of Notifier provide this
* functionality. The default implementation does nothing.
*
* @sa add_listener, add_activate_callback, remove_dependent_signal
*/
inline int Notifier::add_dependent_signal(NotifierSignal* signal) {
return add_activate_callback(dependent_signal_callback, signal);
}
/** @brief Unregister a dependent signal with this Notifier.
* @param signal dependent signal
*
* Undoes the effect of all prior add_dependent_signal(@a signal) calls. Does
* nothing if @a signal was never added. The default implementation does
* nothing.
*
* @sa add_dependent_signal
*/
inline void Notifier::remove_dependent_signal(NotifierSignal* signal) {
remove_activate_callback(dependent_signal_callback, signal);
}
/** @brief Calculate and return the NotifierSignal derived from all empty
* notifiers upstream of element @a e's input @a port.
* @param e an element
* @param port the input port of @a e at which to start the upstream search
*
* 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.
*
* The returned signal is generally conservative, meaning that the signal
* is true whenever a packet exists upstream, but the elements that provide
* notification are responsible for ensuring this.
*
* Overloaded versions of this function can also register a task (as in
* add_listener()), a signal (as in add_dependent_notifier()), or a callback
* function (as in add_active_callback()) for each located notifier. When
* packets become available, the task will be scheduled, the signal will be
* activated, or the callback will be called.
*
* <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.
*
* @sa downstream_full_signal
*/
inline NotifierSignal Notifier::upstream_empty_signal(Element* e, int port) {
return upstream_empty_signal(e, port, (callback_type) 0, 0);
}
/** @brief Calculate and return the NotifierSignal derived from all empty
* notifiers upstream of element @a e's input @a port.
* @param e an element
* @param port the input port of @a e at which to start the upstream search
* @param task task to schedule when packets become available
* @sa add_listener */
inline NotifierSignal Notifier::upstream_empty_signal(Element* e, int port,
Task* task) {
return upstream_empty_signal(e, port, (callback_type) 0, task);
}
/** @brief Calculate and return the NotifierSignal derived from all empty
* notifiers upstream of element @a e's input @a port.
* @param e an element
* @param port the input port of @a e at which to start the upstream search
* @param notifier notifier to activate when packets become available
* @sa add_dependent_signal */
inline NotifierSignal Notifier::upstream_empty_signal(Element* e, int port,
Notifier* dependent_notifier) {
return upstream_empty_signal(e, port, dependent_signal_callback, &dependent_notifier->_signal);
}
/** @brief Calculate and return the NotifierSignal derived from all full
* notifiers downstream of element @a e's output @a port.
* @param e an element
* @param port the output port of @a e at which to start the downstream search
*
* 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.
*
* Overloaded versions of this function can also register a task (as in
* add_listener()), a signal (as in add_dependent_notifier()), or a callback
* function (as in add_active_callback()) for each located notifier. When
* space becomes available, the task will be scheduled, the signal will be
* activated, or the callback will be called.
*
* In current Click, the returned signal is conservative: if it's inactive,
* then there is no space for packets downstream.
*
* <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.
*
* @sa upstream_empty_signal
*/
inline NotifierSignal Notifier::downstream_full_signal(Element* e, int port) {
return downstream_full_signal(e, port, (callback_type) 0, 0);
}
/** @brief Calculate and return the NotifierSignal derived from all full
* notifiers downstream of element @a e's output @a port.
* @param e an element
* @param port the output port of @a e at which to start the downstream search
* @param task task to schedule when packets become available
* @sa add_listener */
inline NotifierSignal Notifier::downstream_full_signal(Element* e, int port,
Task* task) {
return downstream_full_signal(e, port, (callback_type) 0, task);
}
/** @brief Calculate and return the NotifierSignal derived from all full
* notifiers downstream of element @a e's output @a port.
* @param e an element
* @param port the output port of @a e at which to start the downstream search
* @param notifier notifier to activate when packets become available
* @sa add_dependent_signal */
inline NotifierSignal Notifier::downstream_full_signal(Element* e, int port,
Notifier* dependent_notifier) {
return downstream_full_signal(e, port, dependent_signal_callback, &dependent_notifier->_signal);
}
/** @cond never */
inline NotifierSignal Notifier::upstream_empty_signal(Element* e, int port, int x) {
(void) x;
assert(x == 0);
return upstream_empty_signal(e, port);
}
inline NotifierSignal Notifier::upstream_empty_signal(Element* e, int port, int x,
Notifier* notifier) {
(void) x;
assert(x == 0);
return upstream_empty_signal(e, port, notifier);
}
inline NotifierSignal Notifier::downstream_full_signal(Element* e, int port, int x) {
(void) x;
assert(x == 0);
return downstream_full_signal(e, port);
}
inline NotifierSignal Notifier::downstream_full_signal(Element* e, int port, int x,
Notifier* notifier) {
(void) x;
assert(x == 0);
return downstream_full_signal(e, port, notifier);
}
/** @endcond never */
/** @brief Set the associated signal's activity, possibly scheduling any
* listener tasks.
* @param active true iff the signal should be active
* @param schedule if true, wake up listener tasks
*
* If @a active and @a schedule are both true, and the signal was previously
* inactive, then any listener Tasks are scheduled with Task::reschedule().
*
* @sa wake, sleep, add_listener
*/
inline void ActiveNotifier::set_active(bool active, bool schedule) {
bool was_active = Notifier::set_active(active);
if (active && schedule && !was_active) {
// 2007.Sep.6: Perhaps there was a race condition here. Make sure
// that we set the notifier to active BEFORE rescheduling downstream
// tasks. This is because, in a multithreaded environment, a task we
// reschedule might run BEFORE we set the notifier; after which it
// would go to sleep forever.
if (_listener1)
_listener1->reschedule();
else if (task_or_signal_t *tos = _listeners) {
for (; tos->p > 1; tos++)
tos->t->reschedule();
if (tos->p == 1)
for (tos++; tos->p; tos += 2)
tos->f(tos[1].v, this);
}
}
}
/** @brief Set the associated signal to active and schedule any listener
* tasks.
*
* If the signal was previously inactive, then any listener Tasks are
* scheduled with Task::reschedule().
*
* @sa set_active, add_listener
*/
inline void ActiveNotifier::wake() {
set_active(true, true);
}
/** @brief Set the associated signal to inactive.
* @sa set_active
*/
inline void ActiveNotifier::sleep() {
set_active(false, true);
}
inline void click_swap(NotifierSignal& x, NotifierSignal& y) {
x.swap(y);
}
CLICK_ENDDECLS
#endif