// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*-

// Copyright (c) 2006-2007 International Computer Science Institute
//
// 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 XORP 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 XORP LICENSE file; the license in that file is
// legally binding.

#ident "$XORP: xorp/libxorp/round_robin.cc,v 1.5 2007/02/16 22:46:22 pavlin Exp $"

#include "libxorp_module.h"
#include "libxorp/xorp.h"

#include <strings.h>

#include "libxorp/debug.h"
#include "libxorp/eventloop.hh"
#include "libxorp/xlog.h"

#include "round_robin.hh"

RoundRobinObjBase::RoundRobinObjBase()
    : _weight(0), _next(NULL), _prev(NULL)
{
}

bool
RoundRobinObjBase::scheduled() const
{
    return (_next != NULL);
}

RoundRobinQueue::RoundRobinQueue()
    : _next_to_run(NULL), _run_count(0), _elements(0)
{
}

void
RoundRobinQueue::push(RoundRobinObjBase* obj, int weight)
{
    XLOG_ASSERT(obj != NULL);
    XLOG_ASSERT(weight > 0);
    link_object(obj, weight);
}

void
RoundRobinQueue::pop_obj(RoundRobinObjBase* obj)
{
    XLOG_ASSERT(obj != NULL);
    unlink_object(obj);
}

void
RoundRobinQueue::pop()
{
    XLOG_ASSERT(_next_to_run != NULL);
    pop_obj(_next_to_run);
}

RoundRobinObjBase*
RoundRobinQueue::get_next_entry()
{
    RoundRobinObjBase* top = _next_to_run;
    if (top != NULL) {
	XLOG_ASSERT(_run_count < top->weight());
	_run_count++;
	if (_run_count == top->weight()) {
	    _next_to_run = _next_to_run->next();
	    _run_count = 0;
	}
    }
    return top;
}

void
RoundRobinQueue::link_object(RoundRobinObjBase* obj, int weight)
{
    obj->set_weight(weight);

    if (_next_to_run == NULL) {
	// This is the first element
	_next_to_run = obj;
	_run_count = 0;
	obj->set_next(obj);
	obj->set_prev(obj);
    } else {
	//
	// Insert just before the next entry to be run, so this is at the
	// end of the queue.
	//
	obj->set_next(_next_to_run);
	obj->set_prev(_next_to_run->prev());
	obj->prev()->set_next(obj);
	obj->next()->set_prev(obj);
    }
    _elements++;
}

void
RoundRobinQueue::unlink_object(RoundRobinObjBase* obj)
{
    if (obj->next() == obj) {
	// This is the only item in the list
	_next_to_run = NULL;
    } else {
	if (_next_to_run == obj) {
	    _next_to_run = obj->next();
	    _run_count = 0;
	}
	obj->prev()->set_next(obj->next());
	obj->next()->set_prev(obj->prev());
    }
    obj->set_prev(NULL);
    obj->set_next(NULL);
    _elements--;
}


syntax highlighted by Code2HTML, v. 0.9.1