A queue may be either blocked or unblocked at any given time. Generally, a queue is blocked when a packet is in transit between it and its downstream neighbor (most of the time if the queue is occupied). A blocked queue will remain blocked as long as it downstream link is busy and the queue has at least one packet to send. A queue becomes unblocked only when its resume function is invoked (by means of a downstream neighbor scheduling it via a callback), usually when no packets are queued. The callback is implemented by using the following class and methods:
class QueueHandler : public Handler {
public:
inline QueueHandler(Queue& q) : queue_(q) {}
void handle(Event*); /* calls queue_.resume() */
private:
Queue& queue_;
};
void QueueHandler::handle(Event*)
{
queue_.resume();
}
Queue::Queue() : drop_(0), blocked_(0), qh_(*this)
{
Tcl& tcl = Tcl::instance();
bind("limit_", &qlim_);
}
void Queue::recv(Packet* p, Handler*)
{
enque(p);
if (!blocked_) {
/*
* We're not block. Get a packet and send it on.
* We perform an extra check because the queue
* might drop the packet even if it was
* previously empty! (e.g., RED can do this.)
*/
p = deque();
if (p != 0) {
blocked_ = 1;
target_-\>recv(p, &qh_);
}
}
}
void Queue::resume()
{
Packet* p = deque();
if (p != 0)
target_-\>recv(p, &qh_);
else {
if (unblock_on_resume_)
blocked_ = 0;
else
blocked_ = 1;
}
}
The handler management here is somewhat subtle.
When a new Queue object is created,
it includes a QueueHandler object (qh_)
which is initialized to contain a reference to the new Queue object
(Queue& QueueHandler::queue_).
This is performed by the Queue constructor using the expression
qh_(*this).
When a Queue receives a packet it calls the subclass
(i.e. queueing discipline-specific) version of
the enque function with the packet.
If the queue is not blocked, it is allowed to send a packet and
calls the specific deque function which determines which
packet to send, blocks the queue (because a packet is now in transit), and
sends the packet to the queue's downstream neighbor.
Note that any future packets received from upstream neighbors
will arrive to a blocked queue.
When a downstream neighbor wishes to cause the queue to become unblocked
it schedules the QueueHandler's handle function by
passing &qh_ to the simulator scheduler.
The handle function invokes resume, which
will send the next-scheduled packet downstream (and leave the queue blocked),
or unblock the queue when no packet is ready to be sent.
This process is made more clear by also referring to the
[]LinkDelay::recv methodSectionsec:delayclass.
Tom Henderson 2011-11-05