// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*-
// vim:set sts=4 ts=8:

// Copyright (c) 2001-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/eventloop.cc,v 1.22 2007/03/08 00:03:55 atanu Exp $"

#include "libxorp_module.h"

#include "libxorp/xorp.h"
#include "libxorp/xlog.h"
#include "libxorp/debug.h"

#include "eventloop.hh"


//
// Number of EventLoop instances.
// XXX: Warning: static instance. Not suitable for shared library use.
//
static int instance_count = 0;

//
// Last call to EventLoop::run.  0 is a special value that indicates
// EventLoop::run has not been called.
//
// XXX: Warning: static instance. Not suitable for shared library use.
//
static time_t last_ev_run;

EventLoop::EventLoop()
    : _clock(new SystemClock), _timer_list(_clock),
#ifdef HOST_OS_WINDOWS
      _win_dispatcher(_clock)
#else
      _selector_list(_clock)
#endif
{
    instance_count++;
    XLOG_ASSERT(instance_count == 1);
    last_ev_run = 0;
}

EventLoop::~EventLoop()
{
    instance_count--;
    XLOG_ASSERT(instance_count == 0);
    delete _clock;
}

void
EventLoop::run()
{
    const time_t MAX_ALLOWED = 2;
    static time_t last_warned;

    if (last_ev_run == 0)
	last_ev_run = time(0);

    time_t now = time(0);
    time_t diff = now - last_ev_run;

    if (now - last_warned > 0 && (diff > MAX_ALLOWED)) {
	XLOG_WARNING("%d seconds between calls to EventLoop::run", (int)diff);
	last_warned = now;
    }

    TimeVal t;

    _timer_list.advance_time();
    _timer_list.get_next_delay(t);

    int timer_priority = XorpTask::PRIORITY_INFINITY;
    int selector_priority = XorpTask::PRIORITY_INFINITY;
    int task_priority = XorpTask::PRIORITY_INFINITY;

    if (t == TimeVal::ZERO()) {
	timer_priority = _timer_list.get_expired_priority();
    }

#ifdef HOST_OS_WINDOWS
    if (_win_dispatcher.ready())
	selector_priority = _win_dispatcher.get_ready_priority();
#else
    if (_selector_list.ready()) {
	selector_priority = _selector_list.get_ready_priority();
    }
#endif

    if (!_task_list.empty()) {
	task_priority = _task_list.get_runnable_priority();
    }

    debug_msg("Priorities: timer = %d selector = %d task = %d\n",
	      timer_priority, selector_priority, task_priority);

    if ( (timer_priority != XorpTask::PRIORITY_INFINITY)
	 && (timer_priority <= selector_priority)
	 && (timer_priority <= task_priority)) {

	// the most important thing to run next is a timer
	_timer_list.run();

    } else if ( (selector_priority != XorpTask::PRIORITY_INFINITY)
		&& (selector_priority <= task_priority) ) {

	// the most important thing to run next is a selector
#ifdef HOST_OS_WINDOWS
	_win_dispatcher.wait_and_dispatch(&t);
#else
	_selector_list.wait_and_dispatch(&t);
#endif

    } else if (task_priority != XorpTask::PRIORITY_INFINITY) {

	// the most important thing to run next is a task
	_task_list.run();

    } else {
	// there's nothing immediate to run, so go to sleep until the
	// next selector or timer goes off
#ifdef HOST_OS_WINDOWS
	_win_dispatcher.wait_and_dispatch(&t);
#else
	_selector_list.wait_and_dispatch(&t);
#endif
    }

    last_ev_run = time(0);
}

bool
EventLoop::add_ioevent_cb(XorpFd fd, IoEventType type, const IoEventCb& cb,
			  int priority)
{
#ifdef HOST_OS_WINDOWS
    return _win_dispatcher.add_ioevent_cb(fd, type, cb, priority);
#else
    return _selector_list.add_ioevent_cb(fd, type, cb, priority);
#endif
}

bool
EventLoop::remove_ioevent_cb(XorpFd fd, IoEventType type)
{
#ifdef HOST_OS_WINDOWS
    return _win_dispatcher.remove_ioevent_cb(fd, type);
#else
    _selector_list.remove_ioevent_cb(fd, type);
    return true;
#endif
}

size_t
EventLoop::descriptor_count() const
{
#ifdef HOST_OS_WINDOWS
    return _win_dispatcher.descriptor_count();
#else
    return _selector_list.descriptor_count();
#endif
}

XorpTask
EventLoop::new_oneoff_task(const OneoffTaskCallback& cb, int priority,
			   int weight)
{
    return _task_list.new_oneoff_task(cb, priority, weight);
}

XorpTask
EventLoop::new_task(const RepeatedTaskCallback& cb, int priority, int weight)
{
    return _task_list.new_task(cb, priority, weight);
}


syntax highlighted by Code2HTML, v. 0.9.1