// -*- c-basic-offset: 4; related-file-name: "../include/click/master.hh" -*-
/*
 * master.{cc,hh} -- Click event master
 * Eddie Kohler
 *
 * Copyright (c) 2003-6 The Regents of the University of California
 *
 * 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 Click 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 Click LICENSE file; the license in that file is
 * legally binding.
 */

#include <click/config.h>
#include <click/master.hh>
#include <click/element.hh>
#include <click/router.hh>
#include <click/handlercall.hh>
#if CLICK_USERLEVEL && HAVE_SYS_EVENT_H && HAVE_KQUEUE
# include <sys/event.h>
# if HAVE_EV_SET_UDATA_POINTER
#  define EV_SET_UDATA_CAST	(void *)
# else
#  define EV_SET_UDATA_CAST	/* nothing */
# endif
#endif
#if CLICK_USERLEVEL
# include <signal.h>
#endif
CLICK_DECLS

#if CLICK_USERLEVEL && !HAVE_POLL_H
enum { POLLIN = Element::SELECT_READ, POLLOUT = Element::SELECT_WRITE };
#endif

#if CLICK_USERLEVEL
atomic_uint32_t Master::signals_pending;
#endif

Master::Master(int nthreads)
    : _master_paused(0), _routers(0), _task_list(0, 0)
{
    _refcount = 0;
    _stopper = 0;

    for (int tid = -2; tid < nthreads; tid++)
	_threads.push_back(new RouterThread(this, tid));
    
    _task_list.make_list();
    
#if CLICK_USERLEVEL
# if HAVE_SYS_EVENT_H && HAVE_KQUEUE
    _kqueue = kqueue();
    _selected_callno = 0;
# endif
# if !HAVE_POLL_H
    FD_ZERO(&_read_select_fd_set);
    FD_ZERO(&_write_select_fd_set);
    _max_select_fd = -1;
# endif
    assert(!_pollfds.size() && !_read_poll_elements.size() && !_write_poll_elements.size());
    // Add a null 'struct pollfd', then take it off. This ensures that
    // _pollfds.begin() is nonnull, preventing crashes on Mac OS X
    struct pollfd dummy;
    dummy.events = dummy.fd = 0;
# if HAVE_POLL_H
    dummy.revents = 0;
# endif
    _pollfds.push_back(dummy);
    _pollfds.clear();

    signals_pending = 0;
    _siginfo = 0;
    _signal_adding = false;
#endif

#if CLICK_NS
    _siminst = 0;
    _clickinst = 0;
#endif
}

Master::~Master()
{
    _master_lock.acquire();
    _refcount++;
    while (_routers) {
	Router *r = _routers;
	r->use();
	_master_lock.release();
	unregister_router(r);
	r->unuse();
	_master_lock.acquire();
    }
    _refcount--;
    _master_lock.release();

    if (_refcount > 0)
	click_chatter("deleting master while ref count = %d", _refcount);
    
    for (int i = 0; i < _threads.size(); i++)
	delete _threads[i];
#if CLICK_USERLEVEL && HAVE_SYS_EVENT_H && HAVE_KQUEUE
    if (_kqueue >= 0)
	close(_kqueue);
#endif
}

void
Master::use()
{
    _master_lock.acquire();
    _refcount++;
    _master_lock.release();
}

void
Master::unuse()
{
    _master_lock.acquire();
    _refcount--;
    bool del = (_refcount <= 0);
    _master_lock.release();
    if (del)
	delete this;
}


// ROUTERS

void
Master::register_router(Router *router)
{
    _master_lock.acquire();
    assert(router && router->_master == 0 && router->_running == Router::RUNNING_INACTIVE && !router->_next_router);
    _refcount++;		// balanced in unregister_router()
    router->_master = this;
    router->_next_router = _routers;
    _routers = router;
    _master_lock.release();
}

void
Master::prepare_router(Router *router)
{
    // increments _master_paused; should quickly call run_router() or
    // kill_router()
    _master_lock.acquire();
    assert(router && router->_master == this && router->_running == Router::RUNNING_INACTIVE);
    _master_paused++;
    router->_running = Router::RUNNING_PREPARING;
    _master_lock.release();
}

void
Master::run_router(Router *router, bool foreground)
{
    _master_lock.acquire();
    assert(router && router->_master == this && router->_running == Router::RUNNING_PREPARING);
    router->_running = (foreground ? Router::RUNNING_ACTIVE : Router::RUNNING_BACKGROUND);
    _master_paused--;
    _master_lock.release();
}

void
Master::kill_router(Router *router)
{
#if CLICK_LINUXMODULE
    assert(!in_interrupt());
#endif
    
    _master_lock.acquire();
    assert(router && router->_master == this);
    int was_running = router->_running;
    router->_running = Router::RUNNING_DEAD;
    if (was_running >= Router::RUNNING_BACKGROUND)
	_master_paused++;
    else if (was_running == Router::RUNNING_PREPARING)
	/* nada */;
    else {
	/* could not have anything on the list */
	assert(was_running == Router::RUNNING_INACTIVE || was_running == Router::RUNNING_DEAD);
	_master_lock.release();
	return;
    }

    // Fix stopper
    _stopper = 1;
    _master_lock.release();
    
    // Remove tasks
    for (RouterThread **tp = _threads.begin(); tp < _threads.end(); tp++)
	(*tp)->unschedule_router_tasks(router);
    
    {
	SpinlockIRQ::flags_t flags = _task_lock.acquire();

	// Remove pending tasks
	Task *prev = &_task_list;
	for (Task *t = _task_list._pending_next; t != &_task_list; ) {
	    Task *next = t->_pending_next;
	    if (t->_router == router)
		t->_pending_next = 0;
	    else {
		prev->_pending_next = t;
		prev = t;
	    }
	    t = next;
	}
	prev->_pending_next = &_task_list;

	_task_lock.release(flags);
    }
    
    // Remove timers
    {
	_timer_lock.acquire();
	Timer* t;
	for (Timer** tp = _timer_heap.end(); tp > _timer_heap.begin(); )
	    if ((t = *--tp, t->_router == router)) {
		timer_reheapify_from(tp - _timer_heap.begin(), _timer_heap.back());
		// must clear _schedpos AFTER timer_reheapify_from()
		t->_router = 0;
		t->_schedpos = -1;
		// recheck this slot; have moved a timer there
		_timer_heap.pop_back();
		if (tp < _timer_heap.end())
		    tp++;
	    }
	_timer_lock.release();
    }

#if CLICK_USERLEVEL
    // Remove selects
    _select_lock.acquire();
    for (int pi = 0; pi < _pollfds.size(); pi++) {
	int fd = _pollfds[pi].fd;
	// take components out of the arrays early
	Element* read_element = _read_poll_elements[pi];
	Element* write_element = _write_poll_elements[pi];
	if (read_element && read_element->router() == router)
	    remove_pollfd(pi, POLLIN);
	if (write_element && write_element->router() == router)
	    remove_pollfd(pi, POLLOUT);
	if (pi < _pollfds.size() && _pollfds[pi].fd != fd)
	    pi--;
    }
    _select_lock.release();

    // Remove signals
    {
	_signal_lock.acquire();
	SignalInfo **pprev = &_siginfo;
	for (SignalInfo *si = *pprev; si; si = *pprev)
	    if (si->router == router) {
		remove_signal_handler(si->signo, si->router, si->handler);
		pprev = &_siginfo;
	    } else
		pprev = &si->next;
	_signal_lock.release();
    }
#endif

    _master_lock.acquire();
    _master_paused--;
    _master_lock.release();

    // something has happened, so wake up threads
    for (RouterThread** tp = _threads.begin() + 2; tp < _threads.end(); tp++)
	(*tp)->wake();
}

void
Master::unregister_router(Router *router)
{
    assert(router);
    _master_lock.acquire();

    if (router->_master) {
	assert(router->_master == this);

	if (router->_running >= Router::RUNNING_PREPARING)
	    kill_router(router);
    
	Router **pprev = &_routers;
	for (Router *r = *pprev; r; r = r->_next_router)
	    if (r != router) {
		*pprev = r;
		pprev = &r->_next_router;
	    }
	*pprev = 0;
	_refcount--;		// balanced in register_router()
	router->_master = 0;
    }
    
    _master_lock.release();
}

bool
Master::check_driver()
{
#if CLICK_LINUXMODULE
    assert(!in_interrupt());
#endif
    
    _master_lock.acquire();
    _stopper = 0;
    bool any_active = false;
    
    for (Router *r = _routers; r; ) {
	Router *next_router = r->_next_router;
	if (r->runcount() <= 0 && r->_running >= Router::RUNNING_BACKGROUND) {
	    Element *dm = (Element *)(r->attachment("Script"));
	    if (dm) {
		int max = 1000;
		while (HandlerCall::call_write(dm, "step", "router") == 0
		       && r->runcount() <= 0 && --max >= 0)
		    /* do nothing */;
	    }
	    if (r->runcount() <= 0 && r->_running >= Router::RUNNING_BACKGROUND) {
		kill_router(r);
		goto next;
	    }
	}
	if (r->_running == Router::RUNNING_ACTIVE)
	    any_active = true;
    next:
	r = next_router;
    }

    if (!any_active)
	_stopper = 1;
    _master_lock.release();
    return any_active;
}


// PENDING TASKS

void
Master::process_pending(RouterThread *thread)
{
    if (_master_lock.attempt()) {
	if (_master_paused == 0) {
	    // get a copy of the list
	    SpinlockIRQ::flags_t flags = _task_lock.acquire();
	    Task *t = _task_list._pending_next;
	    _task_list._pending_next = &_task_list;
	    thread->_pending = 0;
	    _task_lock.release(flags);

	    // reverse list so pending tasks are processed in the order we
	    // added them
	    Task *prev = &_task_list;
	    while (t != &_task_list) {
		Task* next = t->_pending_next;
		t->_pending_next = prev;
		prev = t;
		t = next;
	    }

	    // process list
	    for (t = prev; t != &_task_list; ) {
		Task* next = t->_pending_next;
		t->_pending_next = 0;
		t->process_pending(thread);
		t = next;
	    }
	}
	_master_lock.release();
    }
}


// TIMERS

void
Master::timer_reheapify_from(int pos, Timer* t)
{
    // MUST be called with _timer_lock held
    Timer** tbegin = _timer_heap.begin();
    Timer** tend = _timer_heap.end();
    int npos;

    while (pos > 0
	   && (npos = (pos-1) >> 1, tbegin[npos]->_expiry > t->_expiry)) {
	tbegin[pos] = tbegin[npos];
	tbegin[npos]->_schedpos = pos;
	pos = npos;
    }

    while (1) {
	Timer* smallest = t;
	Timer** tp = tbegin + 2*pos + 1;
	if (tp < tend && tp[0]->_expiry <= smallest->_expiry)
	    smallest = tp[0];
	if (tp + 1 < tend && tp[1]->_expiry <= smallest->_expiry)
	    smallest = tp[1], tp++;

	smallest->_schedpos = pos;
	tbegin[pos] = smallest;

	if (smallest == t)
	    break;

	pos = tp - tbegin;
    }
}

// How long until next timer expires.

Timestamp
Master::next_timer_expiry()
{
    _timer_lock.acquire();
    if (_timer_heap.size() == 0) {
	_timer_lock.release();
	return Timestamp();
    } else {
	Timestamp next_expiry = _timer_heap.at_u(0)->_expiry;
	_timer_lock.release();
	return next_expiry;
    }
}

void
Master::run_timers()
{
    if (_master_lock.attempt()) {
	if (_master_paused == 0 && _timer_lock.attempt()) {
	    Timestamp now = Timestamp::now();
	    Timer* t;
	    while (_timer_heap.size() > 0 && !_stopper
		   && (t = _timer_heap.at_u(0), t->_expiry <= now)) {
		timer_reheapify_from(0, _timer_heap.back());
		// must reset _schedpos AFTER timer_reheapify_from
		t->_schedpos = -1;
		_timer_heap.pop_back();
		t->_hook(t, t->_thunk);
	    }
	    _timer_lock.release();
	}
	_master_lock.release();
    }
}


// SELECT

#if CLICK_USERLEVEL

namespace {
enum { SELECT_READ = Element::SELECT_READ, SELECT_WRITE = Element::SELECT_WRITE };
}

int
Master::add_select(int fd, Element *element, int mask)
{
    if (fd < 0)
	return -1;
    if (mask == 0)
	return 0;
    assert(element && (mask & ~(SELECT_READ | SELECT_WRITE)) == 0);
    _select_lock.acquire();

    int pi = _pollfds.size();
    for (int pix = 0; pix < _pollfds.size(); pix++)
	if (_pollfds[pix].fd == fd) {
	    // There is exactly one match per fd.
	    if (((mask & SELECT_READ) && (_pollfds[pix].events & POLLIN) && _read_poll_elements[pix] != element)
		|| ((mask & SELECT_WRITE) && (_pollfds[pix].events & POLLOUT) && _write_poll_elements[pix] != element)) {
		_select_lock.release();
		return -1;
	    }
	    pi = pix;
	    break;
	} else if (_pollfds[pix].fd < 0)
	    pi = pix;

    // Add a new selector
    if (pi == _pollfds.size()) {
	_pollfds.push_back(pollfd());
	_pollfds[pi].events = 0;
	_read_poll_elements.push_back(0);
	_write_poll_elements.push_back(0);
#if HAVE_SYS_EVENT_H && HAVE_KQUEUE
	_selected_callnos.push_back(0);
#endif
    }
    _pollfds[pi].fd = fd;

    // Add selectors
    if (mask & SELECT_READ) {
	_pollfds[pi].events |= POLLIN;
	_read_poll_elements[pi] = element;
    }
    if (mask & SELECT_WRITE) {
	_pollfds[pi].events |= POLLOUT;
	_write_poll_elements[pi] = element;
    }

#if HAVE_SYS_EVENT_H && HAVE_KQUEUE
    if (_kqueue >= 0) {
	// Add events to the kqueue
	struct kevent kev[2];
	int nkev = 0;
	if (mask & SELECT_READ) {
	    EV_SET(&kev[nkev], fd, EVFILT_READ, EV_ADD, 0, 0, EV_SET_UDATA_CAST ((intptr_t) pi));
	    nkev++;
	}
	if (mask & SELECT_WRITE) {
	    EV_SET(&kev[nkev], fd, EVFILT_WRITE, EV_ADD, 0, 0, EV_SET_UDATA_CAST ((intptr_t) pi));
	    nkev++;
	}
	int r = kevent(_kqueue, &kev[0], nkev, 0, 0, 0);
	if (r < 0) {
	    // Not all file descriptors are kqueueable.  So if we encounter
	    // a problem, fall back to select() or poll().
	    // click_chatter("Master::add_select(%d, %d): kevent: %s", (int) kev[0].ident, kev[0].filter, strerror(errno));
	    close(_kqueue);
	    _kqueue = -1;
	    // Clean blank entries out of the _pollfds array.
	    for (pollfd *p = _pollfds.begin(); p < _pollfds.end(); p++)
		if (p->fd < 0) {
		    *p = _pollfds.back();
		    _pollfds.pop_back();
		    p--;
		}
	}
    }
#endif
	
#if !HAVE_POLL_H
    // Add 'mask' to the fd_sets
    if (fd < FD_SETSIZE) {
	if (mask & SELECT_READ)
	    FD_SET(fd, &_read_select_fd_set);
	if (mask & SELECT_WRITE)
	    FD_SET(fd, &_write_select_fd_set);
	if (fd > _max_select_fd)
	    _max_select_fd = fd;
    } else {
	static int warned = 0;
# if HAVE_SYS_EVENT_H && HAVE_KQUEUE
	if (_kqueue < 0)
# endif
	    if (!warned) {
		click_chatter("Master::add_select(%d): fd > FD_SETSIZE", fd);
		warned = 1;
	    }
    }
#endif

    _select_lock.release();
    return 0;
}

void
Master::remove_pollfd(int pi, int event)
{
    assert(event == POLLIN || event == POLLOUT);

    // remove event
    _pollfds[pi].events &= ~event;
    if (event == POLLIN)
	_read_poll_elements[pi] = 0;
    else
	_write_poll_elements[pi] = 0;

#if HAVE_SYS_EVENT_H && HAVE_KQUEUE
    // remove event from kqueue
    if (_kqueue >= 0) {
	struct kevent kev;
	EV_SET(&kev, _pollfds[pi].fd, (event == POLLIN ? EVFILT_READ : EVFILT_WRITE), EV_DELETE, 0, 0, EV_SET_UDATA_CAST ((intptr_t) pi));
	int r = kevent(_kqueue, &kev, 1, 0, 0, 0);
	if (r < 0)
	    click_chatter("Master::remove_pollfd(fd %d): kevent: %s", _pollfds[pi].fd, strerror(errno));
    }
#endif
#if !HAVE_POLL_H
    // remove event from select list
    if (_pollfds[pi].fd < FD_SETSIZE) {
	fd_set *fd_ptr = (event == POLLIN ? &_read_select_fd_set : &_write_select_fd_set);
	FD_CLR(_pollfds[pi].fd, fd_ptr);
    }
#endif

    // exit unless there are no events left
    if (_pollfds[pi].events)
	return;
    
    // remove whole pollfd
#if HAVE_SYS_EVENT_H && HAVE_KQUEUE
    // except we don't need to under kqueue
    if (_kqueue >= 0) {
	_pollfds[pi].fd = -1;
	return;
    }
#endif
    
    _pollfds[pi] = _pollfds.back();
    _pollfds.pop_back();
    // 31.Oct.2003 - Peter Swain: keep fds and elements in sync
    _write_poll_elements[pi]  = _write_poll_elements.back();
    _write_poll_elements.pop_back();
    _read_poll_elements[pi]  = _read_poll_elements.back();
    _read_poll_elements.pop_back();
#if !HAVE_POLL_H
    if (!_pollfds.size())
	_max_select_fd = -1;
#endif
}

int
Master::remove_select(int fd, Element *element, int mask)
{
    if (fd < 0)
	return -1;
    assert(element && (mask & ~(SELECT_READ | SELECT_WRITE)) == 0);
    _select_lock.acquire();

#if !HAVE_POLL_H
    // Exit early if no selector defined
    if (fd < FD_SETSIZE
	&& (!(mask & SELECT_READ) || !FD_ISSET(fd, &_read_select_fd_set))
	&& (!(mask & SELECT_WRITE) || !FD_ISSET(fd, &_write_select_fd_set))) {
	_select_lock.release();
	return -1;
    }
#endif

    // Search for selector
    for (pollfd *p = _pollfds.begin(); p != _pollfds.end(); p++)
	if (p->fd == fd) {
	    int pi = p - _pollfds.begin();
	    // check what to remove before removing anything, since
	    // remove_pollfd() can rearrange the _pollfds array
	    bool remove_read = (mask & SELECT_READ) && (p->events & POLLIN) && _read_poll_elements[pi] == element;
	    bool remove_write = (mask & SELECT_WRITE) && (p->events & POLLOUT) && _write_poll_elements[pi] == element;
	    if (remove_read)
		remove_pollfd(pi, POLLIN);
	    if (remove_write)
		remove_pollfd(pi, POLLOUT);
	    _select_lock.release();
	    return (remove_read || remove_write ? 0 : -1);
	}

    _select_lock.release();
    return -1;
}


#if HAVE_SYS_EVENT_H && HAVE_KQUEUE
void
Master::run_selects_kqueue(bool more_tasks)
{
    // Decide how long to wait.
# if CLICK_NS
    // Never block if we're running in the simulator.
    struct timespec wait, *wait_ptr = &wait;
    wait.tv_sec = wait.tv_nsec = 0;
# else /* !CLICK_NS */
    // Never wait if anything is scheduled; otherwise, if no timers, block
    // indefinitely.
#  if SIZEOF_STRUCT_TIMESPEC == 8
    Timestamp t;
    struct timespec *wait_ptr = (struct timespec*) &t;
    if (!more_tasks) {
	t = next_timer_expiry();
	if (t.sec() == 0)
	    wait_ptr = 0;
	else if ((t -= Timestamp::now(), t.sec() >= 0))
	    // fix up subseconds <-> nanoseconds
	    t.set_subsec(Timestamp::subsec_to_nsec(t.subsec()));
	else
	    t.set(0, 0);
    }
#  else /* SIZEOF_STRUCT_TIMESPEC != 8 */
    struct timespec wait, *wait_ptr = &wait;
    wait.tv_sec = wait.tv_nsec = 0;
    if (!more_tasks) {
	Timestamp t = next_timer_expiry();
	if (t.sec() == 0)
	    wait_ptr = 0;
	else if ((t -= Timestamp::now(), t.sec() >= 0))
	    wait = t.timespec();
    }
#  endif /* SIZEOF_STRUCT_TIMESPEC == 8 */
# endif

    // Bump selected_callno
    _selected_callno++;
    if (_selected_callno == 0) { // be anal about wraparound
	memset(_selected_callnos.begin(), 0, _selected_callnos.size() * sizeof(int));
	_selected_callno++;
    }
    
    struct kevent kev[64];
    int n = kevent(_kqueue, 0, 0, &kev[0], 64, wait_ptr);
    int was_errno = errno;
    run_signals();

    if (n < 0 && was_errno != EINTR)
	perror("kevent");
    else if (n > 0)
	for (struct kevent *p = &kev[0]; p < &kev[n]; p++) {
	    Element *e = 0;
	    int pi = (intptr_t) p->udata;
	    // check _pollfds[pi].fd in case 'selected()' called
	    // remove_select()
	    if (_pollfds[pi].fd != (int) p->ident)
		/* do nothing */;
	    else if (p->filter == EVFILT_READ && (_pollfds[pi].events & POLLIN))
		e = _read_poll_elements[pi];
	    else if (p->filter == EVFILT_WRITE && (_pollfds[pi].events & POLLOUT))
		e = _write_poll_elements[pi];
	    if (e && (_selected_callnos[pi] != _selected_callno
		      || _read_poll_elements[pi] != _write_poll_elements[pi])) {
		e->selected(p->ident);
		_selected_callnos[pi] = _selected_callno;
	    }
	}
}
#endif /* HAVE_SYS_EVENT_H && HAVE_KQUEUE */

#if HAVE_POLL_H
void
Master::run_selects_poll(bool more_tasks)
{
    // Decide how long to wait.
# if CLICK_NS
    // Never block if we're running in the simulator.
    int timeout = -1;
# else
    // Never wait if anything is scheduled; otherwise, if no timers, block
    // indefinitely.
    int timeout = 0;
    if (!more_tasks) {
	Timestamp t = next_timer_expiry();
	if (t.sec() == 0)
	    timeout = -1;
	else if ((t -= Timestamp::now(), t.sec() >= 0)) {
	    if (t.sec() >= INT_MAX / 1000)
		timeout = INT_MAX - 1000;
	    else
		timeout = t.msec1();
	}
    }
# endif /* CLICK_NS */

    int n = poll(_pollfds.begin(), _pollfds.size(), timeout);
    int was_errno = errno;
    run_signals();

    if (n < 0 && was_errno != EINTR)
	perror("poll");
    else if (n > 0)
	for (struct pollfd *p = _pollfds.begin(); p < _pollfds.end(); p++)
	    if (p->revents) {
		int pi = p - _pollfds.begin();

		// Beware: calling 'selected()' might call remove_select(),
		// causing disaster! Load everything we need out of the
		// vectors before calling out.

		int fd = p->fd;
		Element *read_elt = (p->revents & ~POLLOUT ? _read_poll_elements[pi] : 0);
		Element *write_elt = (p->revents & ~POLLIN ? _write_poll_elements[pi] : 0);

		if (read_elt)
		    read_elt->selected(fd);
		if (write_elt && write_elt != read_elt)
		    write_elt->selected(fd);

		// 31.Oct.2003 - Peter Swain: _pollfds may have grown or
		// shrunk!
		p = _pollfds.begin() + pi;
		if (p < _pollfds.end() && fd != p->fd)
		    p--;
	    }
}

#else /* !HAVE_POLL_H */
void
Master::run_selects_select(bool more_tasks)
{
    // Decide how long to wait.
# if CLICK_NS
    // Never block if we're running in the simulator.
    struct timeval wait, *wait_ptr = &wait;
    timerclear(&wait);
# else /* !CLICK_NS */
    // Never wait if anything is scheduled; otherwise, if no timers, block
    // indefinitely.
#  if SIZEOF_STRUCT_TIMEVAL == 8
    Timestamp t;
    struct timeval *wait_ptr = (struct timeval*) &t;
    if (!more_tasks) {
	t = next_timer_expiry();
	if (t.sec() == 0)
	    wait_ptr = 0;
	else if ((t -= Timestamp::now(), t.sec() >= 0))
	    // fix up subseconds <-> microseconds
	    t.set_subsec(Timestamp::subsec_to_usec(t.subsec()));
	else
	    t.set(0, 0);
    }
#  else /* SIZEOF_STRUCT_TIMEVAL != 8 */
    struct timeval wait, *wait_ptr = &wait;
    timerclear(&wait);
    if (!more_tasks) {
	Timestamp t = next_timer_expiry();
	if (t.sec() == 0)
	    wait_ptr = 0;
	else if ((t -= Timestamp::now(), t.sec() >= 0))
	    wait = t.timeval();
    }
#  endif /* SIZEOF_STRUCT_TIMEVAL == 8 */
# endif /* CLICK_NS */

    fd_set read_mask = _read_select_fd_set;
    fd_set write_mask = _write_select_fd_set;

    int n = select(_max_select_fd + 1, &read_mask, &write_mask, (fd_set*) 0, wait_ptr);
    int was_errno = errno;
    run_signals();
  
    if (n < 0 && was_errno != EINTR)
	perror("select");
    else if (n > 0)
	for (struct pollfd *p = _pollfds.begin(); p < _pollfds.end(); p++)
	    if (p->fd > FD_SETSIZE || FD_ISSET(p->fd, &read_mask) || FD_ISSET(p->fd, &write_mask)) {
		int pi = p - _pollfds.begin();

		// Beware: calling 'selected()' might call remove_select(),
		// causing disaster! Load everything we need out of the
		// vectors before calling out.

		int fd = p->fd;
		Element *read_elt = (fd > FD_SETSIZE || FD_ISSET(fd, &read_mask) ? _read_poll_elements[pi] : 0);
		Element *write_elt = (fd > FD_SETSIZE || FD_ISSET(fd, &write_mask) ? _write_poll_elements[pi] : 0);

		if (read_elt)
		    read_elt->selected(fd);
		if (write_elt && write_elt != read_elt)
		    write_elt->selected(fd);

		// 31.Oct.2003 - Peter Swain: _pollfds may have grown or
		// shrunk!
		p = _pollfds.begin() + pi;
		if (p < _pollfds.end() && fd != p->fd)
		    p--;
	    }
}
#endif /* HAVE_POLL_H */

void
Master::run_selects(bool more_tasks)
{
    // Wait in select() for input or timer, and call relevant elements'
    // selected() methods.

    if (!_master_lock.attempt())
	return;
    if (_master_paused > 0 || !_select_lock.attempt())
	goto unlock_master_exit;

    // Return early if there are no selectors and there are tasks to run.
    if (_pollfds.size() == 0 && more_tasks)
	goto unlock_select_exit;

    // Call the relevant selector implementation.
#if HAVE_SYS_EVENT_H && HAVE_KQUEUE
    if (_kqueue >= 0) {
	run_selects_kqueue(more_tasks);
	goto unlock_select_exit;
    }
#endif
#if HAVE_POLL_H
    run_selects_poll(more_tasks);
#else
    run_selects_select(more_tasks);
#endif

 unlock_select_exit:
    _select_lock.release();
 unlock_master_exit:
    _master_lock.release();
}

#endif


// SIGNALS

#if CLICK_USERLEVEL

extern "C" {
static void
sighandler(int signo)
{
    sigset_t sigset;
    sigemptyset(&sigset);
    sigaddset(&sigset, signo);
    sigprocmask(SIG_BLOCK, &sigset, 0);
#if !HAVE_SIGACTION
    signal(signo, SIG_DFL);
#endif
    if (signo >= 0 && signo < 32)
	Master::signals_pending |= (1 << signo);
}
}

int
Master::add_signal_handler(int signo, Router *router, const String &handler)
{
    if (signo < 0 || signo >= 32 || router->master() != this)
	return -1;

    SignalInfo *si = new SignalInfo;
    if (!si)
	return -1;
    si->signo = signo;
    si->router = router;
    si->handler = handler;

    _signal_lock.acquire();
    _signal_adding = true;
    (void) remove_signal_handler(signo, router, handler);
    _signal_adding = false;

    si->next = _siginfo;
    _siginfo = si;
    
#if HAVE_SIGACTION
    struct sigaction sa;
    memset(&sa, 0, sizeof(struct sigaction));
    sa.sa_handler = sighandler;
    sa.sa_flags = SA_RESETHAND;
    sigaction(signo, &sa, 0);
#else
    signal(signo, sighandler);
#endif

    _signal_lock.release();
    return 0;
}

int
Master::remove_signal_handler(int signo, Router *router, const String &handler)
{
    _signal_lock.acquire();
    int nhandlers = 0, status = -1;
    SignalInfo **pprev = &_siginfo;
    for (SignalInfo *si = *pprev; si; si = *pprev)
	if (si->signo == signo && si->router == router
	    && si->handler == handler) {
	    *pprev = si->next;
	    delete si;
	    status = 0;
	} else {
	    if (si->signo == signo)
		nhandlers = 1;
	    pprev = &si->next;
	}

    if (!_signal_adding && status >= 0 && nhandlers == 0)
	signal(signo, SIG_DFL);
    _signal_lock.release();
    return status;
}

void
Master::process_signals()
{
    uint32_t had_signals = signals_pending.swap(0);
    uint32_t unhandled_signals = had_signals;
    _signal_lock.acquire();

    SignalInfo *happened = 0;
    SignalInfo **pprev = &_siginfo;
    for (SignalInfo *si = *pprev; si; si = *pprev)
	if ((had_signals & (1 << si->signo)) && si->router->running()) {
	    *pprev = si->next;
	    si->next = happened;
	    happened = si;
	} else
	    pprev = &si->next;

    while (happened) {
	SignalInfo *next = happened->next;
	if (HandlerCall::call_write(happened->handler, happened->router->root_element()) >= 0)
	    unhandled_signals &= ~(1 << happened->signo);
	delete happened;
	happened = next;
    }

    sigset_t sigset;
    sigemptyset(&sigset);
    for (int signo = 0; signo < 32; signo++)
	if (had_signals & (1 << signo))
	    sigaddset(&sigset, signo);
    sigprocmask(SIG_UNBLOCK, &sigset, 0);

    for (int signo = 0; unhandled_signals; signo++)
	if (unhandled_signals & (1 << signo)) {
	    unhandled_signals &= ~(1 << signo);
	    kill(getpid(), signo);
	}

    _signal_lock.release();
}

#endif


// NS

#if CLICK_NS

void
Master::initialize_ns(simclick_sim siminst, simclick_click clickinst)
{
    assert(!_siminst && !_clickinst);
    _siminst = siminst;
    _clickinst = clickinst;
}

#endif


#if CLICK_DEBUG_MASTER
#include <click/straccum.hh>

String
Master::info() const
{
    StringAccum sa;
    sa << "paused:\t" << _master_paused << '\n';
    sa << "stopper:\t" << _stopper << '\n';
    sa << "pending:\t" << (_task_list._pending_next != &_task_list) << '\n';
    for (int i = 0; i < _threads.size(); i++) {
	RouterThread *t = _threads[i];
	sa << "thread " << (i - 1) << ":";
# ifdef CLICK_LINUXMODULE
	if (t->_sleeper)
	    sa << "\tsleep";
	else
	    sa << "\twake";
# endif
	if (t->_pending)
	    sa << "\tpending";
	sa << '\n';
    }
    return sa.take_string();
}

#endif


#if CLICK_USERLEVEL
// Vector template instance
# include <click/vector.cc>
#endif
CLICK_ENDDECLS


syntax highlighted by Code2HTML, v. 0.9.1