6. Quản lý hàng đợi và định trình gói
6.1.1 Khóa hàng đợi
Một hàng đợi có thể bị khóa và được mở khóa tại bất kỳ thời điểm định trước nào. Nhìn chung, một hàng đợi bị khóa khi một gói đang chuyển đi giữa nó và luồng xuống lân cận của nó (phần lớn thời gian nếu hàng đợi bị đầy). Một hàng đợi bị khóa sẽ vẫn bị khóa trong khi liên kết luồng xuống của nó bận và hàng đợi có ít nhất một gói được gửi đi. Một hàng đợi được mở khóa chỉ khi hàm resume được gọi (theo cách mà một luồng xuống lân cận định trình nó thông qua một hàm gọi lại), thường là khi không có gói nào được đưa vào hàng đợi. Việc gọi lại được thực hiện bằng cách sử dụng lớp luồng và các phương thức sau:
class QueueHandler : public Handler { public:
inline QueueHandler(Queue& q) : queue_(q) {} void handle(Event*); /* calls queue_.resume() */ private:
{
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; }
}
Việc quản lý các trình xử lý lỗi ở đây đôi khi rất tinh tế. Khi tạo ra một đối tượng Queue mới, nó bao gồm một đối tượng QueueHandler (qh_) được khởi tạo để chứa một tham chiếu đến đối tượng Queue mới (Queue& Queuehandler: :queue). Điều này được thực hiện bởi hàm dựng Queue sử dụng tham số biểu thức qh_(*this). Khi một Queue nhận một gói, nó sẽ gọi phiên bản lớp con (tức là tạo hàng đợi theo quy định cụ thể) của hàm enque với gói đó. Nếu hàng đợi không bị khóa, nó được phép gửi một gói và gọi hàm deque để xem xét gói gửi, khóa hàng đợi (vì một gói đang được chuyển đi) và gửi gói đến hàng đợi của luồng xuống lân cận. Lưu ý là mọi gói nhận được sau đó của luồng lên lân cận sẽ đến một hàng đợi bị khóa. Khi một luồng xuống lân cận muốn làm cho hàng đợi được mở khóa, nó định trình hàm handle của QueueHandler bằng cách truyền &qh_ đến bộ định trình mô phỏng. Hàm handle gọi hàm resume, hàm này sẽ gửi gói được định trình kế tiếp đến luồng xuống (và vẫn để hàng đợi bị khóa), hoặc mở khóa hàng đợi khi không có gói nào sẵn sàng để được gửi đi. Quá trình này được thực hiện rõ hơn khi tham khảo phương thức LinkDelay::recv().