7.1.1 Queue blocking

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 2014-12-17