/* -*- c -*-
|| This file is part of Pike. For copyright information see COPYRIGHT.
|| Pike is distributed under GPL, LGPL and MPL. See the file COPYING
|| for more information.
|| $Id: backend.cmod,v 1.141 2004/08/23 12:36:35 mast Exp $
*/

#include "global.h"
RCSID("$Id: backend.cmod,v 1.141 2004/08/23 12:36:35 mast Exp $");
#include "fdlib.h"
#include "backend.h"
#include <errno.h>
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#include <string.h>
#include "interpret.h"
#include "object.h"
#include "pike_error.h"
#include "fd_control.h"
#include "main.h"
#include "callback.h"
#include "threads.h"
#include "fdlib.h"
#include <math.h>
#include "interpret.h"
#include "stuff.h"
#include "bignum.h"
#include "svalue.h"
#include "gc.h"

/*
 * Things to do
 *
 * o what happens to callbacks on destruct?
 *
 *   They will just cease to generate any events. If the callback
 *   container object uses the old callback interface and has added an
 *   extra ref to itself to account for the callback connection it
 *   will become garbage. The new interface fixes this. /mast
 *
 * o automatic callback assignment based on current thread
 *
 *   Sounds very odd to me. /mast
 */


#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#else
/* BeOS socket (select etc) stuff */
#ifdef HAVE_NET_SOCKET_H
#include <net/socket.h>
#endif
#endif
#include <sys/stat.h>

/* #define POLL_DEBUG */

#ifdef POLL_DEBUG
#define IF_PD(x)	x
#else /* !POLL_DEBUG */
#define IF_PD(x)
#endif /* POLL_DEBUG */

/* Declarations for the legacy backend interface stuff. */

#include "block_alloc_h.h"
BLOCK_ALLOC (compat_cb_box, N/A)
static int compat_box_dispatcher (struct fd_callback_box *box, int event);
static void update_fd_set (struct Backend_struct *me, int fd,
			   int old_events, int new_events);

/* CALL OUT STUFF */

struct call_out_s
{
  INT32 pos;
  struct timeval tv;
  struct call_out_s *next; /* For block alloc */
  struct call_out_s *next_fun;
  struct call_out_s **prev_fun;
  struct call_out_s *next_arr;
  struct call_out_s **prev_arr;
  struct object *caller;
  struct array *args;
};

#include "block_alloc.h"

#ifdef PIKE_DEBUG
#define MESS_UP_BLOCK(X) do {\
 (X)->next_arr=(struct call_out_s *)(ptrdiff_t)-1; \
 (X)->next_fun=(struct call_out_s *)(ptrdiff_t)-1; \
 (X)->prev_arr=(struct call_out_s **)(ptrdiff_t)-1; \
 (X)->prev_fun=(struct call_out_s **)(ptrdiff_t)-1; \
 (X)->caller=(struct object *)(ptrdiff_t)-1; \
 (X)->args=(struct array *)(ptrdiff_t)-1; \
 (X)->pos=-1; \
 } while(0)
#else
#define MESS_UP_BLOCK(X)
#endif

#undef EXIT_BLOCK
#define EXIT_BLOCK(X) do { \
  *(X->prev_arr)=X->next_arr; \
  if(X->next_arr) X->next_arr->prev_arr=X->prev_arr; \
  *(X->prev_fun)=X->next_fun; \
  if(X->next_fun) X->next_fun->prev_fun=X->prev_fun; \
  MESS_UP_BLOCK(X); \
  } while(0)
BLOCK_ALLOC_FILL_PAGES(call_out_s, 2)

#define alloc_call_out()	debug_malloc_pass(alloc_call_out_s())
#define really_free_call_out(X)	really_free_call_out_s(debug_malloc_pass(X))

typedef struct call_out_s call_out;

struct hash_ent
{
  call_out *arr;
  call_out *fun;
};



DECLARATIONS


struct callback_list do_debug_callbacks;
struct timeval current_time;
struct timeval next_timeout;

/*
 * Stuff to map fds to the proper Backend
 */
static struct Backend_struct **fd_map=0;
static int fd_map_size=0;
PMOD_EXPORT struct Backend_struct *default_backend = NULL;
#ifdef DO_PIKE_CLEANUP
static int num_active_backends = 0;
#endif

static int backend_do_call_outs(struct Backend_struct *me);
static void backend_verify_call_outs(struct Backend_struct *me);
static void backend_cleanup();

struct Backend_struct *get_backend_for_fd(int fd)
{
  if(fd<0 || fd>=fd_map_size) return 0;
  return fd_map[fd];
}

static void low_set_backend_for_fd(int fd, struct Backend_struct *b)
{
#ifdef PIKE_DEBUG
  if(fd<0) Pike_fatal("set_backend_for(%d)\n",fd);
#endif
  if (!b) {
    /* Unregister the fd. */
    if (fd < fd_map_size) {
      fd_map[fd] = NULL;
    }
    return;
  }
  if(fd >= fd_map_size)
  {
    int old=fd_map_size;
    if(!fd_map_size) fd_map_size=64;
    while(fd >= fd_map_size) fd_map_size*=2;
    if (fd_map) {
      fd_map = (struct Backend_struct **)
	realloc(fd_map, sizeof(struct Backend_struct *) * fd_map_size);
    } else {
      fd_map = (struct Backend_struct **)
	malloc(sizeof(struct Backend_struct *) * fd_map_size);
    }
    if(!fd_map)
      Pike_fatal("Out of memory in backend:low_set_backend_for_fd.\n"
	"Tried to allocate %d bytes.\n",sizeof(struct Backend_struct *) * fd_map_size);
    
    MEMSET(fd_map+old,0,sizeof(struct Backend_struct *) * (fd_map_size-old));
  }
  fd_map[fd]=b;
}

struct Backend_struct *really_get_backend_for_fd(int fd)
{
  struct Backend_struct *b;
  if((b=get_backend_for_fd(fd)))
    return b;

#ifdef PIKE_DEBUG
  if(!default_backend)
    Pike_fatal("No backend!\n");
#endif
  low_set_backend_for_fd(fd, default_backend);
  return default_backend;
}

/*
 * POLL/SELECT selection
 */

#ifndef HAVE_AND_USE_POLL
/* Various BSDs have simulated poll(2) APIs. */
#undef HAVE_POLL
#endif

#ifdef HAVE_POLL

/*
 * Backends using poll(2) or similar.
 */

#ifdef HAVE_POLL_H
#include <poll.h>
#endif /* HAVE_POLL_H */

#ifdef HAVE_SYS_POLL_H
#include <sys/poll.h>
#endif /* HAVE_SYS_POLL_H */

/* Some constants... */

/* Notes on POLLRDNORM and POLLIN:
 *
 * According to the AIX manual, POLLIN and POLLRDNORM are both set
 * if there's a nonpriority message on the read queue. POLLIN is
 * also set if the message is of 0 length.
 */

#ifndef POLLRDNORM
#define POLLRDNORM	POLLIN
#endif /* !POLLRDNORM */

#ifndef POLLRDBAND
#define POLLRDBAND	POLLPRI
#endif /* !POLLRDBAND */

#ifndef POLLWRNORM
#define POLLWRNORM	POLLOUT
#endif /* POLLWRNORM */

#ifndef POLLWRBAND
#define POLLWRBAND	POLLOUT
#endif /* !POLLWRBAND */

#define MY_POLLIN POLLRDNORM|POLLIN
#define MY_POLLOUT POLLWRNORM|POLLOUT

#define MY_POLLEXCEPT	POLLRDBAND|POLLRDNORM|POLLIN
#define MY_POLLRDBAND	POLLRDBAND|POLLPRI
#define MY_POLLWREXCEPT	POLLWRBAND|POLLWRNORM|POLLOUT
#define MY_POLLWRBAND	POLLWRBAND|MY_POLLOUT
#define MY_POLLNVAL	POLLNVAL

#if (POLLRDBAND != POLLRDNORM) && (POLLRDBAND != POLLIN)
#define RDBAND_IS_SPECIAL
#endif

#if (POLLWRBAND != POLLOUT) && (POLLWRBAND != POLLWRNORM)
#define WRBAND_IS_SPECIAL
#endif

#define TIMEOUT_IS_MILLISECONDS

#if defined(HAVE_SYS_DEVPOLL_H) && defined(PIKE_POLL_DEVICE)
/*
 * Backend using /dev/poll-style poll device.
 *
 * Used on:
 *   Solaris 7 + patches and above.
 *   OSF/1 + patches and above.
 *   IRIX 5.6.15m and above.
 */
#include <sys/devpoll.h>

#define USE_POLL_DEVICE
#define USE_DEVPOLL
#define POLL_EVENT	struct pollfd
#define OPEN_POLL_DEVICE()	open(PIKE_POLL_DEVICE, O_RDWR)
#define CHILD_NEEDS_TO_REOPEN

#define DECLARE_POLL_EXTRAS		\
  POLL_EVENT poll_fds[POLL_SET_SIZE];	\
  struct dvpoll poll_request = {	\
    poll_fds,				\
    POLL_SET_SIZE,			\
    0,					\
  }

#define MY_POLL(PFD, TIMEOUT)					\
  ((poll_request.dp_timeout = (TIMEOUT)),			\
   (ioctl(PFD, DP_POLL, &poll_request, sizeof(poll_request))))

int POLL_DEVICE_SET_EVENTS(int pfd, int fd, INT32 events)
{
  struct pollfd poll_state[2];
  int e;
  int sz = sizeof(poll_state);

  /* NOTE: POLLREMOVE must (unfortunately) be a separate request. */
  poll_state[0].fd = fd;
  poll_state[0].events = POLLREMOVE;
  poll_state[0].revents = 0;
  poll_state[1].fd = fd;
  poll_state[1].events = events;
  poll_state[1].revents = 0;

  if (!events) {
    /* We're not interrested in the fd anymore. */
    sz = sizeof(poll_state[0]);
  }
  while (((e = write(pfd, poll_state, sz)) < 0) && (errno == EINTR))
    ;

  if (e < 0) {
    Pike_fatal("Failed to set state for fd %d in " PIKE_POLL_DEVICE
	       " (errno:%d).\n",
	       fd, errno);
  }

  /* FIXME: Probably ought to support partial writes. */
  if (e != sz) {
    Pike_fatal("Failed to set state for fd %d in " PIKE_POLL_DEVICE
	       " short write (%d != %d).\n",
	       fd, e, (int)sizeof(poll_state));
  }
  return e;
}
	
#elif defined(HAVE_SYS_EPOLL_H) && defined(PIKE_POLL_DEVICE)
/*
 * Backend using /dev/epoll-style poll device.
 *
 * Used on:
 *   Linux 2.6 and above.
 * Note:
 *   Some libc's are missing wrappers for the system calls, so
 *   we include the appropriate wrappers below.
 */
#include <sys/epoll.h>

/* The following three are defined by <gnu/stubs.h> which is included
 * from <features.h> which is included from just about everywhere, so
 * it is safe to assume that they have been defined if appropriate.
 */
#if defined(__stub_epoll_create) || defined(__stub_epoll_ctl) || \
    defined(__stub_epoll_wait)
/* We have a libc without the wrappers for epoll support.
 */
#ifdef HAVE_SYSCALL_H
#include <syscall.h>
#elif defined(HAVE_SYS_SYSCALL_H)
#include <sys/syscall.h>
#endif /* HAVE_SYSCALL_H || HAVE_SYS_SYSCALL_H */
#ifndef __NR_epoll_create
/* Our libc doesn't even know the syscall numbers for the epoll syscalls.
 */
#ifdef __i386__
#define __NR_epoll_create 254
#define __NR_epoll_ctl 255
#define __NR_epoll_wait 256
#elif defined(__ia64__)
#define __NR_epoll_create 1243
#define __NR_epoll_ctl 1244
#define __NR_epoll_wait 1245
#elif defined(__x86_64__)
#define __NR_epoll_create 214
#define __NR_epoll_ctl 233
#define __NR_epoll_wait 232
#else /* cpu types */
#error Syscall numbers for epoll_create et al not known on this architecture.
#endif /* cpu types */
#endif /* !defined(__NR_epoll_create) */
#if defined(_syscall1) && defined(_syscall4)
_syscall1(int, epoll_create, int, size);                               
_syscall4(int, epoll_ctl, int, epfd, int, op, int, fd,
	  struct epoll_event *, event);
_syscall4(int, epoll_wait, int, epfd, struct epoll_event *, pevents,
	  int, maxevents, int, timeout);
#undef __stub_epoll_create
#undef __stub_epoll_ctl
#undef __stub_epoll_wait
#else /* !_syscall1 || !_syscall4 */
#error Missing macros for generation of syscall wrappers.
#endif /* _syscall1 && _syscall4 */
#endif /* __stub_epoll_{create, ctl, wait} */

#define USE_POLL_DEVICE
#define USE_DEVEPOLL
#define POLL_EVENT	struct epoll_event
#define GET_FD(EVENT)		EVENT.data.fd
#define GET_EVENTS(EVENT)	EVENT.events

/* FIXME: Might want another value instead on POLL_SET_SIZE. */
#define OPEN_POLL_DEVICE()	epoll_create(POLL_SET_SIZE)

#define DECLARE_POLL_EXTRAS		\
  POLL_EVENT poll_fds[POLL_SET_SIZE]

#define MY_POLL(PFD, TIMEOUT)				\
  epoll_wait(PFD, poll_fds, POLL_SET_SIZE, TIMEOUT)

int POLL_DEVICE_SET_EVENTS(int pfd, int fd, INT32 events)
{
  int e;
  if (events) {
    struct epoll_event ev;
    ev.events = events;
    ev.data.fd = fd;

    /* The /dev/epoll interface exposes kernel implementation details...
     */
    IF_PD(fprintf(stderr, "epoll_ctl(%d, EPOLL_CTL_MOD, %d, { 0x%08x, %d })\n",
		  pfd, fd, events, fd));
    while (((e = epoll_ctl(pfd, EPOLL_CTL_MOD, fd, &ev)) < 0)  &&
	   (errno == EINTR))
      ;
    if ((e < 0) && (errno == ENOENT)) {
      IF_PD(fprintf(stderr,
		    "epoll_ctl(%d, EPOLL_CTL_ADD, %d, { 0x%08x, %d })\n",
		    pfd, fd, events, fd));
      while (((e = epoll_ctl(pfd, EPOLL_CTL_ADD, fd, &ev)) < 0)  &&
	     (errno == EINTR))
	;
    }
  } else {
    struct epoll_event dummy;
    /* The last argument must be a proper struct pointer even
     * though it isn't used...
     */
    IF_PD(fprintf(stderr, "epoll_ctl(%d, EPOLL_CTL_DEL, %d, &dummy)\n",
		  pfd, fd));
    while (((e = epoll_ctl(pfd, EPOLL_CTL_DEL, fd, &dummy)) < 0) &&
	   (errno == EINTR))
      ;
    if ((e < 0) && (errno == ENOENT)) return 0;
  }
  IF_PD(if (e < 0) {
    fprintf(stderr, "epoll_ctl() failed with errno: %d\n", errno);
  });
    
  return e;
}

#else /* !USE_POLL_DEVICE */

/*
 * Backend using poll(2).
 *
 * This is used on most older SVR4- or POSIX-style systems.
 */

#define MY_POLL(SET, TIMEOUT)				\
  poll((SET).poll_fds, (SET).num_in_poll, (TIMEOUT))

struct selectors
{
  struct pollfd *poll_fds;
  int poll_fd_size;
  int num_in_poll;
};

#define active_selectors selectors

static void MY_FD_SET(struct selectors *me,
		      int fd,
		      int add)
{
  int i;
  IF_PD(fprintf(stderr, "BACKEND: MY_FD_SET(%d, 0x%04x)\n", fd, add));
  for(i=0; i<me->num_in_poll; i++)
  {
    if(me->poll_fds[i].fd == fd)
    {
      me->poll_fds[i].events |= add;
      return;
    }
  }
  me->num_in_poll++;
  if (me->num_in_poll > me->poll_fd_size)
  {
    me->poll_fd_size += me->num_in_poll;	/* Usually a doubling */
    if (me->poll_fds) {
      me->poll_fds =
	realloc(me->poll_fds, sizeof(struct pollfd)*me->poll_fd_size);
    } else {
      me->poll_fds = malloc(sizeof(struct pollfd)*me->poll_fd_size);
    }
    if (!me->poll_fds)
    {
      Pike_fatal("Out of memory in backend::MY_FD_SET()\n"
	    "Tried to allocate %d pollfds\n", me->poll_fd_size);
    }
  }
  me->poll_fds[me->num_in_poll-1].fd = fd;
  me->poll_fds[me->num_in_poll-1].events = add;
}

static void MY_FD_CLR(struct selectors *me,
		      int fd,
		      int sub)
{
  int i;
  IF_PD(fprintf(stderr, "BACKEND: POLL_FD_CLR(%d, 0x%04x)\n", fd, sub));
  if(!me->poll_fds) return;
  for(i=0; i<me->num_in_poll; i++)
  {
    if(me->poll_fds[i].fd == fd)
    {
      me->poll_fds[i].events &= ~sub;
      if(!me->poll_fds[i].events)
      {
	/* Note that num_in_poll is decreased here.
	 * This is to avoid a lot of -1's below.
	 * /grubba
	 */
	me->num_in_poll--;
	if(i != me->num_in_poll)
	{
	  me->poll_fds[i] = me->poll_fds[me->num_in_poll];
	}
	/* Might want to shrink poll_fds here, but probably not. */
      }
      break;
    }
  }
}
  

static void copy_selectors(struct active_selectors *to,
			   struct selectors *from)
{
  IF_PD(fprintf(stderr, "BACKEND: copy_poll_set() from->num_in_poll=%d\n",
		from->num_in_poll));
  
  if (to->poll_fd_size < from->num_in_poll)
  {
    IF_PD(fprintf(stderr, "BACKEND: copy_poll_set() size %d -> %d\n",
		  to->poll_fd_size,
		  from->poll_fd_size));
    to->poll_fd_size=from->poll_fd_size;
    if (to->poll_fds) {
      to->poll_fds =
	realloc(to->poll_fds, sizeof(struct pollfd)*to->poll_fd_size);
    } else {
      to->poll_fds =
	malloc(sizeof(struct pollfd)*to->poll_fd_size);
    }
    if (!to->poll_fds) {
      Pike_fatal("Out of memory in backend::copy_poll_set()\n"
	    "Tried to allocate %d pollfds\n", to->poll_fd_size);
    }
  }
  
  MEMCPY(to->poll_fds,
	 from->poll_fds,
	 sizeof(struct pollfd)*from->num_in_poll);
  to->num_in_poll=from->num_in_poll;
}

#endif /* HAVE_SYS_DEVPOLL_H || HAVE_SYS_EPOLL_H */

#ifndef GET_FD
#define GET_FD(EVENT)		EVENT.fd
#endif
#ifndef GET_EVENTS
#define GET_EVENTS(EVENT)	EVENT.revents
#endif

#ifdef USE_POLL_DEVICE

/* NOTE: With poll devices the poll set is recalculated each time it's
 *       potentially changed, so MY_FD_{SET,CLR}() are not used.
 */

#define copy_selectors(TO, FROM)

#endif /* USE_POLL_DEVICE */

#elif defined(HAVE_SYS_EVENT_H) && defined(HAVE_KQUEUE) /* && !HAVE_POLL */
/*
 * Backend using kqueue-style poll device.
 *
 * FIXME: Not fully implemented yet!
 *
 * Used on
 *   FreeBSD 4.1 and above.
 *   MacOS X/Darwin 7.x and above.
 *   Various other BSDs.
 */

#include <sys/event.h>

#define USE_KQUEUE
#define POLL_EVENT	struct kevent
#define OPEN_POLL_DEVICE()	kqueue()
#define CHILD_NEEDS_TO_REOPEN

#define PIKE_POLL_DEVICE	"kqueue"

#define TIMEOUT_IS_TIMESPEC

#define MY_POLLIN	EVFILT_READ
#define MY_POLLOUT	EVFILT_WRITE

/* FIXME: The kqueue API has no documented support for out of band data. */
#define MY_POLLEXCEPT	EVFILT_READ
#define MY_POLLRDBAND	EVFILT_READ
#define MY_POLLWREXCEPT	EVFILT_WRITE
#define MY_POLLWRBAND	EVFILT_WRITE

#define DECLARE_POLL_EXTRAS		\
  POLL_EVENT poll_fds[POLL_SET_SIZE]

#define MY_POLL(SET, TIMEOUT)					\
  kevent((SET), NULL, 0, poll_fds, POLL_SET_SIZE, &(TIMEOUT))

#define GET_FD(EVENT)			EVENT.ident
#define GET_EVENTS(EVENT)		EVENT.filter
#define CHECK_EVENT(EVENT, MASK)	(GET_EVENTS(EVENT) == (MASK))

int MY_FD_CLR(int *pfd, int fd, int filter)
{
  struct kevent ev;

  IF_PD(fprintf(stderr, "BACKEND: EV_DISABLE fd:%d, filter:%d\n", fd, filter));
  /* Note: Use EV_DISABLE in preference to EV_DELETE, since
   *       odds are that the fd will be reenabled, and the
   *       filter is deleted anyway when the fd is closed.
   */
  EV_SET(&ev, fd, filter, EV_DISABLE, 0, 0, 0);
  
  return kevent(*pfd, &ev, 1, NULL, 0, NULL);
}

int MY_FD_SET(int *pfd, int fd, int filter)
{
  struct kevent ev[2];

  IF_PD(fprintf(stderr, "BACKEND: EV_ADD fd:%d, filter:%d\n", fd, filter));
  EV_SET(ev, fd, filter, EV_ADD, 0, 0, 0);
  /* Note: EV_ADD unfortunately doesn't reenable EV_DISABLED
   *       filters.
   */
  IF_PD(fprintf(stderr, "BACKEND: EV_ENABLE fd:%d, filter:%d\n", fd, filter));
  EV_SET(ev+1, fd, filter, EV_ENABLE, 0, 0, 0);
  
  return kevent(*pfd, ev, 2, NULL, 0, NULL);
}

#define copy_selectors(TO, FROM)

#else  /* !HAVE_POLL && !HAVE_KQUEUE */

/*
 * Backend using select(2)
 *
 * This is used on most older BSD-style systems, and WIN32.
 */

#define USE_SELECT
#define TIMEOUT_IS_TIMEVAL

struct selectors
{
  int max_fd;
  my_fd_set read;
  my_fd_set write;
  /* except == incoming OOB data (or error according to POSIX)
   * outgoing OOB data is multiplexed on write
   */
  my_fd_set except;
};

struct active_selectors
{
  fd_set rset, wset;
  fd_set eset;
  int max_fd;
};

#define MY_POLLIN &me->set.read
#define MY_POLLOUT &me->set.write

#define MY_POLLEXCEPT &me->set.except
#define MY_POLLRDBAND &me->set.except
#define MY_POLLWREXCEPT &me->set.write
#define MY_POLLWRBAND &me->set.write

#define RDBAND_IS_SPECIAL

#define MY_POLL(SET, TIMEOUT)	\
  fd_select((SET).max_fd + 1, &(SET).rset, &(SET).wset, &(SET).eset,	\
	    (TIMEOUT).tv_sec >= 100000000 ? NULL : &(TIMEOUT))

void MY_FD_CLR(struct selectors *me, int fd, my_fd_set *s)
{
  if(fd > me->max_fd) return;
  my_FD_CLR(fd, s);
  if(fd == me->max_fd)
  {
    while(me->max_fd >=0 &&
	  !my_FD_ISSET(me->max_fd, &me->read) &&
	  !my_FD_ISSET(me->max_fd, &me->write)
	  && !my_FD_ISSET(me->max_fd, &me->except)
      )
      me->max_fd--;
  }
}

void MY_FD_SET(struct selectors *me, int fd, my_fd_set *s)
{
  my_FD_SET(fd, s);
  if(fd > me->max_fd) me->max_fd=fd;
}

static void copy_selectors(struct active_selectors *to,
			   struct selectors *from)
{
  fd_copy_my_fd_set_to_fd_set(&to->rset, &from->read, from->max_fd+1);
  fd_copy_my_fd_set_to_fd_set(&to->wset, &from->write, from->max_fd+1);
  fd_copy_my_fd_set_to_fd_set(&to->eset, &from->except, from->max_fd+1);
  to->max_fd=from->max_fd;
}

#endif	/* HAVE_POLL || USE_KQUEUE */

#ifndef POLL_SET_SIZE
#define POLL_SET_SIZE		32
#endif /* !POLL_SET_SIZE */

#ifndef CHECK_EVENT
#define CHECK_EVENT(EVENT, MASK)	(GET_EVENTS(EVENT) & (MASK))
#endif /* CHECK_EVENT */

#ifdef RBBAND_IS_SPECIAL
#  define MY_FD_CLR_RDBAND(SET, FD) MY_FD_CLR (SET, FD, MY_POLLRDBAND)
#else
#  define MY_FD_CLR_RDBAND(SET, FD)
#endif

#ifdef WRBAND_IS_SPECIAL
#  define MY_FD_CLR_WRBAND(SET, FD) MY_FD_CLR (SET, FD, MY_POLLWRBAND)
#else
#  define MY_FD_CLR_WRBAND(SET, FD)
#endif

/*! @module Pike
 */

/*! @class Backend
 */
PIKECLASS Backend
{
  /* Provide a unique count to be able to tell backends apart with _sprintf. */
  static int unused_id = 0;
  CVAR int id;

  /*
   * Backend callbacks
   */
  CVAR struct callback_list backend_callbacks;

  /* Thread currently executing in the backend. */
#ifdef PIKE_THREADS
  CVAR struct thread_state *exec_thread;
#else
  CVAR int exec_thread;		/* 1 if inside the backend. */
#endif

  /*
   * PIPE for waking up
   */
  CVAR int wakeup_pipe_send_fd;
  CVAR struct fd_callback_box wakeup_cb_box;

  /*
   * FD callback data
   */

  /* An array indexed on fd with arbitrary upper and lower bounds. A
   * lower bould is used to cut down the size for backends that
   * handle a single or only a few fd's. */
  CVAR struct fd_callback_box **fd_boxes;
  CVAR int fd_boxes_start, fd_boxes_size;

  /* Callback boxes which don't correspond to any open file. */
  CVAR struct fd_callback_box **inactive_boxes, **free_inactive_box;
  CVAR int inactive_boxes_size;

  /* 
   * POLL/SELECT fd sets
   */
#ifdef OPEN_POLL_DEVICE
  CVAR int set;
#define active_set set
#else /* !USE_POLL_DEVICE */
  CVAR struct selectors set;
  CVAR struct active_selectors active_set;
#endif /* USE_POLL_DEVUCE */

  /*
   * CALL OUT variables
   */
  CVAR int num_pending_calls;           /* no of busy pointers in buffer */
  CVAR call_out **call_buffer;  /* pointer to buffer */
  CVAR int call_buffer_size;     /* no of pointers in buffer */
  
  CVAR unsigned int hash_size;
  CVAR unsigned int hash_order;
  CVAR struct hash_ent *call_hash;

  /* Should really exist only in PIKE_DEBUG, but 
   * #ifdefs on the last cvar confuses precompile.pike.
   *	/grubba 2001-03-12
   * Should be fixed now -Hubbe
   */
#ifdef PIKE_DEBUG
  CVAR int inside_call_out;
#endif

  /* The object we're in. This ref isn't refcounted. */
  CVAR struct object *backend_obj;

  DECLARE_STORAGE

  PMOD_EXPORT struct object *get_backend_obj (struct Backend_struct *b)
  {
    return b->backend_obj;
  }

  static int wakeup_callback(struct fd_callback_box *box, int event)
    {
      char buffer[1024];
      fd_read(box->fd, buffer, sizeof(buffer)); /* Clear 'flag' */
      return 0;
    }

  /* This is used by threaded programs and signals to wake up the
   * master 'thread'.
   */
  void backend_wake_up_backend(struct Backend_struct *me)
    {
      char foo=0;

      if(me && me->exec_thread)
	fd_write(me->wakeup_pipe_send_fd, &foo ,1);
    }

  /*
   * Backend callbacks.
   */

  PMOD_EXPORT struct callback *backend_debug_add_backend_callback(
    struct Backend_struct *me, callback_func call, void *arg,
    callback_func free_func)
  {
    return add_to_callback(& me->backend_callbacks, call, arg, free_func);
  }

  /*
   * Call outs.
   */

#undef CAR
#undef CDR

#define CAR(X) (((X)<<1)+1)
#define CDR(X) (((X)<<1)+2)
#define PARENT(X) (((X)-1)>>1)
#define CALL_(X) (me->call_buffer[(X)])
#define CALL(X) ((struct call_out_s *)debug_malloc_pass(CALL_(X)))
#define MOVECALL(X,Y) do { INT32 p_=(X); (CALL_(p_)=CALL(Y))->pos=p_; }while(0)
#define CMP(X,Y) my_timercmp(& CALL(X)->tv, <, & CALL(Y)->tv)
#define SWAP(X,Y) do{ call_out *_tmp=CALL(X); (CALL_(X)=CALL(Y))->pos=(X); (CALL_(Y)=_tmp)->pos=(Y); } while(0)

#ifdef PIKE_DEBUG
#define PROTECT_CALL_OUTS() \
   if(me->inside_call_out) Pike_fatal("Recursive call in call_out module.\n"); \
   me->inside_call_out=1
 
#define UNPROTECT_CALL_OUTS() \
   me->inside_call_out=0
#else /* !PIKE_DEBUG */
#define PROTECT_CALL_OUTS()
#define UNPROTECT_CALL_OUTS()
#endif /* PIKE_DEBUG */

#ifdef PIKE_DEBUG
 
 static void backend_verify_call_outs(struct Backend_struct *me)
   {
     struct array *v;
     int e,d;
     
     if(!d_flag) return;
     if(!me->call_buffer) return;

     if(me->num_pending_calls<0 || me->num_pending_calls>me->call_buffer_size)
       Pike_fatal("Error in call out tables.\n");

     if(d_flag<2) return;

     for(e=0;e<me->num_pending_calls;e++)
     {
       if(e)
       {
	 if(CMP(e, PARENT(e)))
	   Pike_fatal("Error in call out heap. (@ %d)\n",e);
       }

       if(!(v=CALL(e)->args))
	 Pike_fatal("No arguments to call.\n");

       if(v->refs < 1)
	 Pike_fatal("Array should have at least one reference.\n");
       
       if(v->malloced_size<v->size)
	 Pike_fatal("Impossible array.\n");
       
       if(!v->size)
	 Pike_fatal("Call out array of zero size!\n");
       
       if(CALL(e)->prev_arr[0] != CALL(e))
	 Pike_fatal("call_out[%d]->prev_arr[0] is wrong!\n",e);
       
       if(CALL(e)->prev_fun[0] != CALL(e))
	 Pike_fatal("call_out[%d]->prev_fun[0] is wrong!\n",e);
       
       if(CALL(e)->pos != e)
	 Pike_fatal("Call_out->pos is not correct!\n");

       if(d_flag>4)
       {
	 for(d=e+1;d<me->num_pending_calls;d++)
	   if(CALL(e)->args == CALL(d)->args)
	     Pike_fatal("Duplicate call out in heap.\n");
       }
     }
     
     for(d=0;d<10 && e<me->call_buffer_size;d++,e++) {
       if (CALL(e)) Pike_fatal("Call out left in buffer.\n");
     }

     for(e=0;e<(int)me->hash_size;e++)
     {
       call_out *c,**prev;
       for(prev=& me->call_hash[e].arr;(c=*prev);prev=& c->next_arr)
       {
	 if(c->prev_arr != prev)
	   Pike_fatal("c->prev_arr is wrong %p.\n",c);

	 if(c->pos<0)
	   Pike_fatal("Free call_out in call_out hash table %p.\n",c);
       }

       for(prev=& me->call_hash[e].fun;(c=*prev);prev=& c->next_fun)
       {
	 if(c->prev_fun != prev)
	   Pike_fatal("c->prev_fun is wrong %p.\n",c);
	 
	 if(c->pos<0)
	   Pike_fatal("Free call_out in call_out hash table %p.\n",c);
       }
     }
   }


#else
#define backend_verify_call_outs(X)
#endif


 static void adjust_down(struct Backend_struct *me,int pos)
   {
     while(1)
     {
       int a=CAR(pos), b=CDR(pos);
       if(a >= me->num_pending_calls) break;
       if(b < me->num_pending_calls)
	 if(CMP(b, a))
	   a=b;
       
       if(CMP(pos, a)) break;
       SWAP(pos, a);
       pos=a;
     }
   }
 
 static int adjust_up(struct Backend_struct *me,int pos)
   {
     int parent=PARENT(pos);
     int from;
#ifdef PIKE_DEBUG
     if(pos <0 || pos>=me->num_pending_calls)
       Pike_fatal("Bad argument to adjust_up(%d)\n",pos);
#endif
     if(!pos) return 0;
     
     if(CMP(pos, parent))
     {
       SWAP(pos, parent);
       from=pos;
       pos=parent;
       while(pos && CMP(pos, PARENT(pos)))
       {
	 parent=PARENT(pos);
	 SWAP(pos, parent);
	 from=pos;
	 pos=parent;
       }
       from+=from&1 ? 1 : -1;
       if(from < me->num_pending_calls && CMP(from, pos))
       {
	 SWAP(from, pos);
	 adjust_down(me,from);
       }
       return 1;
     }
     return 0;
   }
 
 static void adjust(struct Backend_struct *me,int pos)
   {
     if(!adjust_up(me,pos)) adjust_down(me,pos);
   }
 
/* start a new call out, return 1 for success */
 static struct array * new_call_out(struct Backend_struct *me,
				    int num_arg,
				    struct svalue *argp)
   {
     call_out *new;
     struct array *args;
     size_t hval;
     
     PROTECT_CALL_OUTS();
     if(me->num_pending_calls==me->call_buffer_size)
     {
       /* here we need to allocate space for more pointers */
       call_out **new_buffer;

       if(!me->call_buffer)
       {
	 me->call_buffer_size=128;
	 me->call_buffer=(call_out **)xalloc(sizeof(call_out *)*me->call_buffer_size);
	 if(!me->call_buffer) return 0;
	 MEMSET(me->call_buffer, 0, sizeof(call_out *)*me->call_buffer_size);
	 me->num_pending_calls=0;
	 
	 me->hash_size=hashprimes[me->hash_order];
	 me->call_hash=(struct hash_ent *)xalloc(sizeof(struct hash_ent)*me->hash_size);
	 MEMSET(me->call_hash, 0, sizeof(struct hash_ent)*me->hash_size);
       }else{
	 struct hash_ent *new_hash;
	 int e;
	 
	 new_buffer = (call_out **)
	   realloc((char *)me->call_buffer,
		   sizeof(call_out *)*me->call_buffer_size*2);
	 if(!new_buffer)
	   Pike_error("Not enough memory for another call_out\n");
	 MEMSET(new_buffer + me->call_buffer_size, 0,
		sizeof(call_out *)*me->call_buffer_size);
	 me->call_buffer_size*=2;
	 me->call_buffer=new_buffer;

	 if((new_hash=(struct hash_ent *)malloc(sizeof(struct hash_ent)*
						hashprimes[me->hash_order+1])))
	 {
	   free((char *)me->call_hash);
	   me->call_hash=new_hash;
	   me->hash_size=hashprimes[++me->hash_order];
	   MEMSET(me->call_hash, 0, sizeof(struct hash_ent)*me->hash_size);

	   /* Re-hash */
	   for(e=0;e<me->num_pending_calls;e++)
	   {
	     call_out *c=CALL(e);
	     hval=PTR_TO_INT(c->args);

#define LINK(X,c)							\
	     hval%=me->hash_size;					\
	     if((c->PIKE_CONCAT(next_,X)=me->call_hash[hval].X))	\
	       c->PIKE_CONCAT(next_,X)->PIKE_CONCAT(prev_,X)=		\
		 &c->PIKE_CONCAT(next_,X);				\
	     c->PIKE_CONCAT(prev_,X)=&me->call_hash[hval].X;		\
	     me->call_hash[hval].X=c;
	     

	     LINK(arr,c);
	     hval=hash_svalue(c->args->item);
	     LINK(fun,c);
	   }
	 }
       }
     }

     /* time to allocate a new call_out struct */
     args=aggregate_array(num_arg-1);

#ifdef PIKE_DEBUG
     if (CALL(me->num_pending_calls)) {
       Pike_fatal("Lost call out in buffer.\n");
     }
#endif /* PIKE_DEBUG */
     
     CALL_(me->num_pending_calls) = new = alloc_call_out();
     new->pos=me->num_pending_calls;
     
     {
       hval=PTR_TO_INT(args);
       LINK(arr,new);
       hval=hash_svalue(args->item);
       LINK(fun,new);
     }
     
     switch(argp[0].type)
     {
       case T_INT:
	 new->tv.tv_sec=argp[0].u.integer;
	 new->tv.tv_usec=0;
	 break;
	 
       case T_FLOAT:
       {
	 FLOAT_TYPE tmp=argp[0].u.float_number;
	 new->tv.tv_sec = DO_NOT_WARN((long)floor(tmp));
	 new->tv.tv_usec = DO_NOT_WARN((long)(1000000.0 * (tmp - floor(tmp))));
	 break;
       }
       
       default:
	 Pike_fatal("Bad timeout to new_call_out!\n");
     }
     
#ifdef _REENTRANT
     if(num_threads>1)
     {
       struct timeval tmp;
       GETTIMEOFDAY(&tmp);
       my_add_timeval(& new->tv, &tmp);
     }else
#endif
       my_add_timeval(& new->tv, &current_time);
     
     if(Pike_fp && Pike_fp->current_object)
     {
       add_ref(new->caller=Pike_fp->current_object);
     }else{
       new->caller=0;
     }
     
     new->args=args;
     Pike_sp--;
     dmalloc_touch_svalue(Pike_sp);
     
     
     me->num_pending_calls++;
     adjust_up(me, me->num_pending_calls-1);
     backend_verify_call_outs(me);
     
#ifdef _REENTRANT
     backend_wake_up_backend(me);
#endif
     
     UNPROTECT_CALL_OUTS();
     return args;
   }


 /* FIXME add a global function for this? */
 static void backend_count_memory_in_call_outs(struct Backend_struct *me,
				       struct callback *foo,
				       void *bar,
				       void *gazonk)
   {
     push_text("num_call_outs");
     push_int(me->num_pending_calls);
     push_text("call_out_bytes");
     
     push_int64(me->call_buffer_size * sizeof(call_out **)+
		me->num_pending_calls * sizeof(call_out));
   }

   /* FIXME */
#if 0
   MARK 
     {
       int e;
       struct Backend_struct *me=THIS;
       
       for(e=0;e<me->num_pending_calls;e++)
       {
	 gc_mark(CALL(e)->args,0,"call out args");
	 if(CALL(e)->caller)
	   gc_mark(CALL(e)->caller,0,"call out caller");
       }
     }
#endif

/*! @decl mixed call_out(function f, float|int delay, mixed ... args)
 *!
 *! Make a delayed call to a function.
 *!
 *! @[call_out()] places a call to the function @[f] with the argument
 *! @[args] in a queue to be called in about @[delay] seconds.
 *!
 *! If @[f] returns @expr{-1@}, no other call out or callback will be
 *! called by the backend in this round. I.e. @[`()] will return right
 *! away. For the main backend that means it will immediately start
 *! another round and check files and call outs anew.
 *!
 *! @returns
 *!   Returns a call_out identifier that identifies this call_out.
 *!   This value can be sent to eg @[find_call_out()] or @[remove_call_out()].
 *!
 *! @seealso
 *!   @[remove_call_out()], @[find_call_out()], @[call_out_info()]
 */
   PIKEFUN mixed call_out(mixed f, int|float t, mixed ... rest)
     {
       struct svalue tmp;
       struct array *v;
       if(args<2)
	 SIMPLE_TOO_FEW_ARGS_ERROR("call_out", 2);

       if(t->type != T_INT && t->type != T_FLOAT)
	 SIMPLE_BAD_ARG_ERROR("call_out", 2, "int|float");

       /* Swap, for compatibility */
       tmp = Pike_sp[-args];
       Pike_sp[-args] = Pike_sp[1-args];
       Pike_sp[1-args] = tmp;
       
       v = new_call_out(THIS, args, Pike_sp-args);
       ref_push_array(v);
     }

   /* Assumes current_time is correct on entry. */
   static int backend_do_call_outs(struct Backend_struct *me)
     {
       int call_count = 0;
       int args;
       struct timeval tmp;
       backend_verify_call_outs(me);

       tmp.tv_sec = current_time.tv_sec;
       tmp.tv_usec = current_time.tv_usec;
       tmp.tv_sec++;
       while(me->num_pending_calls &&
	     my_timercmp(&CALL(0)->tv, <= ,&current_time))
       {
	 /* unlink call out */
	 call_out c,*cc;
	 
	 PROTECT_CALL_OUTS();
	 cc=CALL(0);
	 if(--me->num_pending_calls)
	 {
	   MOVECALL(0,me->num_pending_calls);
	   adjust_down(me, 0);
	 }
	 CALL_(me->num_pending_calls) = NULL;
	 UNPROTECT_CALL_OUTS();
	 c=*cc;
	 really_free_call_out(cc);
	 
	 if(c.caller) free_object(c.caller);
	 
	 args=c.args->size;
	 push_array_items(c.args);
	 check_destructed(Pike_sp - args);
	 if(Pike_sp[-args].type!=T_INT)
	 {
	   call_count++;
	   f_call_function(args);
	   if (Pike_sp[-1].type == T_INT && Pike_sp[-1].u.integer == -1) {
	     pop_stack();
	     backend_verify_call_outs(me);
	     call_count = -call_count;
	     break;
	   }
	   else
	     pop_stack();
	 }else{
	   pop_n_elems(args);
	 }
	 backend_verify_call_outs(me);
	 
	 GETTIMEOFDAY(&current_time);
	 if(my_timercmp(&current_time, > , &tmp)) break;
       }
       return call_count;
     }


   static int backend_find_call_out(struct Backend_struct *me,
				    struct svalue *fun)
     {
       size_t hval;
       call_out *c;
       
       if(!me->num_pending_calls) return -1;
       
       if(fun->type == T_ARRAY)
       {
	 hval=PTR_TO_INT(fun->u.array);
	 hval%=me->hash_size;
	 for(c=me->call_hash[hval].arr;c;c=c->next_arr)
	 {
	   if(c->args == fun->u.array)
	   {
#ifdef PIKE_DEBUG
	     if(CALL(c->pos) != c)
	       Pike_fatal("Call_out->pos not correct!\n");
#endif
	     return c->pos;
	   }
	 }
       }
       
       hval=hash_svalue(fun);
       hval%=me->hash_size;
       for(c=me->call_hash[hval].fun;c;c=c->next_fun)
       {
	 if(is_eq(fun, ITEM(c->args)))
	 {
#ifdef PIKE_DEBUG
	   if(CALL(c->pos) != c)
	     Pike_fatal("Call_out->pos not correct!\n");
#endif
	   return c->pos;
	 }
       }
       return -1;
     }


/*! @decl int _do_call_outs()
 *!
 *! Do all pending call_outs.
 *!
 *! This function runs all pending call_outs that should have been
 *! run if Pike returned to the backend.  It should not be used in
 *! normal operation.
 *!
 *! As a side-effect, this function sets the value returned by
 *! @[time(1)] to the current time.
 *!
 *! @returns
 *! Zero if no call outs were called, nonzero otherwise.
 *!
 *! @seealso
 *! @[call_out()], @[find_call_out()], @[remove_call_out()]
 */
   PIKEFUN int _do_call_outs()
     {
       GETTIMEOFDAY(&current_time);
       RETURN backend_do_call_outs(THIS);
     }

/*! @decl int find_call_out(function f)
 *! @decl int find_call_out(mixed id)
 *!
 *! Find a call out in the queue.
 *!
 *! This function searches the call out queue. If given a function as
 *! argument, it looks for the first call out scheduled to that function.
 *!
 *! The argument can also be a call out id as returned by @[call_out()], in
 *! which case that call_out will be found (Unless it has already been
 *! called).
 *!
 *! @returns
 *!   @[find_call_out()] returns the remaining time in seconds before that
 *!   call_out will be executed. If no call_out is found,
 *!   @[zero_type](@[find_call_out](f)) will return 1.
 *!
 *! @seealso
 *!   @[call_out()], @[remove_call_out()], @[call_out_info()]
 */
   PIKEFUN int find_call_out(function|mixed f)
     {
       struct Backend_struct *me=THIS;
       int e;
       backend_verify_call_outs(me);

       PROTECT_CALL_OUTS();
       e=backend_find_call_out(me, f);
       pop_n_elems(args);
       if(e==-1)
       {
	 Pike_sp->type = T_INT;
	 Pike_sp->subtype = NUMBER_UNDEFINED;
	 Pike_sp->u.integer=-1;
	 Pike_sp++;
       }else{
	 push_int(CALL(e)->tv.tv_sec - current_time.tv_sec);
       }
       UNPROTECT_CALL_OUTS();
       backend_verify_call_outs(me);
     }

/*! @decl int remove_call_out(function f)
 *! @decl int remove_call_out(function id)
 *!
 *! Remove a call out from the call out queue.
 *!
 *! This function finds the first call to the function @[f] in the call_out
 *! queue and removes it.  You can also give a call out id as argument (as
 *! returned by call_out).
 *!
 *! @returns
 *!   The remaining time in seconds left to that call out will be returned.
 *!   If no call_out was found, @[zero_type](@[remove_call_out](@[f]))
 *!   will return 1.
 *!
 *! @seealso
 *!   @[call_out_info()], @[call_out()], @[find_call_out()]
 */
   PIKEFUN int remove_call_out(function|mixed f)
     {
       struct Backend_struct *me=THIS;
       int e;
       PROTECT_CALL_OUTS();
       backend_verify_call_outs(me);
       e=backend_find_call_out(me,f);
       backend_verify_call_outs(me);
       if(e!=-1)
       {
	 pop_n_elems(args);
	 push_int(CALL(e)->tv.tv_sec - current_time.tv_sec);
	 free_array(CALL(e)->args);
	 if(CALL(e)->caller)
	   free_object(CALL(e)->caller);
	 really_free_call_out(CALL(e));
	 me->num_pending_calls--;
	 if(e!=me->num_pending_calls)
	 {
	   MOVECALL(e,me->num_pending_calls);
	   adjust(me,e);
	 }
	 CALL_(me->num_pending_calls) = NULL;
       }else{
	 pop_n_elems(args);
	 Pike_sp->type = T_INT;
	 Pike_sp->subtype = NUMBER_UNDEFINED;
	 Pike_sp->u.integer = -1;
	 Pike_sp++;
       }
       backend_verify_call_outs(me);
       UNPROTECT_CALL_OUTS();
     }

/* return an array containing info about all call outs:
 * ({  ({ delay, caller, function, args, ... }), ... })
 */
   struct array *backend_get_all_call_outs(struct Backend_struct *me)
     {
       int e;
       struct array *ret;

       backend_verify_call_outs(me);
       PROTECT_CALL_OUTS();
       ret=allocate_array_no_init(me->num_pending_calls,0);
       for(e=0;e<me->num_pending_calls;e++)
       {
	 struct array *v;
	 v=allocate_array_no_init(CALL(e)->args->size+2, 0);
	 ITEM(v)[0].u.integer=CALL(e)->tv.tv_sec - current_time.tv_sec;
	 
	 if(CALL(e)->caller)
	 {
	   ITEM(v)[1].type=T_OBJECT;
	   add_ref(ITEM(v)[1].u.object=CALL(e)->caller);
	   v->type_field = BIT_INT|BIT_OBJECT;
	 }else{
	   v->type_field = BIT_INT;
	 }

	 v->type_field |=
	   assign_svalues_no_free(ITEM(v)+2,
				  ITEM(CALL(e)->args),
				  CALL(e)->args->size,BIT_MIXED);
	 
	 ITEM(ret)[e].type=T_ARRAY;
	 ITEM(ret)[e].u.array=v;
       }
       ret->type_field = BIT_ARRAY;
       UNPROTECT_CALL_OUTS();
       return ret;
     }

/*! @decl array(array) call_out_info()
 *!
 *! Get info about all call_outs.
 *!
 *! This function returns an array with one entry for each entry in the
 *! call out queue. The first in the queue will be at index 0. Each index
 *! contains an array that looks like this:
 *! @array
 *!   @elem int time_left
 *!     Time remaining in seconds until the call_out is to be performed.
 *!   @elem object caller
 *!     The object that scheduled the call_out.
 *!   @elem function fun
 *!     Function to be called.
 *!   @elem mixed ... args
 *!     Arguments to the function.
 *! @endarray
 *!
 *! @seealso
 *!   @[call_out()], @[find_call_out()], @[remove_call_out()]
 */
   PIKEFUN array(array) call_out_info()
     {
       RETURN backend_get_all_call_outs(THIS);
     }

  /*
   * FD box handling
   */

#define GET_ACTIVE_BOX(ME, FD)						\
   ((ME)->fd_boxes[(FD) - (ME)->fd_boxes_start])

#define GET_BOX(ME, FD)							\
   ((FD) < 0 ?								\
    (ME)->inactive_boxes[~(FD)] :					\
    (ME)->fd_boxes[(FD) - (ME)->fd_boxes_start])

#define SAFE_GET_ACTIVE_BOX(ME, FD)					\
   ((FD) >= (ME)->fd_boxes_start &&					\
    (FD) < (ME)->fd_boxes_start + (ME)->fd_boxes_size ?			\
    GET_ACTIVE_BOX (ME, FD) : NULL)

   static struct fd_callback_box *safe_get_box (struct Backend_struct *me, int fd)
   {
     if (fd < 0) {
       fd = ~fd;
       if (fd < me->inactive_boxes_size) {
	 struct fd_callback_box *box = me->inactive_boxes[fd];
	 /* Avoid free list pointers. */
	 if ((struct fd_callback_box **) box < me->inactive_boxes ||
	     (struct fd_callback_box **) box >= me->inactive_boxes + me->inactive_boxes_size)
	   return box;
       }
     }
     else {
       fd -= me->fd_boxes_start;
       if (fd >= 0 && fd < me->fd_boxes_size)
	 return me->fd_boxes[fd];
     }
     return NULL;
   }

/* NOTE: Some versions of AIX seem to have a
 *         #define events reqevents
 *       in one of the poll headerfiles. This will break
 *       the fd_box event handling.
 */
#undef events

   /* Make sure the WANT_EVENT() macro is useable... */
#undef READ
#undef WRITE
#undef READ_OOB
#undef WRITE_OOB
#undef ERROR

#define WANT_EVENT(BOX, WHAT)						\
   ((BOX) && (BOX)->events & PIKE_CONCAT (PIKE_BIT_FD_, WHAT))

#define FOR_EACH_ACTIVE_FD_BOX(ME, BOX_VAR)				\
   struct Backend_struct *me_ = (ME);					\
   struct fd_callback_box *BOX_VAR, **boxes_ = me_->fd_boxes;		\
   int b_, max_ = me_->fd_boxes_size;					\
   for (b_ = 0; b_ < max_; b_++)					\
     if ((BOX_VAR = boxes_[b_]))

#define FOR_EACH_INACTIVE_FD_BOX(ME, BOX_VAR)				\
   struct Backend_struct *me_ = (ME);					\
   struct fd_callback_box *BOX_VAR, **boxes_ = me_->inactive_boxes;	\
   int b_ = 0, max_ = me_->inactive_boxes_size;				\
   for (b_ = 0; b_ < max_; b_++)					\
     if ((BOX_VAR = boxes_[b_]) &&					\
	 /* Avoid free list pointers. */				\
	 ((struct fd_callback_box **) BOX_VAR < boxes_ ||		\
	  (struct fd_callback_box **) BOX_VAR >= boxes_+max_))

#ifdef PIKE_DEBUG
  static void check_box (struct fd_callback_box *box, int fd)
  {
    struct Backend_struct *me;
    if (!box) return;
    if (!(me = box->backend))
      Pike_fatal ("fd_callback_box not hooked to any backend.\n");
    if (fd == INT_MAX)
      fd = box->fd;
    else if (fd != box->fd)
      Pike_fatal ("fd in callback box doesn't correspond to where it's found.\n");
    if (safe_get_box (me, fd) != box)
      Pike_fatal ("fd_callback_box not hooked in correctly for fd %d.\n", box->fd);
  }
#else
#  define check_box(box, fd) do {} while (0)
#endif

   static void add_fd_box (struct fd_callback_box *box)
   {
     struct Backend_struct *me = box->backend;
     int fd = box->fd;

#ifdef PIKE_DEBUG
     if (fd >= 0) {
       struct fd_callback_box *old_box = SAFE_GET_ACTIVE_BOX (me, fd);
       if (old_box == box)
	 Pike_fatal ("The box is already hooked in.\n");
       if (old_box)
	 Pike_fatal ("There's another callback box %p for fd %d.\n",
		     old_box, fd);
       if (get_backend_for_fd (fd) && get_backend_for_fd (fd) != me)
	 Pike_fatal ("The fd is allocated to another backend.\n");
     }
     else {
       int i;
       for (i = 0; i < me->inactive_boxes_size; i++)
	 if (me->inactive_boxes[i] == box)
	   Pike_fatal ("The box is already hooked in.\n");
     }
#endif

     if (fd >= 0) {
       low_set_backend_for_fd (fd, me);

       if (!me->fd_boxes_size) {
	 /* Start small since backends with only a single fd aren't uncommon. */
	 me->fd_boxes_size = 4;
	 me->fd_boxes = calloc (sizeof (me->fd_boxes[0]), me->fd_boxes_size);
	 if (!me->fd_boxes)
	   Pike_fatal ("Out of memory in backend::add_fd_box(): "
		       "Tried to allocate %d fd_callback_box pointers\n",
		       me->fd_boxes_size);
	 me->fd_boxes_start = fd;
	 fd = 0;
       }

       else if (fd < me->fd_boxes_start) {
	 int old_size = me->fd_boxes_size, shift = me->fd_boxes_size;
	 struct fd_callback_box **old_boxes = me->fd_boxes;
	 while (fd < me->fd_boxes_start - shift) shift *= 2;
	 if (me->fd_boxes_start - shift < 0) shift = me->fd_boxes_start;
	 me->fd_boxes_start -= shift;
	 me->fd_boxes_size += shift;
	 debug_malloc_touch(me->fd_boxes);
	 me->fd_boxes =
	   realloc (me->fd_boxes, sizeof (me->fd_boxes[0]) * me->fd_boxes_size);
	 if (!me->fd_boxes)
	   Pike_fatal ("Out of memory in backend::add_fd_box(): "
		       "Tried to allocate %d fd_callback_box pointers\n",
		       me->fd_boxes_size);
	 MEMMOVE (me->fd_boxes + shift, me->fd_boxes,
		  sizeof (me->fd_boxes[0]) * old_size);
	 MEMSET (me->fd_boxes, 0, sizeof (me->fd_boxes[0]) * shift);
	 debug_malloc_touch(me->fd_boxes);
	 fd -= me->fd_boxes_start;
       }

       else {
	 fd -= me->fd_boxes_start;
	 if (fd >= me->fd_boxes_size) {
	   int old_size=me->fd_boxes_size;
	   while(fd >= me->fd_boxes_size) me->fd_boxes_size*=2;
	   debug_malloc_touch(me->fd_boxes);
	   me->fd_boxes =
	     realloc(me->fd_boxes, sizeof(me->fd_boxes[0]) * me->fd_boxes_size);
	   if( !me->fd_boxes )
	     Pike_fatal("Out of memory in backend::add_fd_box(): "
			"Tried to allocate %d fd_callback_box pointers\n",
			me->fd_boxes_size);
	   MEMSET(me->fd_boxes+old_size,
		  0,
		  (me->fd_boxes_size-old_size)*sizeof(me->fd_boxes[0]));
	   debug_malloc_touch(me->fd_boxes);
	 }
       }

       me->fd_boxes[fd] = box;
     }

     else {			/* Add an inactive box. */
       int pos;

       if (!me->free_inactive_box) {
	 pos = me->inactive_boxes_size;
	 if (!me->inactive_boxes_size)
	   me->inactive_boxes_size = 4;
	 else
	   me->inactive_boxes_size *= 2;
	 if (!me->inactive_boxes)
	   me->inactive_boxes =
	     malloc (sizeof (me->inactive_boxes[0]) * me->inactive_boxes_size);
	 else
	   me->inactive_boxes =
	     realloc (me->inactive_boxes,
		      sizeof (me->inactive_boxes[0]) * me->inactive_boxes_size);
	 if (!me->inactive_boxes)
	   Pike_fatal ("Out of memory in backend::add_fd_box(): "
		       "Tried to allocate %d inactive fd_callback_box pointers\n",
		       me->inactive_boxes_size);
	 me->free_inactive_box = me->inactive_boxes + pos;
	 while (++pos < me->inactive_boxes_size)
	   me->inactive_boxes[pos - 1] =
	     (struct fd_callback_box *) (me->inactive_boxes + pos);
	 me->inactive_boxes[pos - 1] = NULL;
       }

       pos = me->free_inactive_box - me->inactive_boxes;
       me->free_inactive_box = (struct fd_callback_box **) *me->free_inactive_box;

       me->inactive_boxes[pos] = box;
       box->fd = ~pos;
     }
   }

   static void remove_fd_box (struct fd_callback_box *box)
   {
     struct Backend_struct *me = box->backend;
     int fd = box->fd;
     check_box (box, INT_MAX);

     /* FIXME: Shrink arrays? */

     if (fd >= 0) {
       low_set_backend_for_fd (fd, NULL);
       me->fd_boxes[fd - me->fd_boxes_start] = NULL;
     }
     else {
       fd = ~fd;
       me->inactive_boxes[fd] = (struct fd_callback_box *) me->free_inactive_box;
       me->free_inactive_box = me->inactive_boxes + fd;
     }
   }

  PMOD_EXPORT void hook_fd_callback_box (struct fd_callback_box *box)
  {
    struct Backend_struct *me = box->backend;
    int fd = box->fd;
    IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: hook_fd_callback_box: "
		  "fd %d, events 0x%x, object %p\n",
		  getpid(), me->id, fd, box->events, box->ref_obj));
#ifdef PIKE_DEBUG
    if (!me) Pike_fatal ("Backend not set.\n");
#endif
    add_fd_box (box);
    if (fd >= 0) update_fd_set (me, fd, 0, box->events);
    if (box->ref_obj && box->events) add_ref (box->ref_obj);
  }

  PMOD_EXPORT void unhook_fd_callback_box (struct fd_callback_box *box)
  {
    /* Accept an unhooked box; can happen when we're called from an
     * object exit hook due to being freed by free_object below. */
    if (!box->backend) {
      IF_PD(fprintf(stderr, "[%d]BACKEND[unhooked box]: unhook_fd_callback_box: "
		    "fd %d, object %p\n", getpid(), box->fd, box->ref_obj));
      return;
    }

    check_box (box, INT_MAX);
    IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: unhook_fd_callback_box: fd %d, object %p\n",
		  getpid(), box->backend->id, box->fd, box->ref_obj));

    if (box->fd >= 0) update_fd_set (box->backend, box->fd, box->events, 0);
    remove_fd_box (box);
    box->backend = NULL;

    if (box->ref_obj && box->events) {
      /* Use gc safe method to allow calls from within the gc. */
      /* box->ref_obj is only converted from a counted to
       * non-counted ref, so it shouldn't be clobbered by the free. */
      union anything u;
      u.object = box->ref_obj;
      gc_free_short_svalue (&u, T_OBJECT);
    }
  }

  PMOD_EXPORT void set_fd_callback_events (struct fd_callback_box *box, int events)
  {
    int old_events = box->events;
    check_box (box, INT_MAX);
    IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: set_fd_callback_events: "
		  "fd %d, events from 0x%x to 0x%x, object %p\n",
		  getpid(), box->backend->id, box->fd, old_events, events,
		  box->ref_obj));

    if (box->fd >= 0) update_fd_set (box->backend, box->fd, old_events, events);
    box->events = events;

    if (box->ref_obj) {
      if (!old_events) {
	if (events) add_ref (box->ref_obj);
      }
      else
	if (!events) {
	  /* Use gc safe method to allow calls from within the gc. */
	  /* box->ref_obj is only converted from a counted to
	   * non-counted ref, so it shouldn't be clobbered by the free. */
	  union anything u;
	  u.object = box->ref_obj;
	  gc_free_short_svalue (&u, T_OBJECT);
	}
    }
  }

  PMOD_EXPORT void change_backend_for_box (struct fd_callback_box *box,
					   struct Backend_struct *new)
  {
    struct Backend_struct *old = box->backend;
    check_box (box, INT_MAX);

#ifdef PIKE_DEBUG
    if (!new) Pike_fatal ("New backend is invalid.\n");
#endif

    IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: change_backend_for_box: "
		  "fd %d, new backend %d\n", getpid(), old->id, box->fd, new->id));

    if (old != new) {
      if (box->fd >= 0) update_fd_set (old, box->fd, box->events, 0);
      remove_fd_box (box);
      box->backend = new;
      add_fd_box (box);
      if (box->fd >= 0) update_fd_set (new, box->fd, 0, box->events);
    }
  }

  PMOD_EXPORT void change_fd_for_box (struct fd_callback_box *box, int new_fd)
  {
    int old_fd = box->fd;

    if (!box->backend) {
      /* Convenience so that the caller doesn't have to check if the
       * box is hooked in. */
      IF_PD(fprintf(stderr, "[%d]BACKEND[unhooked box]: change_fd_for_box: "
		    "fd from %d to %d\n", getpid(), old_fd, new_fd));
      box->fd = new_fd;
    }

    else {
      check_box (box, INT_MAX);

      if (old_fd >= 0 ? old_fd != new_fd : new_fd >= 0) {
	if (old_fd >= 0) update_fd_set (box->backend, old_fd, box->events, 0);
	remove_fd_box (box);
	box->fd = new_fd;
	add_fd_box (box);
	new_fd = box->fd;
	if (new_fd >= 0) update_fd_set (box->backend, new_fd, 0, box->events);
      }

      IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: change_fd_for_box: "
		    "fd from %d to %d\n", getpid(), box->backend->id, old_fd, new_fd));
    }
  }

  /*
   * FD set handling
   */

#ifdef USE_POLL_DEVICE
  void UPDATE_BLACK_BOX(struct Backend_struct *me, int fd, int wanted_events)
  {
    INT32 events = 0;

    if (wanted_events & PIKE_BIT_FD_READ) {
      events |= MY_POLLIN;
    }
    if (wanted_events & PIKE_BIT_FD_WRITE) {
      events |= MY_POLLOUT;
    }
    if (wanted_events & PIKE_BIT_FD_READ_OOB) {
      events |= MY_POLLRDBAND;
    }
    if (wanted_events & PIKE_BIT_FD_WRITE_OOB) {
      events |= MY_POLLWRBAND;
    }

    IF_PD(fprintf(stderr, "UPDATE_BLACK_BOX(%d, %d) ==> events: 0x%08x\n",
		  me->set, fd, events));
    POLL_DEVICE_SET_EVENTS(me->set, fd, events);
  }
#elif defined(USE_KQUEUE)
  /* Note: Only used by REOPEN_POLL_DEVICE on a freshly opened kqueue. */
  void UPDATE_BLACK_BOX(struct Backend_struct *me, int fd, int wanted_events)
  {
    struct kevent ev[2];
    int nev = 0;
    if (wanted_events & PIKE_BIT_FD_READ ||
	wanted_events & PIKE_BIT_FD_READ_OOB) {
      EV_SET(ev, fd, MY_POLLIN, EV_ADD, 0, 0, 0);
      nev++;
    }
    if (wanted_events & PIKE_BIT_FD_WRITE ||
	wanted_events & PIKE_BIT_FD_WRITE_OOB) {
      EV_SET(ev, fd, MY_POLLOUT, EV_ADD, 0, 0, 0);
      nev++;
    }
    if (nev)
      kevent(me->set, ev, nev, NULL, 0, NULL);
  }
#endif /* USE_POLL_DEVICE */

#ifdef CHILD_NEEDS_TO_REOPEN
  /* This is called in the child process to restore
   * poll state after fork() in case of detaching.
   */
  void REOPEN_POLL_DEVICE(struct Backend_struct *me)
  {
    int fd;
    while ((close(me->set) < 0) && (errno == EINTR))
      ;
    while (((fd = OPEN_POLL_DEVICE()) < 0) && (errno == EINTR))
      ;
    if (fd < 0) {
      Pike_fatal("Failed to reopen " PIKE_POLL_DEVICE
		 " after fork (errno: %d).\n", errno);
    }
    if (fd != me->set) {
      int e;
      while (((e = dup2(fd, me->set)) < 0) && (errno == EINTR))
	;
      if (e < 0) {
	/* We hope we can use the fd at the new location... */
	me->set = fd;
      } else {
	while ((close(fd) < 0) && (errno == EINTR))
	  ;
      }
    }
    set_close_on_exec(me->set, 1);

    /* Restore the poll-state for all the fds. */
    {FOR_EACH_ACTIVE_FD_BOX (me, box) {
	UPDATE_BLACK_BOX (me, box->fd, box->events);
      }}
  }

  static struct Backend_struct **backends = NULL;
  static int num_backends = 0;
  static int backends_size = 0;

  /* Called from the init callback. */
  static void register_backend(struct Backend_struct *me)
  {
    if (num_backends == backends_size) {
      struct Backend_struct **new_backends =
	realloc(backends, (backends_size+1)*sizeof(struct Backend_struct *)*2);
      if (!new_backends) {
	Pike_error("Out of memory.\n");
      }
      backends = new_backends;
      backends_size = (backends_size+1)*2;
    }
    backends[num_backends++] = me;
  }

  /* Called from the exit callback. */
  static void unregister_backend(struct Backend_struct *me)
  {
    int i = num_backends;
    /* Search backwards since new backends are more likely to be destructed
     * than old backends.
     */
    while (i--) {
      if (backends[i] == me) {
	backends[i] = backends[--num_backends];
	backends[num_backends] = NULL;
	return;	/* A backend is only supposed to be registered once. */
      }
    }
  }

  /* Called in the child after fork(). */
  static void reopen_all_backends(struct callback *cb, void *a, void *b)
  {
    int i;
    for (i=0; i < num_backends; i++) {
      REOPEN_POLL_DEVICE(backends[i]);
    }
  }
#else /* !CHILD_NEEDS_TO_REOPEN */
#define register_backend(X)
#define unregister_backend(X)
#endif /* CHILD_NEEDS_TO_REOPEN */

  static void update_fd_set (struct Backend_struct *me, int fd,
			     int old_events, int new_events)
  {
    int changed_events = old_events ^ new_events;
    if (changed_events) {

#ifdef USE_POLL_DEVICE

      UPDATE_BLACK_BOX(me, fd, new_events);

#else  /* !USE_POLL_DEVICE */

      if (changed_events & PIKE_BIT_FD_READ) {
	if (new_events & PIKE_BIT_FD_READ) {
	  MY_FD_SET(&me->set, fd, MY_POLLIN);
#ifndef HAVE_POLL
	  /* Got to enable the exception set to get errors (at least
	   * according to POSIX). */
	  MY_FD_SET(&me->set, fd, MY_POLLEXCEPT);
#endif
	}
	else {
	  MY_FD_CLR(&me->set, fd, MY_POLLIN);
#ifdef HAVE_POLL
	  if (new_events & PIKE_BIT_FD_READ_OOB)
	  {
	    MY_FD_SET(&me->set, fd, MY_POLLRDBAND);
	  }
#else
	  if (!(new_events & PIKE_BIT_FD_READ_OOB) &&
	      !(new_events & PIKE_BIT_FD_WRITE))
	    /* Exceptions might cause calls to read, read_oob and write. */
	    MY_FD_CLR(&me->set, fd, MY_POLLEXCEPT);
#endif
	}
      }

      if (changed_events & PIKE_BIT_FD_READ_OOB) {
	if (new_events & PIKE_BIT_FD_READ_OOB)
	  MY_FD_SET(&me->set, fd, MY_POLLRDBAND);
	else {
	  if (!(new_events & PIKE_BIT_FD_READ)) {
#ifdef HAVE_POLL
	    MY_FD_CLR(&me->set, fd, MY_POLLRDBAND);
#else
	    if (!(new_events & PIKE_BIT_FD_WRITE))
	      /* Exceptions might cause calls to read, read_oob and write. */
	      MY_FD_CLR(&me->set, fd, MY_POLLEXCEPT);
#endif
	  } else {
	    MY_FD_CLR_RDBAND(&me->set, fd);
	  }
	}
      }

      if (changed_events & PIKE_BIT_FD_WRITE) {
	if (new_events & PIKE_BIT_FD_WRITE) {
	  MY_FD_SET(&me->set, fd, MY_POLLOUT);
#ifndef HAVE_POLL
	  /* Got to enable the exception set to get errors (at least
	   * according to POSIX). */
	  MY_FD_SET(&me->set, fd, MY_POLLEXCEPT);
#endif
	}
	else {
	  if (!(new_events & PIKE_BIT_FD_WRITE_OOB)) {
	    MY_FD_CLR(&me->set, fd, MY_POLLOUT);
#ifndef HAVE_POLL
	    if (!(new_events & PIKE_BIT_FD_READ) &&
		!(new_events & PIKE_BIT_FD_READ_OOB))
	      /* Exceptions might cause calls to read, read_oob and write. */
	      MY_FD_CLR(&me->set, fd, MY_POLLEXCEPT);
#endif
	  }
	}
      }

      if (changed_events & PIKE_BIT_FD_WRITE_OOB) {
	if (new_events & PIKE_BIT_FD_WRITE_OOB)
	  MY_FD_SET(&me->set, fd, MY_POLLWRBAND);
	else {
	  if (!(new_events & PIKE_BIT_FD_WRITE)) {
	    MY_FD_CLR(&me->set, fd, MY_POLLWREXCEPT);
	  } else {
	    MY_FD_CLR_WRBAND(&me->set, fd);
	  }
	}
      }

#endif	/* !USE_POLL_DEVICE */

      if (new_events & ~old_events)
	/* New events were added. */
	backend_wake_up_backend (me);
    }
  }

#ifdef PIKE_DEBUG

  static void backend_do_debug(struct Backend_struct *me)
    {
      int e;
      PIKE_STAT_T tmp;

      backend_verify_call_outs(me);
      
      /* FIXME: OOB? */
#ifdef USE_SELECT
      for(e=0;e<=me->set.max_fd;e++)
      {
	if(my_FD_ISSET(e, &me->set.read)
	   || my_FD_ISSET(e, &me->set.write)
	   || my_FD_ISSET(e, &me->set.except)
	  )
	{
	  int ret;

	  if (e >= fd_map_size || fd_map[e] != me)
	    Pike_fatal ("Isn't referenced from fd_map for fd %d in select set.\n", e);

	  do {
	    ret = fd_fstat(e, &tmp);
	    /* FIXME: Perhaps do check_threads_etc() here? */
	  }while(ret < 0 && errno == EINTR);

	  if(ret<0)
	  {
	    switch(errno)
	    {
	      case EBADF:
		Pike_fatal("Backend filedescriptor %d is bad.\n",e);
		break;
	      case ENOENT:
		Pike_fatal("Backend filedescriptor %d is not.\n",e);
		break;
	    }
	  }
	}
      }
#else  /* !USE_SELECT */
#if defined(HAVE_POLL) && !defined(USE_POLL_DEVICE)
      for(e=0;e<me->set.num_in_poll;e++)
      {
	int ret;
	int fd = me->set.poll_fds[e].fd;

	if (fd >= fd_map_size || fd_map[fd] != me)
	  Pike_fatal ("Isn't referenced from fd_map for fd %d at %d in poll set.\n",
		      fd, e);

	do {
	  ret=fd_fstat(fd, &tmp);
	  /* FIXME: Perhaps do check_threads_etc() here? */
	}while(ret < 0 && errno == EINTR);

	if(ret<0)
	{
	  switch(errno)
	  {
	    case EBADF:
	      Pike_fatal("Backend filedescriptor %d is bad.\n", fd);
	      break;
	    case ENOENT:
	      Pike_fatal("Backend filedescriptor %d is not.\n", fd);
	      break;
	  }
	}
      }
#endif /* HAVE_POLL && !HAVE_SYS_DEVPOLL_H */
#endif	/* USE_SELECT */

      {FOR_EACH_ACTIVE_FD_BOX (me, box) check_box (box, INT_MAX);}
      {FOR_EACH_INACTIVE_FD_BOX (me, box) check_box (box, INT_MAX);}
    }

#endif	/* PIKE_DEBUG */

  static void low_backend_cleanup (struct Backend_struct *me)
  {
    me->exec_thread = 0;
  }

  /* A negative tv_sec in timeout turns it off. If it ran until the
   * timeout without calling any callbacks or call outs (except those
   * on backend_callbacks) then tv_sec will be set to -1. Otherwise it
   * will be set to the time spent. */
  void low_backend_once(struct Backend_struct *me,
			struct timeval *timeout)
    {
      int i, done_something = 0;
      struct timeval start_time;
#ifdef DECLARE_POLL_EXTRAS
      /* Declare any extra variables needed by MY_POLL(). */
      DECLARE_POLL_EXTRAS;
#endif /* DECLARE_POLL_EXTRAS */

      alloca(0);			/* Do garbage collect */
#ifdef PIKE_DEBUG
      if(d_flag > 1) do_debug();
#endif
      
#ifndef OWN_GETHRTIME
      GETTIMEOFDAY(&current_time);
#else
      /* good place to run the gethrtime-conversion update
	 since we have to run gettimeofday anyway /Mirar */
      own_gethrtime_update(&current_time);
#endif
      start_time.tv_sec = current_time.tv_sec;
      start_time.tv_usec = current_time.tv_usec;

      {
	ONERROR uwp;
#ifdef TIMEOUT_IS_MILLISECONDS
	int poll_timeout;
#elif defined(TIMEOUT_IS_TIMEVAL)
	struct timeval poll_timeout;
#elif defined(TIMEOUT_IS_TIMESPEC)
	struct timespec poll_timeout;
#else
#error Unknown timeout method.
#endif /* TIMEOUT_IS_* */
#ifdef PIKE_DEBUG
	struct timeval max_timeout;
#endif

	if(me->exec_thread) {
#ifdef PIKE_THREADS
	  if (me->exec_thread != Pike_interpreter.thread_state)
	    Pike_error ("Backend already in use by another thread.\n");
	  else
#endif
	    /* It's actually not a problem to make this function
	     * reentrant, but that'd introduce a risk of races in the
	     * callbacks (i.e. between when a read callback is called
	     * and when it reads the data), and besides I can't think
	     * of any sane way to use it. Also, this error can help
	     * discover otherwise tricky bugs. /mast */
	    Pike_error ("Backend already running - cannot reenter.\n");
	}
#ifdef PIKE_THREADS
	me->exec_thread = Pike_interpreter.thread_state;
#else
	me->exec_thread = 1;
#endif
	SET_ONERROR (uwp, low_backend_cleanup, me);

	if (timeout->tv_sec < 0) {
	  next_timeout.tv_sec = 100000000;
	  next_timeout.tv_usec = 999999;
	}
	else {
	  next_timeout.tv_sec = timeout->tv_sec;
	  next_timeout.tv_usec = timeout->tv_usec;
	  my_add_timeval(&next_timeout, &current_time);
	}

	/* Call outs */
	if(me->num_pending_calls)
	  if(my_timercmp(& CALL(0)->tv, < , &next_timeout))
	    next_timeout = CALL(0)->tv;

#ifdef PIKE_DEBUG
	max_timeout = next_timeout;
#endif
	call_callback(& me->backend_callbacks, NULL);
#ifdef PIKE_DEBUG
	if (my_timercmp (&max_timeout, <, &next_timeout))
	  Pike_fatal ("Timeout raised from %lu.%lu to %lu.%lu by a backend callback.\n",
		      (unsigned long)max_timeout.tv_sec,
		      (unsigned long)max_timeout.tv_usec,
		      (unsigned long)next_timeout.tv_sec,
		      (unsigned long)next_timeout.tv_usec);
#endif

	if(my_timercmp(&next_timeout, > , &current_time))
	{
	  my_subtract_timeval(&next_timeout, &current_time);
	}else{
	  next_timeout.tv_usec = 0;
	  next_timeout.tv_sec = 0;
	}

#ifdef TIMEOUT_IS_MILLISECONDS
	if (next_timeout.tv_sec >= 100000000)
	  /* Take this as waiting forever. */
	  poll_timeout = -1;
	else if(next_timeout.tv_sec < 0)
	  poll_timeout = 0;
	else if(next_timeout.tv_sec > (INT_MAX/1002))
	  poll_timeout = INT_MAX;
	else
	  poll_timeout = (next_timeout.tv_sec*1000) +
	    next_timeout.tv_usec/1000;
#elif defined(TIMEOUT_IS_TIMEVAL)
	poll_timeout = next_timeout;
#elif defined(TIMEOUT_IS_TIMESPEC)
	poll_timeout.tv_sec = next_timeout.tv_sec;
	poll_timeout.tv_nsec = next_timeout.tv_usec*1000;
#else
#error Unknown timeout method.
#endif /* TIMEOUT_IS_* */

	copy_selectors(& me->active_set, &me->set);

	IF_PD(fprintf (stderr, "[%d]BACKEND[%d]: Doing poll on fds:\n",
		       getpid(), me->id));
#if defined(HAVE_POLL) && defined(POLL_DEBUG) && !defined(USE_POLL_DEVICE)
	{
	  int i;
	  for (i = 0; i < me->active_set.num_in_poll; i++) {
	    fprintf (stderr,
		     "[%d]BACKEND[%d]:   fd %4d: %-4s %-5s %-8s %-9s: 0x%04x\n",
		     getpid(), me->id,
		     me->active_set.poll_fds[i].fd,
		     me->active_set.poll_fds[i].events & (POLLRDNORM|POLLIN) ? "read" : "",
		     me->active_set.poll_fds[i].events & POLLOUT ? "write" : "",
		     me->active_set.poll_fds[i].events & POLLRDBAND ? "read_oob" : "",
		     me->active_set.poll_fds[i].events & POLLWRBAND ? "write_oob" : "",
		     me->active_set.poll_fds[i].events);
	  }
	}
	fprintf(stderr, "[%d]BACKEND[%d]: poll(%p, %d, %d)...", getpid(), me->id,
		me->active_set.poll_fds,
		me->active_set.num_in_poll,
		poll_timeout);
#endif /* HAVE_POLL && POLL_DEBUG && !USE_POLL_DEVICE */

	THREADS_ALLOW();

	/* Note: The arguments to MY_POLL may be evaluated multiple times. */
	i = MY_POLL(me->active_set, poll_timeout);

	IF_PD(fprintf(stderr, " => %d\n", i));

	THREADS_DISALLOW();

	me->exec_thread = 0;
	UNSET_ONERROR (uwp);

	GETTIMEOFDAY(&current_time);
      }

      if (!i) {
	/* Timeout */
      } else if (i>0) {
#ifdef PIKE_DEBUG
	int num_active = i;
#endif /* PIKE_DEBUG */
	done_something = 1;

#ifdef USE_SELECT

	debug_malloc_touch(me->fds);
	for(i=0; i <= me->active_set.max_fd; i++)
	{
	  int err = 0;
	  struct fd_callback_box *box;

	  if(fd_FD_ISSET(i, &me->active_set.eset)) {
	    /* Check for errors. GNU libc says this isn't set on error, but
	     * POSIX does. FIXME: What bits will be set for errors on GNU
	     * systems, then? Should we always check for that? */
	    ACCEPT_SIZE_T len = sizeof (err);
	    IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: exception on %d\n",
			  getpid(), me->id, i));
	    if (!getsockopt (i, SOL_SOCKET, SO_ERROR, &err, &len) && err) {
	      IF_PD (fprintf (stderr, "[%d]BACKEND[%d]: error on %d, error=%d\n",
			      getpid(), me->id, i, err));
	    }

	    else {
	      box = SAFE_GET_ACTIVE_BOX (me, i);
	      check_box (box, i);
	      if (WANT_EVENT (box, READ_OOB)) {
		errno = 0;
		IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: read_oob_callback(%d, %p)\n",
			      getpid(), me->id, i, box->ref_obj));
		if (box->callback (box, PIKE_FD_READ_OOB) == -1)
		  goto backend_round_done;
	      }
	    }
	  }
	  
	  if(fd_FD_ISSET(i, &me->active_set.rset)) {
	    IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: read on %d\n",
			  getpid(), me->id, i));
	    box = SAFE_GET_ACTIVE_BOX (me, i);
	    check_box (box, i);
	    if (WANT_EVENT (box, READ)) {
	      IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: read_callback(%d, %p)\n",
			    getpid(), me->id, i, box->ref_obj));
	      errno = 0;
	      if (box->callback (box, PIKE_FD_READ) == -1)
		goto backend_round_done;
	    }
	  }
	  
	  if(fd_FD_ISSET(i, &me->active_set.wset)) {
	    IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: write on %d\n",
			  getpid(), me->id, i));
	    box = SAFE_GET_ACTIVE_BOX (me, i);
	    check_box (box, i);
	    if (WANT_EVENT (box, WRITE_OOB)) {
	      /* OOB can by BSD definition always be written, so if we can
	       * write normal data it's reasonable to assume OOB can be
	       * written too without too much risk of being thrown away. */
	      IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: write_oob_callback(%d, %p)\n",
			    getpid(), me->id, i, box->ref_obj));
	      errno = 0;
	      if (box->callback (box, PIKE_FD_WRITE_OOB) == -1)
		goto backend_round_done;
	    }
	    else if (WANT_EVENT (box, WRITE)) {
	      IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: write_callback(%d, %p)\n",
			    getpid(), me->id, i, box->ref_obj));
	      errno = 0;
	      if (box->callback (box, PIKE_FD_WRITE) == -1)
		goto backend_round_done;
	    }
	  }

	  if (err) {
	    int old_events;
	    errno = err;
	    box = SAFE_GET_ACTIVE_BOX (me, i);
	    if (box) {
	      check_box (box, i);
	      /* We don't want to keep this fd anymore. */
	      old_events = box->events;
	      set_fd_callback_events (box, box->events & PIKE_BIT_FD_ERROR);
	      if (WANT_EVENT (box, ERROR)) {
		IF_PD(fprintf(stderr,
			      "[%d]BACKEND[%d]: error event %d on fd %d sent to %p\n",
			      getpid(), me->id, err, i, box->ref_obj));
		if (box->callback (box, PIKE_FD_ERROR) == -1)
		  goto backend_round_done;
	      }
	      /* The following is temporary compat stuff. */
	      else if (old_events & PIKE_BIT_FD_READ) {
		IF_PD(fprintf(stderr,
			      "[%d]BACKEND[%d]: read_callback(%d, %p) for error %d\n",
			      getpid(), me->id, i, box->ref_obj, err));
		if (box->callback (box, PIKE_FD_READ) == -1)
		  goto backend_round_done;
	      } else if (old_events & PIKE_BIT_FD_WRITE) {
		IF_PD(fprintf(stderr,
			      "[%d]BACKEND[%d]: write_callback(%d, %p) for error %d\n",
			      getpid(), me->id, i, box->ref_obj, err));
		if (box->callback (box, PIKE_FD_WRITE) == -1)
		  goto backend_round_done;
	      }
	    }
	  }
	}

#else  /* !USE_SELECT */

#if defined(USE_POLL_DEVICE) || defined(USE_KQUEUE)
	while(i--)
#define ACTIVE_POLLSET poll_fds
#else
	for(i=0; i<me->active_set.num_in_poll; i++)
#define ACTIVE_POLLSET me->active_set.poll_fds
#endif
	{
	  int fd = GET_FD(ACTIVE_POLLSET[i]);
	  struct fd_callback_box *box;
#ifdef PIKE_DEBUG
	  int handled = 0;
#endif /* PIKE_DEBUG */


#ifdef USE_KQUEUE
	  IF_PD(fprintf(stderr, "fd:%d filter:%d flags:0x%08x\n",
			GET_FD(ACTIVE_POLLSET[i]),
			GET_EVENTS(ACTIVE_POLLSET[i]),
			ACTIVE_POLLSET[i].flags));
#else /* !USE_KQUEUE */
	  IF_PD(fprintf(stderr, "fd:%d events:0x%04x\n",
			GET_FD(ACTIVE_POLLSET[i]),
			GET_EVENTS(ACTIVE_POLLSET[i])));
#endif /* USE_KQUEUE */

	  /* FIMXE: kqueue wants to look at
	   *	    ACTIVE_POLLSET[i].flags & EV_ERROR
	   */
#ifdef MY_POLLNVAL
	  if(CHECK_EVENT(ACTIVE_POLLSET[i], MY_POLLNVAL))
	  {
	    /* NOTE: /dev/poll returns POLLNVAL for closed descriptors. */
	    IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: POLLNVAL on %d\n",
			  getpid(), me->id, fd));
#ifdef PIKE_DEBUG
#ifdef HAVE_POLL
	    /* FIXME */
	    if (SAFE_GET_ACTIVE_BOX (me, fd)) {
	      /* It's still there... */
	      struct pollfd fds;
	      int ret;
	      fds.fd=fd;
	      fds.events=POLLIN;
	      fds.revents=0;
	      ret=poll(&fds, 1,1 );
	      if(fds.revents & POLLNVAL)
		Pike_fatal("Bad filedescriptor %d to poll().\n", fd);
	      break;
	    }
#endif
	    handled = 1;
#endif /* PIKE_DEBUG */
	  }
#endif /* MY_POLLNVAL */

	  if (CHECK_EVENT(ACTIVE_POLLSET[i], MY_POLLRDBAND)) {
	    IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: POLLRDBAND on %d\n",
			  getpid(), me->id, fd));
	    box = SAFE_GET_ACTIVE_BOX (me, fd);
	    check_box (box, fd);
	    if (WANT_EVENT (box, READ_OOB))
	    {
	      errno = 0;
	      IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: read_oob_callback(%d, %p)\n",
			    getpid(), me->id, fd, box->ref_obj));
	      if (box->callback (box, PIKE_FD_READ_OOB) == -1)
		goto backend_round_done;
	    }
#ifdef PIKE_DEBUG
	    handled = 1;
#endif /* PIKE_DEBUG */
	  }

	  if(CHECK_EVENT(ACTIVE_POLLSET[i], MY_POLLIN))
	  {
	    IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: POLLRDNORM|POLLIN on %d\n",
			  getpid(), me->id, fd));
	    box = SAFE_GET_ACTIVE_BOX (me, fd);
	    check_box (box, fd);
	    if (WANT_EVENT (box, READ)) {
	      IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: read_callback(%d, %p)\n",
			    getpid(), me->id, fd, box->ref_obj));
	      errno = 0;
	      if (box->callback (box, PIKE_FD_READ) == -1)
		goto backend_round_done;
	    }
#ifdef PIKE_DEBUG
	    handled = 1;
#endif /* PIKE_DEBUG */
	  }

	  if (CHECK_EVENT(ACTIVE_POLLSET[i], MY_POLLWRBAND)) {
	    IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: POLLWRBAND on %d\n",
			  getpid(), me->id, fd));
	    box = SAFE_GET_ACTIVE_BOX (me, fd);
	    check_box (box, fd);
	    if (WANT_EVENT (box, WRITE_OOB)) {
	      IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: write_oob_callback(%d, %p)\n",
			    getpid(), me->id, fd, box->ref_obj));
	      errno = 0;
	      if (box->callback (box, PIKE_FD_WRITE_OOB) == -1)
		goto backend_round_done;
	    }
#ifdef PIKE_DEBUG
	    handled = 1;
#endif /* PIKE_DEBUG */
	  }

	  if(CHECK_EVENT(ACTIVE_POLLSET[i], MY_POLLOUT)) {
	    IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: POLLOUT on %d\n",
			  getpid(), me->id, fd));
	    box = SAFE_GET_ACTIVE_BOX (me, fd);
	    check_box (box, fd);
	    if (WANT_EVENT (box, WRITE)) {
	      IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: write_callback(%d, %p)\n",
			    getpid(), me->id, fd, box->ref_obj));
	      errno = 0;
	      if (box->callback (box, PIKE_FD_WRITE) == -1)
		goto backend_round_done;
	    }
#ifdef PIKE_DEBUG
	    handled = 1;
#endif /* PIKE_DEBUG */
	  }

	  /* FIMXE: kqueue wants to look at
	   *	    ACTIVE_POLLSET[i].flags & (EV_ERROR|EV_EOF)
	   */
#ifndef USE_KQUEUE
	  if (CHECK_EVENT(ACTIVE_POLLSET[i], POLLHUP|POLLERR))
	  {
	    /* Closed or error */
	    int old_events;
	    errno = 0;
	    if (CHECK_EVENT(ACTIVE_POLLSET[i], POLLERR)) {
	      int err;
	      ACCEPT_SIZE_T len = sizeof (err);
	      if (!getsockopt (fd, SOL_SOCKET, SO_ERROR, &err, &len)) {
		IF_PD (fprintf (stderr,
				"[%d]BACKEND[%d]: POLLERR on %d, error=%d\n",
				getpid(), me->id, fd, err));
		errno = err;
	      }
#ifdef PIKE_DEBUG
	      else
		fprintf(stderr, "Got POLLERR on non-socket fd %d (getsockopt errno=%d)\n",
			fd, errno);
#endif /* PIKE_DEBUG */
	    }
	    else {
	      IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: POLLHUP on %d\n",
			    getpid(), me->id, fd));
	    }
	    box = SAFE_GET_ACTIVE_BOX (me, fd);
	    if (box) {
	      check_box (box, fd);
	      /* We don't want to keep this fd anymore. */
	      old_events = box->events;
	      set_fd_callback_events (box, box->events & PIKE_BIT_FD_ERROR);
	      if (WANT_EVENT (box, ERROR)) {
		IF_PD(fprintf(stderr,
			      "[%d]BACKEND[%d]: error event on fd %d sent to %p\n",
			      getpid(), me->id, i, box->ref_obj));
		if (box->callback (box, PIKE_FD_ERROR) == -1)
		  goto backend_round_done;
	      }
	      /* The following is temporary compat stuff. */
	      else if (old_events & PIKE_BIT_FD_READ) {
		IF_PD(fprintf(stderr,
			      "[%d]BACKEND[%d]: read_callback(%d, %p) for error %d\n",
			      getpid(), me->id, fd, box->ref_obj, errno));
		if (box->callback (box, PIKE_FD_READ) == -1)
		  goto backend_round_done;
	      } else if (old_events & PIKE_BIT_FD_WRITE) {
		IF_PD(fprintf(stderr,
			      "[%d]BACKEND[%d]: write_callback(%d, %p) for error %d\n",
			      getpid(), me->id, fd, box->ref_obj, errno));
		if (box->callback (box, PIKE_FD_WRITE) == -1)
		  goto backend_round_done;
	      }
	    }
#ifdef PIKE_DEBUG
	    handled = 1;
#endif /* PIKE_DEBUG */
	  }
#endif /* !USE_KQUEUE */

#ifdef PIKE_DEBUG
	  num_active -= handled;
	  if (!handled && GET_EVENTS(ACTIVE_POLLSET[i])) {
	    fprintf(stderr, "[%d]BACKEND[%d]: fd %ld has revents 0x%08lx, "
		    "but hasn't been handled.\n", getpid(), me->id,
		    (long)GET_FD(ACTIVE_POLLSET[i]),
		    (long)GET_EVENTS(ACTIVE_POLLSET[i]));
	  }
#endif /* PIKE_DEBUG */
	}

#if defined(PIKE_DEBUG) && !defined(USE_POLL_DEVICE) && !defined(USE_KQUEUE)
	if (num_active) {
	  fprintf(stderr, "[%d]BACKEND[%d]: %d more active fds than were handled.\n",
		  getpid(), me->id, num_active);
	  for(i=0; i<me->active_set.num_in_poll; i++) {
	    fprintf(stderr,
		    "[%d]BACKEND[%d]: fd %ld, events 0x%08lx, revents 0x%08lx\n",
		    getpid(), me->id,
		    (long)me->active_set.poll_fds[i].fd,
		    (long)me->active_set.poll_fds[i].events,
		    (long)me->active_set.poll_fds[i].revents);
	  }
	}
#endif /* PIKE_DEBUG && !USE_POLL_DEVICE && !USE_KQUEUE */

#endif	/* USE_SELECT */

	/* Must be up-to-date for backend_do_call_outs. */
	GETTIMEOFDAY(&current_time);
      }else{
	switch(errno)
	{
#ifdef __NT__
	  default:
	    Pike_fatal("Error in backend %d\n",errno);
	    break;
#endif
	    
	  case EINVAL:
	    Pike_fatal("Invalid timeout to select().\n");
	    break;
	    
#ifdef WSAEINTR
	  case WSAEINTR:
#endif
	  case EINTR:		/* ignore */
	    break;
	    
#ifdef WSAEBADF
	  case WSAEBADF:
#endif
#ifdef WSAENOTSOCK
	  case WSAENOTSOCK:
#endif
	  case EBADF:
	    /* TODO: Fix poll version! */
#ifdef USE_SELECT

	    copy_selectors(& me->active_set, &me->set);

	    timeout->tv_usec=0;
	    timeout->tv_sec=0;
	    if(MY_POLL(me->active_set, *timeout) < 0)
	    {
	      switch(errno)
	      {
#ifdef WSAEBADF
		case WSAEBADF:
#endif
#ifdef WSAENOTSOCK
		case WSAENOTSOCK:
#endif
		case EBADF:
		{
		  FOR_EACH_ACTIVE_FD_BOX (me, box) {
		    fd_FD_ZERO(& me->active_set.rset);
		    fd_FD_ZERO(& me->active_set.wset);
		    fd_FD_ZERO(& me->active_set.eset);
		    
		    if(my_FD_ISSET(box->fd, &me->set.read))
		      fd_FD_SET(box->fd, & me->active_set.rset);
		    if(my_FD_ISSET(box->fd, &me->set.write))
		      fd_FD_SET(box->fd, & me->active_set.wset);
		    if(my_FD_ISSET(box->fd, &me->set.except))
		      fd_FD_SET(box->fd, & me->active_set.eset);
		    
		    timeout->tv_usec=0;
		    timeout->tv_sec=0;
		    
		    if(MY_POLL(me->active_set, *timeout) < 0)
		    {
		      switch(errno)
		      {
#ifdef __NT__
			default:
#endif
			case EBADF:
#ifdef WSAEBADF
			case WSAEBADF:
#endif
#ifdef WSAENOTSOCK
			case WSAENOTSOCK:
#endif

#ifdef DEBUG_MALLOC
			  debug_malloc_dump_fd(box->fd);
#endif
			  Pike_fatal("Filedescriptor %d (%s) caused fatal error %d in backend.\n",box->fd,fd_info(box->fd),errno);
			  
			case EINTR:
			  break;
		      }
		    }
		  }
		}
	      }
#ifdef _REENTRANT
	      /* FIXME: Extra stderr messages should not be allowed.../Hubbe */
	      write_to_stderr("Bad filedescriptor to select().\n"
			      "fd closed in another thread?\n", 62);
#else /* !_REENTRANT */
	      Pike_fatal("Bad filedescriptor to select().\n");
#endif /* _REENTRANT */
	    }
#endif	/* USE_SELECT */
	    break;
	    
	}
      }

      {
	int call_outs_called =
	  backend_do_call_outs(me); /* Will update current_time after calls. */
	if (call_outs_called)
	  done_something = 1;
	if (call_outs_called < 0)
	  goto backend_round_done;
      }

      call_callback(& me->backend_callbacks, (void *)(ptrdiff_t)1);

    backend_round_done:
      if (!done_something)
	timeout->tv_sec = -1;
      else {
	timeout->tv_sec = current_time.tv_sec;
	timeout->tv_usec = current_time.tv_usec;
	my_subtract_timeval (timeout, &start_time);
      }
    }

  /*! @decl float|int(0..0) `()(void|float|int(0..0) sleep_time)
   *!   Perform one pass through the backend.
   *!
   *! @param sleep_time
   *!   Wait at most @[sleep_time] seconds. The default when
   *!   unspecified or the integer @expr{0@} is no time limit.
   *!
   *! @returns
   *!   If the backend did call any callbacks or call outs then the
   *!   time spent in the backend is returned as a float. Otherwise
   *!   the integer @expr{0@} is returned.
   */
  PIKEFUN float|int(0..0) `()(void|float|int(0..0) sleep_time)
  {
    struct timeval timeout;

    if (sleep_time && sleep_time->type == PIKE_T_FLOAT) {
      timeout.tv_sec = (long) floor (sleep_time->u.float_number);
      timeout.tv_usec =
	(long) ((sleep_time->u.float_number - timeout.tv_sec) * 1e6);
    }
    else
      timeout.tv_sec = -1;
    pop_n_elems (args);

    low_backend_once(THIS, &timeout);

    if (timeout.tv_sec < 0)
      push_int (0);
    else
      push_float ((float) timeout.tv_sec + (float) timeout.tv_usec / 1e6);
  }

#ifndef tObjImpl_THREAD
  /* Kludge for precompile.pike; it resolves object(Thread.Thread)
   * to tObjImpl_THREAD, while "program_id.h" only knows about
   * tObjImpl_THREAD_ID.
   */
#define tObjImpl_THREAD	tObjImpl_THREAD_ID
#endif /* !tObjImpl_THREAD */

  /*! @decl Thread.Thread executing_thread()
   *! @decl int executing_thread()
   *!
   *! Return the thread currently executing in the backend. I.e. the
   *! thread that has called @[`()] and haven't exited from that call.
   *! Zero is returned if there's no thread in the backend.
   *!
   *! If Pike is compiled without thread support then @expr{1@} is
   *! returned if we're inside the backend, @expr{0@} otherwise.
   */
  PIKEFUN object(Thread.Thread)|int(0..1) executing_thread()
    /* FIXME: The type is too weak, but precompile.pike doesn't
     * understand different function variants in cpp branches. */
  {
    pop_n_elems (args);
#ifdef PIKE_THREADS
    if (THIS->exec_thread)
      ref_push_object (THIS->exec_thread->thread_obj);
    else
      push_int (0);
#else
    push_int (THIS->exec_thread);
#endif
  }

  /*! @decl void add_file(Stdio.File|Stdio.FILE f)
   *! 
   *! Add @[f] to this backend. This simply does
   *! @expr{f->set_backend(backend)@} where @expr{backend@} is this
   *! object.
   */
  PIKEFUN void add_file(object f)
  {
    ref_push_object (Pike_fp->current_object);
    apply (f, "set_backend", 1);
    pop_stack();
  }


   /*! @decl int id()
    *!
    *! Return an integer that uniquely identifies this backend. For
    *! the default backend that integer is @expr{0@}.
    */
   PIKEFUN int id()
   {
     RETURN (THIS->id);
   }

   PIKEFUN string _sprintf(int type, mapping flags)
   {
     if (type == 'O') {
       push_constant_text ("Pike.Backend(%d)");
       push_int (THIS->id);
       f_sprintf (2);
       stack_pop_n_elems_keep_top (args);
     }
     else {
       pop_n_elems (args);
       push_int (0);
     }
   }

  extern int pike_make_pipe(int *);

  GC_CHECK
  {
    struct Backend_struct *me =
      (struct Backend_struct *) Pike_fp->current_storage;
    int e;

    for (e = 0; e < me->num_pending_calls; e++) {
      if (CALL(e)->caller)
	debug_gc_check (CALL(e)->caller,
			" as caller for call out in backend object");
      if (CALL(e)->args)
	debug_gc_check (CALL(e)->args,
			" as args for call out in backend object");
    }

    {FOR_EACH_ACTIVE_FD_BOX (me, box) {
	check_box (box, INT_MAX);
	if (box->ref_obj && box->events)
	  debug_gc_check (box->ref_obj, " as container object "
			  "for an active callback in backend object");
      }}
    {FOR_EACH_INACTIVE_FD_BOX (me, box) {
	check_box (box, INT_MAX);
	if (box->ref_obj && box->events)
	  debug_gc_check (box->ref_obj, " as container object "
			  "for an inactive callback in backend object");
      }}
  }

  GC_RECURSE
  {
    struct Backend_struct *me =
      (struct Backend_struct *) Pike_fp->current_storage;
    int e;

    for (e = 0; e < me->num_pending_calls; e++) {
      if (CALL(e)->caller)
	gc_recurse_object (CALL(e)->caller);
      if (CALL(e)->args)
	gc_recurse_array (CALL(e)->args);
    }

    {FOR_EACH_ACTIVE_FD_BOX (me, box) {
	if (box->ref_obj && box->events)
	  gc_recurse_short_svalue ((union anything *) &box->ref_obj, T_OBJECT);
      }}
    {FOR_EACH_INACTIVE_FD_BOX (me, box) {
	if (box->ref_obj && box->events)
	  gc_recurse_short_svalue ((union anything *) &box->ref_obj, T_OBJECT);
      }}
  }

  INIT
    {
      struct Backend_struct *me = THIS;

      me->id = unused_id++;

      IF_PD (fprintf (stderr, "[%d]BACKEND[%d]: init\n", getpid(), me->id));

      me->exec_thread = 0;

      me->backend_callbacks.callbacks=0;
      me->backend_callbacks.num_calls=0;

      GETTIMEOFDAY(&current_time); /* Why? /mast */

      me->num_pending_calls=0;
      me->call_buffer=0;
      me->call_buffer_size=0;
      me->hash_size=0;
      me->hash_order=5;
      me->call_hash=0;

      me->backend_obj = Pike_fp->current_object; /* Note: Not refcounted. */

#ifdef PIKE_DEBUG
      me->inside_call_out=0;
#endif

      me->fd_boxes=0;
      me->fd_boxes_start = me->fd_boxes_size = 0;
      me->inactive_boxes = me->free_inactive_box = NULL;
      me->inactive_boxes_size = 0;

#ifdef OPEN_POLL_DEVICE
      if ((me->set = OPEN_POLL_DEVICE()) < 0) {
	Pike_error("Failed to open poll device (errno:%d)\n", errno);
      }
      set_close_on_exec(me->set, 1);
#elif defined(HAVE_POLL)
      me->set.poll_fds=0;
      me->set.poll_fd_size=0;
      me->set.num_in_poll=0;

      me->active_set.poll_fds=0;
      me->active_set.poll_fd_size=0;
      me->active_set.num_in_poll=0;
#else /* !HAVE_POLL */
      me->set.max_fd=0;
      my_FD_ZERO(&me->set.read);
      my_FD_ZERO(&me->set.write);
      my_FD_ZERO(&me->set.except);
      /* FIXME: Should there be something else here? */
      /* me->set.num_fds=0; */
#endif /* OPEN_POLL_DEVICE || HAVE_POLL */

      {
	int pipe[2];
	if(pike_make_pipe(pipe) < 0)
	  Pike_error("Couldn't create backend wakeup pipe! errno=%d.\n",errno);

	set_nonblocking(pipe[0],1);
	set_nonblocking(pipe[1],1);
	INIT_FD_CALLBACK_BOX (&me->wakeup_cb_box, me, NULL,
			      pipe[0], PIKE_BIT_FD_READ, wakeup_callback);
	me->wakeup_pipe_send_fd = pipe[1];

	/* Don't keep these on exec! */
	set_close_on_exec(pipe[0], 1);
	set_close_on_exec(pipe[1], 1);
      }

      register_backend(me);

#ifdef DO_PIKE_CLEANUP
      num_active_backends++;
#endif
    }
  
  EXIT
    {
      struct Backend_struct *me=THIS;
      int e;

      IF_PD (fprintf (stderr, "[%d]BACKEND[%d]: exit\n", getpid(), me->id));

      free_callback_list(& THIS->backend_callbacks);

      fd_close(THIS->wakeup_cb_box.fd);
      fd_close(THIS->wakeup_pipe_send_fd);

      if (me->fd_boxes) {
	FOR_EACH_ACTIVE_FD_BOX (me, box) {
	    check_box (box, INT_MAX);

#ifdef PIKE_DEBUG
	    if (get_backend_for_fd (box->fd) != me)
	      Pike_fatal ("Inconsistency in global fd map for fd %d: "
			  "backend is %p, expected %p.\n",
			  box->fd, get_backend_for_fd (box->fd), me);
#endif

	    if (box->callback == compat_box_dispatcher) {
#ifdef PIKE_DEBUG
	      fprintf (stderr, "[%d]BACKEND[%d]: "
		       "Compat callbacks left at exit for fd %d: 0x%x\n",
		       getpid(), me->id, box->fd, box->events);
#endif
	      really_free_compat_cb_box ((struct compat_cb_box *) box);
	    }

	    if (box->backend) {
	      box->backend = NULL;
	      if (box->ref_obj && box->events)
		free_object (box->ref_obj);
	    }
	  }

	free(me->fd_boxes);
	me->fd_boxes = NULL;
	me->fd_boxes_start = me->fd_boxes_size = 0;
      }

      if (me->inactive_boxes) {
	FOR_EACH_INACTIVE_FD_BOX (me, box) {
	    check_box (box, INT_MAX);
#ifdef PIKE_DEBUG
	    if (box->callback == compat_box_dispatcher)
	      Pike_fatal ("Got inactive callback in compat interface.\n");
#endif

	    if (box->backend) {
	      box->backend = NULL;
	      if (box->ref_obj && box->events)
		free_object (box->ref_obj);
	    }
	  }

	free(me->inactive_boxes);
	me->inactive_boxes = me->free_inactive_box = NULL;
	me->inactive_boxes_size = 0;
      }

      /* Make sure we aren't referenced any more. */
      /* FIXME: Ought to keep better track of our fds so that we don't
       * need to do this loop. /mast */
      for (e = 0; e < fd_map_size; e++) {
	if (fd_map[e] == me) fd_map[e] = NULL;
      }

#ifdef HAVE_POLL
#ifdef USE_POLL_DEVICE
      unregister_backend(me);
      fd_close(THIS->set);
#else
      if (me->set.poll_fds) {
	free(me->set.poll_fds);
	me->set.poll_fds = NULL;
	me->set.poll_fd_size = 0;
	me->set.num_in_poll = 0;
      }
      if (me->active_set.poll_fds) {
	free(me->active_set.poll_fds);
	me->active_set.poll_fds = NULL;
	me->active_set.poll_fd_size = 0;
	me->active_set.num_in_poll = 0;
      }
#endif
#endif /* HAVE_POLL */

      /* CALL OUT */
      backend_verify_call_outs(me);
      for(e=0;e<me->num_pending_calls;e++)
      {
	free_array(CALL(e)->args);
	if(CALL(e)->caller) free_object(CALL(e)->caller);
	really_free_call_out(CALL(e));
      }
      me->num_pending_calls=0;
      if(me->call_buffer) free((char*)me->call_buffer);
      me->call_buffer=NULL;
      if(me->call_hash) free((char*)me->call_hash);
      me->call_hash=NULL;

#ifdef DO_PIKE_CLEANUP
      if (!--num_active_backends) backend_cleanup();
#endif
    }
}

/*! @endclass
 */

/*! @module DefaultBackend
 *!   This is the @[Backend] object that files and call_outs are
 *!   handled by by default.
 *!
 *!   This is also the @[Backend] object that will be used if @[main()]
 *!   returns @expr{-1@}.
 *!
 *! @seealso
 *!   @[Backend], @[Stdio.File()->set_nonblocking()], @[call_out()]
 */

/*! @endmodule
 */

/*! @endmodule
 */

/*! @decl mixed call_out(function f, float|int delay, mixed ... args)
 *! @decl void _do_call_outs()
 *! @decl int find_call_out(function f)
 *! @decl int find_call_out(mixed id)
 *! @decl int remove_call_out(function f)
 *! @decl int remove_call_out(function id)
 *! @decl array(array) call_out_info()
 *!   These are aliases for the corresponding functions in
 *!   @[Pike.DefaultBackend].
 *!
 *! @seealso
 *!   @[Pike.Backend()->call_out()], @[Pike.Backend()->_do_call_outs()],
 *!   @[Pike.Backend()->find_call_out()], @[Pike.Backend()->remove_call_out()],
 *!   @[Pike.Backend()->call_out_info()]
 */

/* This doesn't need to be here */
PMOD_EXPORT int write_to_stderr(char *a, size_t len)
{
#ifdef __NT__
  size_t e;
  for(e=0;e<len;e++)
    putc(a[e],stderr);
#else
  int nonblock=0;
  size_t pos;
  int tmp;
  
  if(!len) return 1;
  
  for(pos=0;pos<len;pos+=tmp)
  {
    tmp=write(2,a+pos,len-pos);
    if(tmp<0)
    {
      tmp=0;
      switch(errno)
      {
#ifdef EWOULDBLOCK
	case EWOULDBLOCK:
	  nonblock=1;
	  set_nonblocking(2,0);
	  continue;
#endif
	  
	case EINTR:
	  check_threads_etc();
	  continue;
      }
      break;
    }
  }
  
  if(nonblock)
    set_nonblocking(2,1);
  
#endif
  return 1;
}

PMOD_EXPORT struct object *get_backend_obj_for_fd (int fd)
{
  struct Backend_struct *b = really_get_backend_for_fd (fd);
  if (!b) return NULL;
  return b->backend_obj;
}

PMOD_EXPORT void set_backend_for_fd (int fd, struct Backend_struct *new)
{
  struct Backend_struct *old = get_backend_for_fd (fd);

  IF_PD (fprintf (stderr, "Changing backend from %d to %d for fd %d\n",
		  old ? old->id : -1, new ? new->id : -1, fd));

  if (!old)
    low_set_backend_for_fd (fd, new);
  else if (old != new) {
    struct fd_callback_box *box = SAFE_GET_ACTIVE_BOX (old, fd);
    if (box) {
      if (new)
	change_backend_for_box (box, new);
      else {
	int is_compat_box = box->callback == compat_box_dispatcher;
	unhook_fd_callback_box (box);
	if (is_compat_box)
	  really_free_compat_cb_box ((struct compat_cb_box *) box);
      }
    }
    low_set_backend_for_fd (fd, new);
  }
}

/* Compat stuff for old backend interface. */

struct compat_cb_box
{
  struct fd_callback_box box;	/* Must be first. */
  file_callback read, write, read_oob, write_oob;
  void *read_data, *write_data, *read_oob_data, *write_oob_data;
};

#include "block_alloc.h"

#undef BLOCK_ALLOC_NEXT
#define BLOCK_ALLOC_NEXT read_data

#undef DMALLOC_DESCRIBE_BLOCK
#define DMALLOC_DESCRIBE_BLOCK(X) do {					\
    fprintf (stderr, "  backend: %p, fd: %d, events: 0x%x\n",		\
	     X->box.backend, X->box.fd, X->box.events);			\
  } while (0)

BLOCK_ALLOC_FILL_PAGES (compat_cb_box, 1)

static int compat_box_dispatcher (struct fd_callback_box *box, int event)
{
  struct compat_cb_box *cbox = (struct compat_cb_box *) box;
  switch (event) {
    case PIKE_FD_READ:
      IF_PD (fprintf (stderr, "[%d]BACKEND[%d]: compat_box_dispatcher for "
		      "PIKE_FD_READ to %p %p\n", getpid(),
		      cbox->box.backend->id, cbox->read, cbox->read_data));
      return cbox->read (cbox->box.fd, cbox->read_data);
    case PIKE_FD_WRITE:
      IF_PD (fprintf (stderr, "[%d]BACKEND[%d]: compat_box_dispatcher for "
		      "PIKE_FD_WRITE to %p %p\n", getpid(),
		      cbox->box.backend->id, cbox->write, cbox->write_data));
      return cbox->write (cbox->box.fd, cbox->write_data);
    case PIKE_FD_READ_OOB:
      IF_PD (fprintf (stderr, "[%d]BACKEND[%d]: compat_box_dispatcher for "
		      "PIKE_FD_READ_OOB to %p %p\n", getpid(),
		      cbox->box.backend->id, cbox->read_oob, cbox->read_oob_data));
      return cbox->read_oob (cbox->box.fd, cbox->read_oob_data);
    case PIKE_FD_WRITE_OOB:
      IF_PD (fprintf (stderr, "[%d]BACKEND[%d]: compat_box_dispatcher for "
		      "PIKE_FD_WRITE_OOB to %p %p\n", getpid(),
		      cbox->box.backend->id, cbox->write_oob, cbox->write_oob_data));
      return cbox->write_oob (cbox->box.fd, cbox->write_oob_data);
    default:
#ifdef PIKE_DEBUG
      Pike_fatal ("Unexpected event type %d.\n", event);
#endif
      return 0;			/* To keep gcc happy. */
  }
}

#define WRAP(CB, EVENT_BIT)						\
  void PIKE_CONCAT3(set_, CB, _callback) (int fd, file_callback cb, void *data)	\
  {									\
    struct Backend_struct *b = really_get_backend_for_fd (fd);		\
    struct fd_callback_box *box = SAFE_GET_ACTIVE_BOX (b, fd);		\
    struct compat_cb_box *cbox;						\
									\
    IF_PD (fprintf (stderr, "[%d]BACKEND[%d]: set_" #CB "_callback (%d, %p, %p)\n", \
		    getpid(), b->id, fd, cb, data));			\
									\
    if (box) {								\
      check_box (box, fd);						\
      DO_IF_DEBUG (							\
	if (box->callback != compat_box_dispatcher)			\
	  Pike_fatal ("Mixing old and new style "			\
		      "backend interfaces for fd %d.\n", fd);		\
      );								\
      cbox = (struct compat_cb_box *) box;				\
    }									\
    else {								\
      if (!cb) return;							\
      cbox = alloc_compat_cb_box();					\
      INIT_FD_CALLBACK_BOX (&cbox->box, b, NULL,			\
			    fd, 0, compat_box_dispatcher);		\
    }									\
									\
    cbox->CB = cb;							\
    cbox->PIKE_CONCAT (CB, _data) = data;				\
									\
    if (cb)								\
      set_fd_callback_events (&cbox->box, cbox->box.events | EVENT_BIT); \
    else {								\
      set_fd_callback_events (&cbox->box, cbox->box.events & ~EVENT_BIT); \
      if (!cbox->box.events) {						\
	unhook_fd_callback_box (&cbox->box);				\
	really_free_compat_cb_box (cbox);				\
      }									\
    }									\
  }									\
  									\
  file_callback PIKE_CONCAT3(query_, CB, _callback) (int fd)		\
  {									\
    struct Backend_struct *b=get_backend_for_fd (fd);			\
    struct fd_callback_box *box;					\
    struct compat_cb_box *cbox;						\
									\
    if (!b) return NULL;						\
    if (!(box = SAFE_GET_ACTIVE_BOX (b, fd))) return NULL;		\
    check_box (box, fd);						\
    DO_IF_DEBUG (							\
      if (box->callback != compat_box_dispatcher)			\
	Pike_fatal ("Mixing old and new style "				\
		    "backend interfaces for fd %d.\n", fd);		\
    );									\
									\
    cbox = (struct compat_cb_box *) box;				\
    if (!(cbox->box.events & EVENT_BIT)) return NULL;			\
    return cbox->CB;							\
  }									\
  									\
  void *PIKE_CONCAT3(query_, CB, _callback_data) (int fd)		\
  {									\
    struct Backend_struct *b=get_backend_for_fd (fd);			\
    struct fd_callback_box *box;					\
    struct compat_cb_box *cbox;						\
									\
    if (!b) return NULL;						\
    if (!(box = SAFE_GET_ACTIVE_BOX (b, fd))) return NULL;		\
    check_box (box, fd);						\
    DO_IF_DEBUG (							\
      if (box->callback != compat_box_dispatcher)			\
	Pike_fatal ("Mixing old and new style "				\
		    "backend interfaces for fd %d.\n", fd);		\
    );									\
									\
    cbox = (struct compat_cb_box *) box;				\
    if (!(cbox->box.events & EVENT_BIT)) return NULL;			\
    return cbox->PIKE_CONCAT (CB, _data);				\
  }

WRAP(read, PIKE_BIT_FD_READ);
WRAP(write, PIKE_BIT_FD_WRITE);
WRAP(read_oob, PIKE_BIT_FD_READ_OOB);
WRAP(write_oob, PIKE_BIT_FD_WRITE_OOB);

PMOD_EXPORT struct callback *debug_add_backend_callback(callback_func call,
							void *arg,
							callback_func free_func)
{
  return backend_debug_add_backend_callback(default_backend,
					    call,
					    arg,
					    free_func);
}

void wake_up_backend(void)
{
  if(default_backend)
    backend_wake_up_backend(default_backend);
}

void do_call_outs(void)
{
  if(default_backend) {
    GETTIMEOFDAY (&current_time);
    backend_do_call_outs(default_backend);
  }
}

#ifdef PIKE_DEBUG
long do_debug_cycle=1;
long current_do_debug_cycle=0;
void do_debug(void)
{
  extern void check_all_arrays(void);
  extern void check_all_mappings(void);
  extern void check_all_programs(void);
  extern void check_all_objects(void);
  extern void verify_shared_strings_tables(void);
  extern void slow_check_stack(void);

  if(current_do_debug_cycle) return;
  current_do_debug_cycle=++do_debug_cycle;

  if (d_flag > 2) {
    verify_shared_strings_tables();
    slow_check_stack();
    check_all_arrays();
    check_all_mappings();
    check_all_programs();
    check_all_objects();
  }

  call_callback(& do_debug_callbacks, 0);

  if(default_backend)
    backend_do_debug(default_backend);

  if(d_flag>3) do_gc(NULL, 1);

  current_do_debug_cycle=0;
}

PMOD_EXPORT void debug_check_fd_not_in_use (int fd)
{
  if (fd < 0) Pike_fatal ("Invalid fd: %d\n", fd);
  if (fd < fd_map_size && fd_map[fd])
    Pike_fatal ("fd %d already in use by backend %d.\n", fd, fd_map[fd]->id);
}

#endif /* PIKE_DEBUG */


void init_backend(void)
{
  init_call_out_s_blocks();
  init_compat_cb_box_blocks();
  INIT;
  {
    struct object *o=clone_object(Backend_program,0);
    default_backend=OBJ2_BACKEND(o);
    add_object_constant("__backend",o,0);
  }
#ifndef USE_SELECT
  IF_PD(fprintf(stderr,
		"MY_POLLIN:       0x%04x\n"
		"MY_POLLOUT:      0x%04x\n"
		"MY_POLLEXCEPT:   0x%04x\n"
		"MY_POLLRDBAND:   0x%04x\n"
		"MY_POLLWREXCEPT: 0x%04x\n"
		"MY_POLLWRBAND:   0x%04x\n",
		MY_POLLIN, MY_POLLOUT,
		MY_POLLEXCEPT, MY_POLLRDBAND,
		MY_POLLWREXCEPT, MY_POLLWRBAND));
#endif /* !USE_SELECT */
#ifdef CHILD_NEEDS_TO_REOPEN
  /* /dev/poll and kqueue fds get invalidated at fork. */
  dmalloc_accept_leak(add_to_callback(&fork_child_callback,
				      reopen_all_backends, NULL, NULL));
#endif
}

#ifdef DO_PIKE_CLEANUP
void exit_backend()
{
  free_object (default_backend->backend_obj);
  default_backend = 0;
  EXIT;
}

/* Note: This is called when the last backend object, which might be
 * after exit_backend if there's garbage. */
static void backend_cleanup()
{
#ifdef CHILD_NEEDS_TO_REOPEN
  if (backends) {
    free(backends);
    num_backends = 0;
  }
#endif /* CHILD_NEEDS_TO_REOPEN */
  free_all_call_out_s_blocks();
  free_all_compat_cb_box_blocks();
  if(fd_map)
  {
    free(fd_map);
    fd_map=0;
    fd_map_size=0;
  }
#ifdef HAVE_BROKEN_F_SETFD
  cleanup_close_on_exec();
#endif /* HAVE_BROKEN_F_SETFD */
}
#endif
