/* -*- 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: 8cfc6d8685d918209d5450d90fb88f85b0e5fe1f $
 *
 * Backend object.
 */

#include "global.h"
#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 "array.h"
#include <math.h>
#include "interpret.h"
#include "stuff.h"
#include "bignum.h"
#include "builtin_functions.h"
#include "mapping.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
 */

/* For select */
#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>

/* For poll and /dev/poll and epoll */
#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 */

/* For /dev/poll */
#ifdef HAVE_SYS_DEVPOLL_H
#include <sys/devpoll.h>
#endif /* HAVE_SYS_DEVPOLL_H */

/* For epoll */
#ifdef HAVE_SYS_EPOLL_H
#include <sys/epoll.h>
#endif /* HAVE_SYS_EPOLL_H */

/* For kqueue. */
#ifdef HAVE_SYS_EVENT_H
#include <sys/event.h>
#endif /* HAVE_SYS_EVENT_H */

/* The following are used on Linux'es that have an old libc. */
#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 */

/*
 * Debugging and tracing.
 */

/* #define POLL_DEBUG */
/* #define CALL_OUT_DEBUG */

#ifdef PIKE_EXTRA_DEBUG
/* #define POLL_DEBUG */
/* #define CALL_OUT_DEBUG */
#endif

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

#ifdef CALL_OUT_DEBUG
#define IF_CO(X) X
#else
#define IF_CO(X)
#endif

#ifdef PIKE_THREADS
#define THR_NO (int) PTR_TO_INT (THREAD_T_TO_PTR (th_self()))
#else
#define THR_NO getpid()
#endif

/* 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);

/* CALL OUT STUFF */

struct call_out_s
{
  INT32 pos;
  size_t fun_hval;
  struct timeval tv;
  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;
};

/* MUST be after any include of "block_alloc_h.h"! */
#include "block_alloc.h"

#undef BLOCK_ALLOC_NEXT
#define BLOCK_ALLOC_NEXT	next_fun

#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;

/*
 * Stuff to map fds to the proper Backend
 */
static struct Backend_struct **fd_map=0;
static int fd_map_size=0;
static struct object *default_backend_obj = NULL;
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 %"PRINTSIZET"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;
}

/*! @module Pike
 */

/*! @class __Backend
 *!   Base class for the various backend implementations.
 *!
 *!   Implements callback registration functions and defines the
 *!   main backend APIs.
 */
PIKECLASS Backend
{
  /* Provide a unique count to be able to tell backends apart with _sprintf. */
  static int unused_id = 0;
  CVAR int id;

  CVAR struct timeval next_timeout;

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

  /*! @decl function(Backend:void) before_callback
   *! @decl function(Backend:void) after_callback
   *!
   *! If set, these are called just before and after the backend waits
   *! for an event.
   *!
   *! If an error is thrown from these callbacks then it is reported
   *! using @expr{master()->handle_error()@} - it doesn't interrupt
   *! the operation of the backend.
   */
  /* before_callback is not strictly necessary since one can just as
   * well run it before `(), but it's convenient to have a standard
   * place to hook in a function. */
  PIKEVAR function(Backend:void) before_callback;
  PIKEVAR function(Backend:void) after_callback;

  /* 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;
  CVAR int may_need_wakeup;

  /*
   * FD callback data
   */

  /* An array indexed on fd with arbitrary upper and lower bounds. A
   * lower bound 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;

  /*
   * Hooks to inheriting classes.
   */
#ifdef PIKE_DEBUG
  typedef void debug_handler_fn (struct Backend_struct *me, void *data);
  CVAR debug_handler_fn *debug_handler;
#endif /* PIKE_DEBUG */
  typedef void update_fd_set_handler_fn (struct Backend_struct *me, void *data,
					 int fd,
					 int old_events, int new_events);
  CVAR update_fd_set_handler_fn *update_fd_set_handler;
  CVAR void *handler_data;

  /*
   * 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;

#ifdef _REENTRANT
  /* Currently only used for poll devices. */
  CVAR int set_busy;
  CVAR COND_T set_change;
#endif

  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' */
#ifdef _REENTRANT
    while (box->backend->set_busy) {
      co_wait_interpreter(&box->backend->set_change);
    }
#endif /* _REENTRANT */
    return 0;
  }

  /* This is used by threaded programs and signals to wake up the
   * master 'thread'.
   *
   * It's called from the signal handler so it must not lock any mutex
   * whatsoever. E.g. dmalloc stuff is verboten here.
   */
  PMOD_EXPORT void backend_wake_up_backend(struct Backend_struct *me)
  {
    char foo=0;

    if(me && me->may_need_wakeup && (me->wakeup_pipe_send_fd >= 0)) {
      /* Avoid fd_write with its dmalloc stuff. */
      int len;
      do {
	len =
#ifdef HAVE_WINSOCK_H
	  debug_fd_write
#else
	  write
#endif
	  (me->wakeup_pipe_send_fd, &foo ,1);
      } while ((len < 0) && (errno == EINTR));
    }
  }

  /* Lower the timeout
   *
   * Typically used from backend callbacks.
   */
  PMOD_EXPORT void backend_lower_timeout(struct Backend_struct *me,
					 struct timeval *tv)
  {
    if (my_timercmp(tv, <=, &me->next_timeout)) {
      me->next_timeout = *tv;
    }
  }

  /*
   * 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);
  }

  void call_backend_monitor_cb (struct Backend_struct *me, struct svalue *cb)
  {
    ref_push_object (me->backend_obj);
    safe_apply_svalue (cb, 1, 1);
    pop_stack();
  }

  /*
   * 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
  static void do_unprotect_call_outs(struct Backend_struct *me)
  {
    me->inside_call_out = 0;
  }

#define DECLARE_PROTECT_CALL_OUTS	ONERROR pco_err
#define PROTECT_CALL_OUTS()					\
  do {								\
    if(me->inside_call_out)					\
      Pike_fatal("Recursive call in call_out module.\n");	\
    SET_ONERROR(pco_err, do_unprotect_call_outs, me);		\
    me->inside_call_out=1;					\
  } while(0)
 
#define UNPROTECT_CALL_OUTS()			\
  CALL_AND_UNSET_ONERROR(pco_err)

#else /* !PIKE_DEBUG */
#define DECLARE_PROTECT_CALL_OUTS
#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)
   {
     call_out *new;
     struct array *args;
     size_t fun_hval;
     size_t hval;
     DECLARE_PROTECT_CALL_OUTS;
     
     /* NOTE: hash_svalue() can run Pike code! */
     fun_hval = hash_svalue(Pike_sp + 1 - num_arg);

     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_hash)
       {
	 if (!me->call_buffer) {
	   me->call_buffer_size=128;
	   me->call_buffer =
	     (call_out **)xalloc(sizeof(call_out *)*me->call_buffer_size);
	   MEMSET(me->call_buffer, 0, sizeof(call_out *)*me->call_buffer_size);
	   me->num_pending_calls=0;
	 }
	 if (!me->call_hash) {
	   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=c->fun_hval;
	     LINK(fun,c);
	   }
	 }
       }
     }

     /* time to allocate a new call_out struct */
     push_array(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 = new->fun_hval = fun_hval;
       LINK(fun,new);
     }
     
     switch(Pike_sp[-2].type)
     {
       case T_INT:
	 new->tv.tv_sec=Pike_sp[-2].u.integer;
	 new->tv.tv_usec=0;
	 break;
	 
       case T_FLOAT:
       {
	 FLOAT_TYPE tmp=Pike_sp[-2].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);
       IF_CO (fprintf (stderr, "BACKEND[%d]: Adding call out at %ld.%ld "
		       "(current time is %ld.%ld)\n", me->id,
		       new->tv.tv_sec, new->tv.tv_usec,
		       tmp.tv_sec, tmp.tv_usec));
     }else
#endif
     {
       my_add_timeval(& new->tv, &current_time);
       IF_CO (fprintf (stderr, "BACKEND[%d]: Adding call out at %ld.%ld "
		       "(current_time is %ld.%ld)\n", me->id,
		       new->tv.tv_sec, new->tv.tv_usec,
		       current_time.tv_sec, current_time.tv_usec));
     }

     if(Pike_fp && Pike_fp->current_object)
     {
       add_ref(new->caller=Pike_fp->current_object);
     }else{
       new->caller=0;
     }
     
     new->args=args;
     Pike_sp -= 2;
     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;
   }

  static void backend_count_memory_in_call_outs(struct Backend_struct *me)
  {
    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));

  }

  static void count_memory_in_call_outs(struct callback *foo,
					void *bar,
					void *gazonk)
  {
    backend_count_memory_in_call_outs(default_backend);
  }

  /*! @decl mapping(string:int) get_stats()
   *!
   *! Get some statistics about the backend.
   *!
   *! @returns
   *!   Returns a mapping with the follwoing content:
   *!   @mapping
   *!     @member int "num_call_outs"
   *!       The number of active call-outs.
   *!     @member int "call_out_bytes"
   *!       The amount of memory used by the call-outs.
   *!   @endmapping
   */
  PIKEFUN mapping(string:int) get_stats()
  {
    struct svalue *save_sp = Pike_sp;
    backend_count_memory_in_call_outs(THIS);
    f_aggregate_mapping(Pike_sp - save_sp);
    stack_pop_n_elems_keep_top(args);
  }

   /* 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 array 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 array 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);
       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;
	 DECLARE_PROTECT_CALL_OUTS;
	 
	 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)
	 {
	   IF_CO(
	     fprintf(stderr, "[%d]BACKEND[%d]: backend_do_call_outs: "
		     "calling call out ", THR_NO, me->id);
	     print_svalue (stderr, Pike_sp - args);
	     fputc ('\n', stderr);
	   );
	   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{
	   IF_CO(fprintf(stderr, "[%d]BACKEND[%d]: backend_do_call_outs: "
			 "ignoring destructed call out\n", THR_NO, me->id));
	   pop_n_elems(args);
	 }
	 backend_verify_call_outs(me);
	 
	 GETTIMEOFDAY(&current_time);
	 if(my_timercmp(&current_time, > , &tmp)) break;
       }

       IF_CO (
	 if (me->num_pending_calls)
	   fprintf (stderr, "BACKEND[%d]: backend_do_call_outs: stopping with %d "
		    "call outs left, closest with time %ld.%ld "
		    "(current_time %ld.%ld, limit at %ld.%ld)\n",
		    me->id, me->num_pending_calls,
		    CALL(0)->tv.tv_sec, CALL(0)->tv.tv_usec,
		    current_time.tv_sec, current_time.tv_usec,
		    tmp.tv_sec, tmp.tv_usec);
	 else
	   fprintf (stderr, "BACKEND[%d]: backend_do_call_outs: "
		    "no outstanding call outs\n",
		    me->id);
       );

       return call_count;
     }

   /* NB: Calls Pike code, so MUST NOT be used in a PROTECT_CALL_OUTS()
    *     context.
    *
    *     Note that a non-NULL return value only indicates that the
    *     fun was found before the calls of is_eq(), which may have
    *     caused the call_out to be removed.
    */
   static struct array *backend_find_call_out_info(struct Backend_struct *me,
						   struct svalue *fun)
     {
       size_t hval, fun_hval;
       call_out *c;
       struct svalue *save_sp;
       DECLARE_PROTECT_CALL_OUTS;

       if(!me->num_pending_calls) return NULL;

       PROTECT_CALL_OUTS();

       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
	     UNPROTECT_CALL_OUTS();
	     add_ref(c->args);
	     return c->args;
	   }
	 }
       }

       fun_hval=hash_svalue(fun);
       hval = fun_hval % me->hash_size;
       save_sp = Pike_sp;
       for(c=me->call_hash[hval].fun;c;c=c->next_fun)
       {
	 if(c->fun_hval == fun_hval)
	 {
#ifdef PIKE_DEBUG
	   if(CALL(c->pos) != c)
	     Pike_fatal("Call_out->pos not correct!\n");
#endif
	   /* Delay the is_eq() call until we've finished
	    * scanning the hash table.
	    */
	   ref_push_array(c->args);
	 }
       }
       UNPROTECT_CALL_OUTS();

       /* Check if any of the potential hits we found is a match. */
       /* Note: is_eq() may call Pike code (which we want),
	*       however, we can't let it modify the hash
	*       table while we're scanning it.
	*/
       while (Pike_sp > save_sp) {
	 struct array *res = Pike_sp[-1].u.array;
	 /* FIXME: Use CYCLIC! */
	 if (is_eq(fun, ITEM(res))) {
	   add_ref(res);
	   pop_n_elems(Pike_sp - save_sp);
	   return res;
	 }
	 pop_stack();
       }

       return NULL;
     }

   /* Typically used in a PROTECT_CALL_OUTS() context. */
   static int backend_find_call_out(struct Backend_struct *me,
				    struct array *co_info)
     {
       size_t hval, fun_hval;
       call_out *c;

       if(!co_info || !me->num_pending_calls) return -1;

       hval=PTR_TO_INT(co_info);
       hval%=me->hash_size;
       for(c=me->call_hash[hval].arr;c;c=c->next_arr)
       {
	 if(c->args == co_info)
	 {
#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(array 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;
     struct array *co_info;

     backend_verify_call_outs(me);

     co_info = backend_find_call_out_info(me, f);

     if(!co_info)
     {
       Pike_sp->type = T_INT;
       Pike_sp->subtype = NUMBER_UNDEFINED;
       Pike_sp->u.integer=-1;
       Pike_sp++;
     } else {
       int e;
       struct timeval now;
       DECLARE_PROTECT_CALL_OUTS;
       PROTECT_CALL_OUTS();
       e = backend_find_call_out(me, co_info);
       pop_n_elems(args);
       free_array(co_info);
       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(array 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;
     struct array *co_info;

     backend_verify_call_outs(me);

     co_info = backend_find_call_out_info(me, f);

     if(!co_info)
     {
       pop_n_elems(args);
       Pike_sp->type = T_INT;
       Pike_sp->subtype = NUMBER_UNDEFINED;
       Pike_sp->u.integer = -1;
       Pike_sp++;
     } else {
       int e;
       DECLARE_PROTECT_CALL_OUTS;

       PROTECT_CALL_OUTS();
       backend_verify_call_outs(me);
       e = backend_find_call_out(me, co_info);
       backend_verify_call_outs(me);
       if(e!=-1)
       {
	 IF_CO (fprintf (stderr, "BACKEND[%d]: Removing call out at %ld.%ld "
			 "(current_time is %ld.%ld)\n", me->id,
			 CALL(e)->tv.tv_sec, CALL(e)->tv.tv_usec,
			 current_time.tv_sec, current_time.tv_usec));
	 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++;
       }
       free_array(co_info);
       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;
       ONERROR err;
       DECLARE_PROTECT_CALL_OUTS;

       backend_verify_call_outs(me);
       PROTECT_CALL_OUTS();
       ret=allocate_array_no_init(0, me->num_pending_calls);
       SET_ONERROR(err, do_free_array, ret);
       ret->type_field = BIT_ARRAY;
       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;
	   ITEM(v)[1].subtype = 0;
	   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->size++;
       }
       UNSET_ONERROR(err);
       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 {
#ifdef PIKE_DEBUG
	   {
	     struct fd_callback_box **p, **boxes = me->inactive_boxes;
	     int i;
	     int max = pos; /* me->inactive_boxes_size before enlargement. */
	     for (i = 0; i < max; i++)
	       if ((p = (struct fd_callback_box **) boxes[i]) &&
		   p >= boxes && p < boxes + max)
		 Pike_fatal ("Still got free list pointers in inactive box "
			     "list that is about to be enlarged.\n");
	   }
#endif
	   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;
     }
   }

   static void update_fd_set (struct Backend_struct *me, int fd,
			      int old_events, int new_events)
   {
     if (fd < 0) return;

#ifdef __NT__
     if (new_events && !(fd_query_properties(fd, fd_CAN_NONBLOCK) & fd_CAN_NONBLOCK)) {
       Pike_fatal("update_fd_set() on non-socket!\n");
     }
#endif /* __NT__ */

     if (me->update_fd_set_handler) {
       me->update_fd_set_handler(me, me->handler_data,
				 fd, old_events, new_events);
     } else {
       Pike_fatal("No update_fd_set_handler set[%p, %d, 0x%04x, 0x%04x].\n");
     }
   }

  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",
		  THR_NO, me->id, fd, box->events, box->ref_obj));
#ifdef PIKE_DEBUG
    if (!me) Pike_fatal ("Backend not set.\n");
#endif
#ifdef __NT__
    if ((fd >= 0) && box->events &&
	!(fd_query_properties(fd, fd_CAN_NONBLOCK) & fd_CAN_NONBLOCK)) {
      Pike_fatal("hook_fd_callback_box() on non-socket!\n"
		 "  fd: %d\n"
		 "  events: 0x%04x\n"
		 "  fd_properties: 0x%04x\n",
		 fd, box->events, fd_query_properties(fd, fd_CAN_NONBLOCK));
    }
#endif /* __NT__ */
    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);
  }

  /**
   * Unlink the box from the active list it is on (if any),
   * and return 1 if it was active (and thus needs to be freed).
   */
  static int unlink_box(struct fd_callback_box *box)
  {
    if (box->next) {
      /* The box is active in some backend. Unlink it. */
      struct fd_callback_box *pred = box->next;
      /* Find the predecessor. */
      while (pred->next != box) {
	pred = pred->next;
      }
      pred->next = box->next;
      box->next = NULL;
      return 1;
    }
    return 0;
  }

  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", THR_NO, 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",
		  THR_NO, 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;
    /* Make sure no further callbacks are called on this box. */
    box->revents = 0;

    /* Unlink the box from the active list in case it is there,
     * otherwise the active list may get cut, which could cause
     * the remaining boxes on the list to be lost from the backend..
     */
    if (unlink_box(box) && box->ref_obj) {
      /* 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);
    }

    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);
      box->events = 0;
    }
  }

  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",
		  THR_NO, 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;
    if (old) 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",
		  THR_NO, old?old->id:-1, box->fd, new->id));

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

	/* Unlink the box from the active in the old backend,
	 * and free it if it was there.
	 */
	if (unlink_box(box) && box->ref_obj) {
	  free_object(box->ref_obj);
	}
      }
      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, obj: %p\n",
		    THR_NO, old_fd, new_fd, box->ref_obj));
      box->fd = new_fd;
      box->revents = 0;
    }

    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;
	box->revents = 0;
	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", THR_NO, box->backend->id, old_fd, new_fd));
    }
  }

  static void do_free_fd_box(struct fd_callback_box *box)
  {
    if (box->ref_obj) free_object(box->ref_obj);
  }

  static void do_free_fd_list(struct fd_callback_box *fd_list)
  {
    struct fd_callback_box *box;
    while ((box = fd_list->next)) {
      fd_list->next = box->next;
      box->next = NULL;
      if (box->ref_obj) free_object(box->ref_obj);
    }
  }

#ifdef PIKE_DEBUG

  static void backend_do_debug(struct Backend_struct *me)
  {
    backend_verify_call_outs(me);

    if (me->debug_handler) {
      me->debug_handler(me, me->handler_data);
    }
      
    {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 */

  /*! @decl float|int(0..0) `()(void|float|int(0..0) sleep_time)
   *!   Perform one pass through the backend.
   *!
   *!   Calls any outstanding call-outs and non-blocking I/O
   *!   callbacks that are registred in this backend object.
   *!
   *! @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.
   *!
   *! @seealso
   *!   @[Pike.DefaultBackend], @[main()]
   */
  PIKEFUN float|int(0..0) `()(void|float|int(0..0) sleep_time)
    prototype;
  {
  }

#ifndef tObjImpl_THREAD
  /* Kludge for precompile.pike; it resolves object(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 hasn'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)|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)
   *!
   *! Register a file to be handled by this backend.
   *! 
   *! @param f
   *!   File to register.
   *!
   *! Registers @[f] to be handled by this backend.
   *! This simply does @expr{f->set_backend(backend)@} where
   *! @expr{backend@} is this object.
   *!
   *! @seealso
   *!   @[Pike.DefaultBackend], @[main()]
   */
  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);
      }}
  }

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

  static void low_backend_once_setup(struct Backend_struct *me,
				     struct timeval *start_time)
  {
#ifdef PIKE_DEBUG
    struct timeval max_timeout;
#endif
    struct timeval *next_timeout = &me->next_timeout;

    alloca(0);			/* Do garbage collect */
#ifdef PIKE_DEBUG
    if(d_flag > 1) do_debug();
#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
      
#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
    if (start_time->tv_sec < 0) {
      next_timeout->tv_sec = -1;
      next_timeout->tv_usec = 0;
    }
    else {
      *next_timeout = *start_time;
      my_add_timeval(next_timeout, &current_time);
    }

    *start_time = current_time;

    /* Call outs */
    if(me->num_pending_calls)
      if(next_timeout->tv_sec < 0 ||
	 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, me);
#ifdef PIKE_DEBUG
    if (max_timeout.tv_sec >= 0 &&
	(next_timeout->tv_sec < 0 ||
	 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 (next_timeout->tv_sec < 0) {
      /* Wait "forever". */
      next_timeout->tv_sec = 100000000;
      next_timeout->tv_usec = 0;
    }
    else 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;
    }
  }

  /* Call callbacks for the active events.
   *
   * NOTE: The first element in the fd_list is a sentinel!
   *
   * returns 1 on early exit.
   */
  static int backend_call_active_callbacks(struct fd_callback_box *fd_list,
					   struct Backend_struct *me)
  {
    struct fd_callback_box *box;
    while((box = fd_list->next))
    {
      int fd = box->fd;
      ONERROR uwp;

      /* Unhook the box. */
      fd_list->next = box->next;
      box->next = NULL;
      SET_ONERROR(uwp, do_free_fd_box, box);

      IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: Examining box for fd %d"
		    " revents:0x%04x\n",
		    THR_NO, me->id, fd, box->revents));

      if (box->fd < 0) {
	/* The box is no longer active.
	 * Or we have found our sentinel fd_list.
	 *
	 * Note that the loop will terminate, since we
	 * have broken the cycle above when we set
	 * box->next to NULL.
	 */
	CALL_AND_UNSET_ONERROR(uwp);
	continue;
      }

      /* From the roxen-chat re: connecttest.pike/FreeBSD:
       *
       * kqueue is returning the correct info:
       * {7,EVFILT_READ,EV_ADD|EV_EOF,61,0x0,0x0}.
       * errno 61 is ECONNREFUSED
       */
      if (box->revents & box->events & PIKE_BIT_FD_READ_OOB) {
	IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: read_oob_callback(%d, %p)\n",
		      THR_NO, me->id, fd, box->ref_obj));
	errno = 0;
	if (box->callback (box, PIKE_FD_READ_OOB) == -1) {
	  CALL_AND_UNSET_ONERROR(uwp);
	  goto backend_round_done;
	}
      }

      if (box->revents & box->events & PIKE_BIT_FD_READ) {
	/* FIXME: Consider utilizing ACTIVE_POLLSET[i].data in
	 *        the kqueue case.
	 */
	IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: read_callback(%d, %p)\n",
		      THR_NO, me->id, fd, box->ref_obj));
	errno = 0;
	if (box->callback (box, PIKE_FD_READ) == -1) {
	  CALL_AND_UNSET_ONERROR(uwp);
	  goto backend_round_done;
	}
      }

      if (box->revents & box->events & PIKE_BIT_FD_WRITE_OOB) {
	IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: write_oob_callback(%d, %p)\n",
		      THR_NO, me->id, fd, box->ref_obj));
	errno = 0;
	if (box->callback (box, PIKE_FD_WRITE_OOB) == -1) {
	  CALL_AND_UNSET_ONERROR(uwp);
	  goto backend_round_done;
	}
      }

      if (box->revents & box->events & PIKE_BIT_FD_WRITE) {
	/* FIXME: Consider utilizing ACTIVE_POLLSET[i].data in
	 *        the kqueue case.
	 */
	IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: write_callback(%d, %p)\n",
		      THR_NO, me->id, fd, box->ref_obj));
	errno = 0;
	if (box->callback (box, PIKE_FD_WRITE) == -1) {
	  CALL_AND_UNSET_ONERROR(uwp);
	  goto backend_round_done;
	}
      }

      if (box->revents & PIKE_BIT_FD_ERROR) {
	/* Error */
	int old_events;
	int err;
	ACCEPT_SIZE_T len = sizeof (err);
	errno = 0;
	/* FIXME: This could be too late - the error might be
	 * clobbered by the callbacks we might have called
	 * above. */
	if (!getsockopt (fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len)) {
	  IF_PD (fprintf (stderr,
			  "[%d]BACKEND[%d]: POLLERR on %d, error=%d\n",
			  THR_NO, me->id, fd, err));
	  errno = err;
	}
	else {
	  /* Note: This happens for FIFOs and PIPEs on Linux on the write-end
	   *       if the read-end has been closed.
	   */
#ifdef PIKE_DEBUG
#ifdef ENOTSOCK
	  if (errno != ENOTSOCK) {
#endif
	    fprintf(stderr,
		    "Got POLLERR on non-socket fd %d (getsockopt errno=%d)\n",
		    fd, errno);
#ifdef ENOTSOCK
	  } else {
	    IF_PD(fprintf(stderr, "Got POLLERR on non-socket fd %d\n", fd));
	  }
#endif
#endif /* PIKE_DEBUG */
	  errno = err = EPIPE;
	}

	box->revents = 0;
	    
	/* We don't want to keep this fd anymore.
	 * Note: This disables any further callbacks.
	 */
	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",
			THR_NO, me->id, fd, box->ref_obj));
	  if (box->callback (box, PIKE_FD_ERROR) == -1) {
	    CALL_AND_UNSET_ONERROR(uwp);
	    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",
			THR_NO, me->id, fd, box->ref_obj, err));
	  if (box->callback (box, PIKE_FD_READ) == -1) {
	    CALL_AND_UNSET_ONERROR(uwp);
	    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",
			THR_NO, me->id, fd, box->ref_obj, err));
	  if (box->callback (box, PIKE_FD_WRITE) == -1) {
	    CALL_AND_UNSET_ONERROR(uwp);
	    goto backend_round_done;
	  }
	}
      }

      CALL_AND_UNSET_ONERROR(uwp);
    }
    return 0;

  backend_round_done:
    return 1;
  }

  INIT
  {
    struct Backend_struct *me = THIS;

    me->id = unused_id++;

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

#ifdef _REENTRANT
    me->set_busy = 0;
    co_init(&me->set_change);
#endif /* _REENTRANT */
    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 PIKE_DEBUG
    me->debug_handler = NULL;
#endif
    me->update_fd_set_handler = NULL;
    me->handler_data = me;

    /* Note that we can't hook the wakeup pipe
     * until we are fully initialized.
     * The actual hooking of the wakeup pipe
     * is therefore done in create() below.
     */
    me->wakeup_pipe_send_fd = -1;
    INIT_FD_CALLBACK_BOX(&me->wakeup_cb_box, me, NULL, -1,
			 PIKE_BIT_FD_READ, wakeup_callback);

    me->may_need_wakeup = 0;

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

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

    free_callback_list(& THIS->backend_callbacks);

    if (THIS->wakeup_cb_box.fd >= 0)
      fd_close(THIS->wakeup_cb_box.fd);
    if (me->wakeup_pipe_send_fd >= 0)
      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",
		   THR_NO, me->id, box->fd, box->events);
#endif
	  really_free_compat_cb_box ((struct compat_cb_box *) box);
	}

	unhook_fd_callback_box(box);
      }

      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

	unhook_fd_callback_box(box);
      }

      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;
    }

    /* 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 _REENTRANT
    co_destroy(&me->set_change);
#endif

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

  PIKEFUN void create()
    flags ID_PROTECTED;
  {
    struct Backend_struct *me = THIS;

    if (!me->update_fd_set_handler) {
      Pike_error("Attempt to clone the base Backend class.\n");
    }

    if (me->wakeup_pipe_send_fd < 0) {
      int pipe[2];
      IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: Creating wakeup pipe...\n",
		    THR_NO, me->id));
      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);
      IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: Initializing wakeup pipe...\n",
		    THR_NO, me->id));
      change_fd_for_box (&me->wakeup_cb_box, pipe[0]);
      me->wakeup_pipe_send_fd = pipe[1];

      IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: Wakeup pipe is [%d, %d]\n",
		    THR_NO, me->id,
		    me->wakeup_pipe_send_fd, THIS->wakeup_cb_box.fd));

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

/*! @endclass
 */

/*
 * POLL/SELECT selection
 */

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

/* #undef BACKEND_USES_DEVPOLL */
/* #undef BACKEND_USES_DEVEPOLL */
/* #undef BACKEND_USES_POLL_DEVICE */

#ifdef HAVE_POLL

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

/* 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

#ifdef BACKEND_USES_DEVPOLL
/*
 * 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.
 */

#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 PDB_POLL(PFD, TIMEOUT)					\
  ((poll_request.dp_timeout = (TIMEOUT)),			\
   (ioctl(PFD, DP_POLL, &poll_request, sizeof(poll_request))))

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

  IF_PD(fprintf(stderr, "POLL_DEVICE_SET_EVENTS(%p, %d, %d, 0x%08x)\n",
		me, pfd, fd, events));

  /* 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]);
  }

#ifdef _REENTRANT
  /* FIXME: Ought to check if we're the backend.
   */
  if(num_threads>1)
  {
    SWAP_OUT_CURRENT_THREAD();
    /* Release the poll set from the backend. */
    IF_PD(fprintf(stderr, "POLL_DEVICE_SET_EVENTS[%p] grabbing the poll set\n",
		  me));
    while (me->set_busy) {
      co_wait_interpreter(&me->set_change);
    }
    me->set_busy = 1;
    IF_PD(fprintf(stderr, "POLL_DEVICE_SET_EVENTS[%p] wake up backend\n",
		  me));
    backend_wake_up_backend(me);
    /* The backend is now waiting in wakeup_callback(). */
    SWAP_IN_CURRENT_THREAD();
  }
#endif /* _REENTRANT */

  IF_PD(fprintf(stderr, "POLL_DEVICE_SET_EVENTS[%p] updating the poll set\n",
		me));
  while (((e = write(pfd, poll_state, sz)) < 0) && (errno == EINTR))
    ;

#ifdef _REENTRANT
  me->set_busy = 0;
  if(num_threads>1)
  {
    /* Release the backend from wakeup_callback(). */
    IF_PD(fprintf(stderr, "POLL_DEVICE_SET_EVENTS[%p] releasing the backend\n",
		  me));
    co_broadcast(&me->set_change);
  }
#endif /* _REENTRANT */

  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(BACKEND_USES_DEVEPOLL)
/*
 * 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.
 */

#ifndef PIKE_POLL_DEVICE
#define PIKE_POLL_DEVICE	"epoll"
#endif

/* 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.
 */
#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 POLL_EVENT	struct epoll_event
#define PDB_GET_FD(EVENT)	EVENT.data.fd
#define PDB_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 PDB_POLL(PFD, TIMEOUT)				\
  epoll_wait(PFD, poll_fds, POLL_SET_SIZE, TIMEOUT)

int POLL_DEVICE_SET_EVENTS(struct Backend_struct *me,
			   int pfd, int fd, INT32 events)
{
  int e;

  if (events) {
    struct epoll_event ev;
#ifdef __CHECKER__
    MEMSET(&ev, 0, sizeof(ev));
#endif
    ev.events = events;
    ev.data.fd = fd;

    /* To avoid valgrind complaints when fd doesn't fill up the
     * ev.data union. */
    PIKE_MEM_RW (ev.data);

    /* 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...
     */
    PIKE_MEM_RW (dummy);
    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;
}

#endif /* HAVE_SYS_DEVPOLL_H || HAVE_SYS_EPOLL_H */

#ifdef HAVE_POLL

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

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

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

static void pb_MY_FD_SET(struct pb_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 pb_MY_FD_CLR(struct pb_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 pb_copy_selectors(struct pb_selectors *to,
			      struct pb_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_POLL */

#define PB_GET_FD(EVENT)	EVENT.fd
#ifndef PDB_GET_FD
#define PDB_GET_FD(EVENT)	PB_GET_FD(EVENT)
#endif
#define PB_GET_EVENTS(EVENT)	EVENT.revents
#ifndef PDB_GET_EVENTS
#define PDB_GET_EVENTS(EVENT)	PB_GET_EVENTS(EVENT)
#endif

#elif defined(BACKEND_USES_KQUEUE)
/*
 * Backend using kqueue-style poll device.
 *
 * FIXME: Not fully implemented yet! Out of band data handling is missing.
 *
 * Used on
 *   FreeBSD 4.1 and above.
 *   MacOS X/Darwin 7.x and above.
 *   Various other BSDs.
 */


#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

#define MY_POLLERR	EV_ERROR
#if 0
#define MY_POLLHUP	EV_EOF
#else /* !0 */
#define MY_POLLHUP	0
#endif /* 0 */

#ifdef __APPLE__
/* Not defined in old MacOS X. */
#ifndef EV_OOBAND
#define EV_OOBAND	EV_FLAG1
#endif
#elif !defined(EV_OOBAND)
/* FreeBSD seems to handle OOB data inline with ordinary data. */
#define EV_OOBAND	0
#endif /* __APPLE__ */

/* 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 PDB_POLL(SET, TIMEOUT)					\
  kevent((SET), NULL, 0, poll_fds, POLL_SET_SIZE, &(TIMEOUT))

#define PDB_GET_FD(EVENT)		EVENT.ident
#define PDB_GET_EVENTS(EVENT)		EVENT.filter
#define PDB_CHECK_EVENT(EVENT, MASK)	(PDB_GET_EVENTS(EVENT) == (MASK))

/* NOTE: Error events are signalled in the flags field. They thus
 *       must be checked for before the ordinary events.
 */
#define PDB_CHECK_ERROR_EVENT(EVENT, MASK)	(EVENT.flags & (MASK))

int pdb_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 pdb_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 pdb_MY_FD_CLR_RDBAND(SET, FD)
#define pdb_MY_FD_CLR_WRBAND(SET, FD)

#endif

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

#define MY_READSET	0
#define MY_WRITESET	1
#define MY_EXCEPTSET	2
/* except == incoming OOB data (or error according to POSIX)
 * outgoing OOB data is multiplexed on write
 */

struct sb_selectors
{
  int max_fd;
  my_fd_set sets[3];
};

struct sb_active_selectors
{
  fd_set asets[3];
  int max_fd;
};

#define SB_SELECT(SET, TIMEOUT)					\
  fd_select((SET).max_fd + 1,					\
	    (SET).asets + MY_READSET,				\
	    (SET).asets + MY_WRITESET,				\
	    (SET).asets + MY_EXCEPTSET,				\
	    (TIMEOUT).tv_sec >= 100000000 ? NULL : &(TIMEOUT))

void sb_MY_FD_CLR(struct sb_selectors *me, int fd, int setno)
{
  if(fd > me->max_fd) return;
  my_FD_CLR(fd, me->sets + setno);
  if(fd == me->max_fd)
  {
    while(me->max_fd >=0 &&
	  !my_FD_ISSET(me->max_fd, me->sets + MY_READSET) &&
	  !my_FD_ISSET(me->max_fd, me->sets + MY_WRITESET)
	  && !my_FD_ISSET(me->max_fd, me->sets + MY_EXCEPTSET)
      )
      me->max_fd--;
  }
}

void sb_MY_FD_SET(struct sb_selectors *me, int fd, int setno)
{
  my_FD_SET(fd, me->sets + setno);
  if(fd > me->max_fd) me->max_fd=fd;
}

static void sb_copy_selectors(struct sb_active_selectors *to,
			      struct sb_selectors *from)
{
  fd_copy_my_fd_set_to_fd_set(to->asets + MY_READSET,
			      from->sets + MY_READSET, from->max_fd+1);
  fd_copy_my_fd_set_to_fd_set(to->asets + MY_WRITESET,
			      from->sets + MY_WRITESET, from->max_fd+1);
  fd_copy_my_fd_set_to_fd_set(to->asets + MY_EXCEPTSET,
			      from->sets + MY_EXCEPTSET, from->max_fd+1);
  to->max_fd=from->max_fd;
}

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

#define PB_CHECK_EVENT(EVENT, MASK)	(PB_GET_EVENTS(EVENT) & (MASK))
#ifndef PDB_CHECK_EVENT
#define PDB_CHECK_EVENT(EVENT, MASK)	(PDB_GET_EVENTS(EVENT) & (MASK))
#endif /* PB_CHECK_EVENT */

#define PB_CHECK_ERROR_EVENT(EVENT, MASK)	PB_CHECK_EVENT(EVENT, MASK)
#ifndef PDB_CHECK_ERROR_EVENT
#define PDB_CHECK_ERROR_EVENT(EVENT, MASK)	PDB_CHECK_EVENT(EVENT, MASK)
#endif /* PB_CHECK_ERROR_EVENT */

#ifdef RDBAND_IS_SPECIAL
#  define pb_MY_FD_CLR_RDBAND(SET, FD) pb_MY_FD_CLR (SET, FD, MY_POLLRDBAND)
#else
#  define pb_MY_FD_CLR_RDBAND(SET, FD)
#endif

#ifdef WRBAND_IS_SPECIAL
#  define pb_MY_FD_CLR_WRBAND(SET, FD) pb_MY_FD_CLR (SET, FD, MY_POLLWRBAND)
#else
#  define pb_MY_FD_CLR_WRBAND(SET, FD)
#endif

#ifndef MY_POLLERR
#define MY_POLLERR	POLLERR
#endif

#ifndef MY_POLLHUP
#define MY_POLLHUP	POLLHUP
#endif

#if defined(BACKEND_USES_POLL_DEVICE) || defined(BACKEND_USES_KQUEUE)

/*! @class PollDeviceBackend
 *! @inherit __Backend
 *!
 *! @[Backend] implemented with @tt{/dev/poll@} (Solaris, OSF/1 and IRIX),
 *! @tt{epoll(2)@} (Linux) or @tt{kqueue(2)@} (MacOS X, FreeBSD, OpenBSD, etc).
 *!
 *! @seealso
 *!   @[Backend]
 */
PIKECLASS PollDeviceBackend
{
  INHERIT Backend;

  /* Helpers to find the above inherit. */
  static ptrdiff_t pdb_offset = 0;
  CVAR struct Backend_struct *backend;

  /* 
   * POLL/SELECT fd sets
   */
  CVAR int set;

  DECLARE_STORAGE

  /*
   * FD set handling
   */

  static void pdb_UPDATE_BLACK_BOX(struct PollDeviceBackend_struct *me, int fd,
				   int wanted_events)
  {
#ifdef BACKEND_USES_POLL_DEVICE
    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->backend, me->set, fd, events);
#elif defined(BACKEND_USES_KQUEUE)
    /* Note: Only used by REOPEN_POLL_DEVICE on a freshly opened kqueue. */
    struct kevent ev[2];
    int nev = 0;
    if (wanted_events & (PIKE_BIT_FD_READ|PIKE_BIT_FD_READ_OOB)) {
      if (wanted_events & PIKE_BIT_FD_READ_OOB) {
	/* We want OOB notifications.
	 *
	 * Without setting EV_OOBAND, we won't get any OOB notifications,
	 * unless there is also in-band data.
	 */
	IF_PD(fprintf(stderr, "BACKEND: EV_SET fd:%d, READ, ADD, [OOBAND]\n",
		      fd));
	EV_SET(ev, fd, MY_POLLIN, EV_ADD|EV_OOBAND, 0, 0, 0);
      } else {
	IF_PD(fprintf(stderr, "BACKEND: EV_SET fd:%d, READ, ADD\n", fd));
	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) {
      IF_PD(fprintf(stderr, "BACKEND: EV_SET fd:%d, WRITE, ADD\n", fd));
      EV_SET(ev, fd, MY_POLLOUT, EV_ADD, 0, 0, 0);
      nev++;
    }
    if (nev)
      kevent(me->set, ev, nev, NULL, 0, NULL);
#endif /* BACKEND_USES_POLL_DEVICE */
  }

  /* This is called in the child process to restore
   * poll state after fork() in case of detaching.
   */
  static void pdb_REOPEN_POLL_DEVICE(struct PollDeviceBackend_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->backend, box) {
	pdb_UPDATE_BLACK_BOX (me, box->fd, box->events);
      }}
  }

  static struct PollDeviceBackend_struct **pdb_backends = NULL;
  static int num_pdb_backends = 0;
  static int pdb_backends_size = 0;

  /* Called from the init callback. */
  static void register_pdb_backend(struct PollDeviceBackend_struct *me)
  {
    if (num_pdb_backends == pdb_backends_size) {
      struct PollDeviceBackend_struct **new_backends =
	realloc(pdb_backends,
		(pdb_backends_size+1) *
		sizeof(struct PollDeviceBackend_struct *)*2);
      if (!new_backends) {
	Pike_error("Out of memory.\n");
      }
      pdb_backends = new_backends;
      pdb_backends_size = (pdb_backends_size+1)*2;
    }
    pdb_backends[num_pdb_backends++] = me;
  }

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

  /* Called in the child after fork(). */
  static void reopen_all_pdb_backends(struct callback *cb, void *a, void *b)
  {
    int i;
    for (i=0; i < num_pdb_backends; i++) {
      pdb_REOPEN_POLL_DEVICE(pdb_backends[i]);
    }
  }

  static void pdb_update_fd_set(struct Backend_struct *me,
				struct PollDeviceBackend_struct *pdb, int fd,
				int old_events, int new_events)
  {
    int changed_events = old_events ^ new_events;

    IF_PD(fprintf (stderr, "[%d]BACKEND[%d]: pdb_update_fd_set(.., %d, %d, %d):\n",
		   THR_NO, me->id, fd, old_events, new_events));
    

    if (changed_events) {

#ifdef BACKEND_USES_POLL_DEVICE

      pdb_UPDATE_BLACK_BOX(pdb, fd, new_events);

#elif defined(BACKEND_USES_KQUEUE)
      struct kevent ev[2];

      if (changed_events & (PIKE_BIT_FD_READ|PIKE_BIT_FD_READ_OOB)) {
	if (new_events & PIKE_BIT_FD_READ_OOB) {
	  /* We want OOB even if there's no in-band data. */
	  IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: fd:%d READ, ADD, [OOBAND]\n",
			THR_NO, me->id, fd));
	  EV_SET(ev, fd, EVFILT_READ, EV_ADD|EV_OOBAND, 0, 0, 0);
	  EV_SET(ev+1, fd, EVFILT_READ, EV_ENABLE|EV_OOBAND, 0, 0, 0);
	  kevent(pdb->set, ev, 2, NULL, 0, NULL);
	} else if (new_events & PIKE_BIT_FD_READ) {
	  IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: fd:%d READ, ADD\n",
			THR_NO, me->id, fd));
	  EV_SET(ev, fd, EVFILT_READ, EV_ADD, 0, 0, 0);
	  EV_SET(ev+1, fd, EVFILT_READ, EV_ENABLE, 0, 0, 0);
	  kevent(pdb->set, ev, 2, NULL, 0, NULL);
	} else {
	  IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: fd:%d READ, DISABLE\n",
			THR_NO, me->id, fd));
	  pdb_MY_FD_CLR(&pdb->set, fd, MY_POLLIN);
	}
      }

      if (changed_events & (PIKE_BIT_FD_WRITE|PIKE_BIT_FD_WRITE_OOB)) {
	if (new_events & (PIKE_BIT_FD_WRITE|PIKE_BIT_FD_WRITE_OOB)) {
	  IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: fd:%d WRITE, ADD\n",
			THR_NO, me->id, fd));
	  pdb_MY_FD_SET(&pdb->set, fd, MY_POLLOUT);
	} else {
	  IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: fd:%d WRITE, DISABLE\n",
			THR_NO, me->id, fd));
	  pdb_MY_FD_CLR(&pdb->set, fd, MY_POLLOUT);
	}
      }

#else  /* !BACKEND_USES_POLL_DEVICE */

      if (changed_events & PIKE_BIT_FD_READ) {
	if (new_events & PIKE_BIT_FD_READ) {
	  pdb_MY_FD_SET(&pdb->set, fd, MY_POLLIN);
	  /* Got to enable the exception set to get errors (at least
	   * according to POSIX). */
	  pdb_MY_FD_SET(&pdb->set, fd, MY_POLLEXCEPT);
	}
	else {
	  pdb_MY_FD_CLR(&pdb->set, fd, MY_POLLIN);
	  if (!(new_events & PIKE_BIT_FD_READ_OOB) &&
	      !(new_events & PIKE_BIT_FD_WRITE))
	    /* Exceptions might cause calls to read, read_oob and write. */
	    pdb_MY_FD_CLR(&pdb->set, fd, MY_POLLEXCEPT);
	}
      }

      if (changed_events & PIKE_BIT_FD_READ_OOB) {
	if (new_events & PIKE_BIT_FD_READ_OOB)
	  pdb_MY_FD_SET(&pdb->set, fd, MY_POLLRDBAND);
	else {
	  if (!(new_events & PIKE_BIT_FD_READ)) {
	    if (!(new_events & PIKE_BIT_FD_WRITE))
	      /* Exceptions might cause calls to read, read_oob and write. */
	      pdb_MY_FD_CLR(&pdb->set, fd, MY_POLLEXCEPT);
	  } else {
	    pdb_MY_FD_CLR_RDBAND(&pdb->set, fd);
	  }
	}
      }

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

      if (changed_events & PIKE_BIT_FD_WRITE_OOB) {
	if (new_events & PIKE_BIT_FD_WRITE_OOB)
	  pdb_MY_FD_SET(&pdb->set, fd, MY_POLLWRBAND);
	else {
	  if (!(new_events & PIKE_BIT_FD_WRITE)) {
	    pdb_MY_FD_CLR(&pdb->set, fd, MY_POLLWREXCEPT);
	  } else {
	    pdb_MY_FD_CLR_WRBAND(&pdb->set, fd);
	  }
	}
      }

#endif	/* !BACKEND_USES_POLL_DEVICE */

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

  /* Mapping of events to flags and callbacks.
   *
   * Event	select	poll		kqueue			callback
   *
   * data_in	read	POLLIN		EVFILT_READ		READ
   *			POLLRDNORM	EVFILT_READ[EOF]      
   *
   * data_out	write	POLLOUT		EVFILT_WRITE		WRITE
   *			POLLWRNORM
   *
   * oob_in	except	POLLPRI		EVFILT_READ[EV_OOBAND]	READ_OOB
   *			POLLRDBAND
   *
   * oob_out	write	POLLWRBAND	(EVFILT_WRITE)		WRITE_OOB
   *
   * close_in	read	POLLIN		EVFILT_READ[EOF]	READ
   *                    POLLHUP(Linux pipe)
   *
   * close_out	write	POLLHUP		EVFILT_WRITE[EOF]	WRITE
   *			POLLERR(Linux pipe)			>WRITE_OOB
   *
   * conn_ok	write	POLLOUT		EVFILT_WRITE		WRITE
   *
   * conn_fail	read	POLLIN		EVFILT_READ[EOF]	READ
   *		except						(READ_OOB)
   *
   * new_conn	read	POLLIN		EVFILT_READ		READ
   *
   * sock_err	except	POLLERR		EVFILT_READ[ERR]	ERROR
   *								>READ
   *
   * sock_err	except	POLLERR		EVFILT_WRITE[ERR]	ERROR
   *								>WRITE
   */


#ifdef POLL_DEBUG
  static void pdb_describe_event(struct Backend_struct *me, POLL_EVENT event)
  {
#ifdef BACKEND_USES_KQUEUE
    fprintf(stderr, "[%d]BACKEND[%d]: fd:%d filter:%d flags:0x%08x",
	    THR_NO, me->id, PDB_GET_FD(event), PDB_GET_EVENTS(event),
	    event.flags);
    if (PDB_CHECK_EVENT(event, MY_POLLIN)) {
      fprintf(stderr, "  EVFILT_READ");
    } else if (PDB_CHECK_EVENT(event, MY_POLLOUT)) {
      fprintf(stderr, "  EVFILT_WRITE");
    } else {
      fprintf(stderr, "  UNKNOWN");
    }
    if (event.flags & EV_OOBAND) {
      fprintf(stderr, "[OOBAND]");
    }
    if (event.flags & EV_ERROR) {
      fprintf(stderr, "[ERROR]");
    }
    if (event.flags & EV_EOF) {
      fprintf(stderr, "[EOF]");
    }
    fprintf(stderr, "(%d)\n", event.data);
#else /* !BACKEND_USES_KQUEUE */
    fprintf(stderr, "[%d]BACKEND[%d]: fd:%d events:0x%04x",
	    THR_NO, me->id, PDB_GET_FD(event), PDB_GET_EVENTS(event));
    if (PDB_CHECK_EVENT(event, MY_POLLNVAL)) {
      fprintf(stderr, "  POLLNVAL");
    }
    if (PDB_CHECK_EVENT(event, MY_POLLERR)) {
      fprintf(stderr, "  POLLERR");
    }
    if (PDB_CHECK_EVENT(event, MY_POLLHUP)) {
      fprintf(stderr, "  POLLHUP");
    }
    if (PDB_CHECK_EVENT(event, MY_POLLRDBAND)) {
      fprintf(stderr, "  POLLRDBAND");
    }
    if (PDB_CHECK_EVENT(event, MY_POLLIN)) {
      fprintf(stderr, "  POLLIN");
    }
    if (PDB_CHECK_EVENT(event, MY_POLLWRBAND)) {
      fprintf(stderr, "  POLLWRBAND");
    }
    if (PDB_CHECK_EVENT(event, MY_POLLOUT)) {
      fprintf(stderr, "  POLLOUT");
    }
    fprintf(stderr, "\n");
#endif /* BACKEND_USES_KQUEUE */
  }
#else /* !POLL_DEBUG */
#define pdb_describe_event(BACKEND, EVENT)
#endif /* POLL_DEBUG */

  /* 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. */
  static void pdb_low_backend_once(struct PollDeviceBackend_struct *pdb,
				   struct timeval *timeout)
  {
    ONERROR uwp;
    int i, done_something = 0;
    struct timeval start_time = *timeout;
    struct Backend_struct *me = pdb->backend;
#ifdef DECLARE_POLL_EXTRAS
    /* Declare any extra variables needed by MY_POLL(). */
    DECLARE_POLL_EXTRAS;
#endif /* DECLARE_POLL_EXTRAS */

    low_backend_once_setup(pdb->backend, &start_time);
    SET_ONERROR(uwp, low_backend_cleanup, THIS->backend);

    if (me->before_callback.type != T_INT)
      call_backend_monitor_cb (me, &me->before_callback);

    {
#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_* */
      struct timeval *next_timeout = &pdb->backend->next_timeout;

      me->may_need_wakeup = 1;

#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_* */

      IF_PD(fprintf (stderr, "[%d]BACKEND[%d]: Doing poll on fds:\n",
		     THR_NO, me->id));

      check_threads_etc();
      THREADS_ALLOW();

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

      IF_PD(fprintf(stderr, " => %d (timeout was: %d)\n", i, poll_timeout));

      THREADS_DISALLOW();
      check_threads_etc();
      me->may_need_wakeup = 0;
      GETTIMEOFDAY(&current_time);
    }

    if (me->after_callback.type != T_INT)
      call_backend_monitor_cb (me, &me->after_callback);

    if (!i) {
      /* Timeout */
    } else if (i>0) {
      int num_active = i;
      struct fd_callback_box fd_list = {
	me, NULL, &fd_list,
	-1, 0, 0,
	NULL,
      };
      struct fd_callback_box *box;
      ONERROR free_fd_list;

      SET_ONERROR(free_fd_list, do_free_fd_list, &fd_list);

      done_something = 1;


      /* First clear revents for all the fds.
       *
       * FIXME: This is done for paranoia reasons. If all code that
       *        messes with fds clears revents, this isn't needed.
       *
       * Note: This needs to be a separate loop, since kqueue sends
       *       read and write in two separate events.
       */
      while(i--)
      {
	int fd = PDB_GET_FD(poll_fds[i]);
	box = SAFE_GET_ACTIVE_BOX (me, fd);
	if (box) {
	  check_box (box, fd);
	  box->revents = 0;
	}
      }

      /* Then flag the active events.
       */
      i = num_active;
      while(i--)
      {
	int fd = PDB_GET_FD(poll_fds[i]);

	pdb_describe_event(me, poll_fds[i]);

	if (!(box = SAFE_GET_ACTIVE_BOX (me, fd))) {
	  /* The box is no longer active. */
	  continue;
	}

#ifdef MY_POLLNVAL
	if(PDB_CHECK_ERROR_EVENT(poll_fds[i], MY_POLLNVAL))
	{
	  struct pollfd fds;
	  int ret;
	  /* NOTE: /dev/poll returns POLLNVAL for closed descriptors. */
	  IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: POLLNVAL on %d\n",
			THR_NO, me->id, fd));
#ifdef PIKE_DEBUG
#ifdef HAVE_POLL
	  /* FIXME */

	  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);
#endif
	  /* Don't do anything further with this fd. */
	  continue;
#endif /* PIKE_DEBUG */
	}
#endif /* MY_POLLNVAL */

	check_box (box, fd);

	{
#ifdef PIKE_DEBUG
	  int handled = 0;
#endif /* PIKE_DEBUG */
	  if (PDB_CHECK_ERROR_EVENT(poll_fds[i], MY_POLLERR)) {
	    /* Errors are signalled on the first available callback. */
	    IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: POLLERR on %d\n",
			  THR_NO, me->id, fd));
	    box->revents |= PIKE_BIT_FD_ERROR;
	    /* Note that Linux pipe's signal close in the write-direction
	     * with POLLERR.
	     *
	     * FIXME: Signal on write-direction?
	     */
#ifdef BACKEND_USES_KQUEUE
	    /* kqueue signals errors as read or write events but
	     * with an additional error flag, so we must take care
	     * to not set any read/write bits if it's a sole error
	     * event. */
	    if (!poll_fds[i].data) goto next_fd;
#endif
	  }

	  if (PDB_CHECK_ERROR_EVENT(poll_fds[i], MY_POLLHUP)) {
	    IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: POLLHUP on %d\n",
			  THR_NO, me->id, fd));
	    /* Linux signals close in the read-direction of pipes
	     * and fifos with POLLHUP. */
	    box->revents |= PIKE_BIT_FD_READ|PIKE_BIT_FD_READ_OOB;
	    /* For historical reasons we also signal on the write-drection. */
	    box->revents |= PIKE_BIT_FD_WRITE|PIKE_BIT_FD_WRITE_OOB;
	    DO_IF_DEBUG(handled = 1);
	  }
	  if (PDB_CHECK_EVENT(poll_fds[i], MY_POLLRDBAND)
#ifdef BACKEND_USES_KQUEUE
	      && (!EV_OOBAND || PDB_CHECK_ERROR_EVENT(poll_fds[i], EV_OOBAND))
#endif
	      ) {
	    IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: POLLRDBAND on %d\n",
			  THR_NO, me->id, fd));
	    box->revents |= PIKE_BIT_FD_READ_OOB;
	    DO_IF_DEBUG(handled = 1);
	  }
	  if (PDB_CHECK_EVENT(poll_fds[i], MY_POLLIN)) {
	    IF_PD(fprintf(stderr,
			  "[%d]BACKEND[%d]: POLLRDNORM|POLLIN on %d\n",
			  THR_NO, me->id, fd));
	    box->revents |= PIKE_BIT_FD_READ;
	    DO_IF_DEBUG(handled = 1);
	  }
	  if (PDB_CHECK_EVENT(poll_fds[i], MY_POLLWRBAND)) {
	    IF_PD(fprintf(stderr,
			  "[%d]BACKEND[%d]: POLLWRBAND on %d\n",
			  THR_NO, me->id, fd));
	    box->revents |= PIKE_BIT_FD_WRITE_OOB;
	    DO_IF_DEBUG(handled = 1);
	  }
	  if (PDB_CHECK_EVENT(poll_fds[i], MY_POLLOUT)) {
	    IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: POLLOUT on %d\n",
			  THR_NO, me->id, fd));
	    box->revents |= PIKE_BIT_FD_WRITE;
	    DO_IF_DEBUG(handled = 1);
	  }
#ifdef PIKE_DEBUG
	  if (!handled && PDB_GET_EVENTS(poll_fds[i])) {
	    fprintf(stderr, "[%d]BACKEND[%d]: fd %ld has revents 0x%08lx, "
		    "but hasn't been handled.\n", THR_NO, me->id,
		    (long)PDB_GET_FD(poll_fds[i]),
		    (long)PDB_GET_EVENTS(poll_fds[i]));
	    pdb_describe_event(me, poll_fds[i]);
	  }
#endif /* PIKE_DEBUG */
	}
	if (box->revents) {
	  if (!(box->revents & (box->events | PIKE_BIT_FD_ERROR))) {
	    /* Robustness paranoia; we've only received events that we
	     * aren't interested in. Unregister the unwanted events
	     * in case we are out of sync with the poll device.
	     * Otherwise we risk entering a busy loop.
	     */
	    IF_PD(fprintf(stderr,
			  "[%d]BACKEND[%d]: Backend is out of sync for fd %d\n"
			  "[%d]BACKEND[%d]: Wanted: 0x%04x Received: 0x%04x\n",
			  THR_NO, me->id, fd,
			  THR_NO, me->id, box->events, box->revents));
	    pdb_update_fd_set(me, pdb, fd, box->revents|box->events,
			      box->events);
	  }
	next_fd:
	  /* Hook in the box on the fd_list. */
	  if (!box->next) {
	    IF_PD(fprintf(stderr,
			  "[%d]BACKEND[%d]: hooking in box for fd %d\n",
			  THR_NO, me->id, fd));
	    box->next = fd_list.next;
	    fd_list.next = box;
	    if (box->ref_obj) add_ref(box->ref_obj);
	  } else {
	    IF_PD(fprintf(stderr,
			  "[%d]BACKEND[%d]: fd %d already in list.\n",
			  THR_NO, me->id, fd));
	  }
	}
      }

      /* Common code for all variants.
       *
       * Call callbacks for the active events.
       */
      if (backend_call_active_callbacks(&fd_list, me)) {
	CALL_AND_UNSET_ONERROR(free_fd_list);
	goto backend_round_done;
      }

      CALL_AND_UNSET_ONERROR(free_fd_list);

      /* 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 ENOTSOCK
      case ENOTSOCK:
#endif
#ifdef WSAENOTSOCK
      case WSAENOTSOCK:
#endif
      case EBADF:
	/* TODO: Fix poll version! */
	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, NULL);

  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);
    }

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

  /*! @decl float|int(0..0) `()(void|float|int(0..0) sleep_time)
   *!   Perform one pass through the backend.
   *!
   *!   Calls any outstanding call-outs and non-blocking I/O
   *!   callbacks that are registred in this backend object.
   *!
   *! @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.
   *!
   *! @seealso
   *!   @[Pike.DefaultBackend], @[main()]
   */
  PIKEFUN float|int(0..0) `()(void|float|int(0..0) sleep_time)
  {
    struct timeval timeout;	/* Got bogus gcc warning on timeout.tv_usec. */

    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 if (sleep_time && sleep_time->type == T_INT &&
	     sleep_time->u.integer) {
      SIMPLE_BAD_ARG_ERROR("`()", 1, "float|int(0..0)");
    }
    else
      timeout.tv_sec = -1;

    pdb_low_backend_once(THIS, &timeout);

    pop_n_elems (args);
    if (timeout.tv_sec < 0)
      push_int (0);
    else
      push_float (DO_NOT_WARN ((FLOAT_TYPE)
			       (DO_NOT_WARN ((double) timeout.tv_sec) +
				DO_NOT_WARN ((double) timeout.tv_usec) / 1e6)));
  }

  EXTRA
  {
    pdb_offset = Pike_compiler->new_program->inherits[1].storage_offset -
      Pike_compiler->new_program->inherits[0].storage_offset;

    /* /dev/poll and kqueue fds get invalidated at fork. */
    dmalloc_accept_leak(add_to_callback(&fork_child_callback,
					reopen_all_pdb_backends, NULL, NULL));
  }

  INIT
  {
    struct Backend_struct *me =
      THIS->backend = (struct Backend_struct *)(((char *)THIS) + pdb_offset);

    IF_PD (fprintf (stderr, "[%d]BACKEND[%d]: Registering device backend...\n",
		    THR_NO, me->id));

    me->update_fd_set_handler = (update_fd_set_handler_fn *) pdb_update_fd_set;
    me->handler_data = THIS;

    IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: Registering backend...\n",
		  THR_NO, me->id));
    register_pdb_backend(THIS);

    IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: Opening poll device...\n",
		  THR_NO, me->id));
    if ((THIS->set = OPEN_POLL_DEVICE()) < 0) {
      Pike_error("Failed to open poll device (errno:%d)\n", errno);
    }
    set_close_on_exec(THIS->set, 1);
  }
  
  EXIT
    gc_trivial;
  {
    struct Backend_struct *me = THIS->backend;

    IF_PD (fprintf (stderr, "[%d]BACKEND[%d]: Closing poll device...\n",
		    THR_NO, me->id));

    if (THIS->set >= 0)
      fd_close(THIS->set);
    unregister_pdb_backend(THIS);
  }
}

/*! @endclass
 */

#endif /* BACKEND_USES_POLL_DEVUCE || BACKEND_USES_KQUEUE */

#ifdef HAVE_POLL

/*! @class PollBackend
 *! @inherit __Backend
 *!
 *! @[Backend] implemented with @tt{poll(2)@} (SVr4, POSIX).
 *!
 *! @seealso
 *!   @[Backend]
 */
PIKECLASS PollBackend
{
  INHERIT Backend;

  /* Helpers to find the above inherit. */
  static ptrdiff_t pb_offset = 0;
  CVAR struct Backend_struct *backend;

  /* 
   * POLL/SELECT fd sets
   */
  CVAR struct pb_selectors set;
  CVAR struct pb_selectors active_set;

  DECLARE_STORAGE

  /*
   * FD set handling
   */

  static void pb_update_fd_set (struct Backend_struct *me,
				struct PollBackend_struct *pb, int fd,
				int old_events, int new_events)
  {
    int changed_events = old_events ^ new_events;

    IF_PD(fprintf (stderr, "[%d]BACKEND[%d]: pb_update_fd_set(.., %d, %d, %d):\n",
		   THR_NO, me->id, fd, old_events, new_events));
    

    if (changed_events) {

      if (changed_events & PIKE_BIT_FD_READ) {
	if (new_events & PIKE_BIT_FD_READ) {
	  pb_MY_FD_SET(&pb->set, fd, MY_POLLIN);
	}
	else {
	  pb_MY_FD_CLR(&pb->set, fd, MY_POLLIN);
	  if (new_events & PIKE_BIT_FD_READ_OOB)
	  {
	    pb_MY_FD_SET(&pb->set, fd, MY_POLLRDBAND);
	  }
	}
      }

      if (changed_events & PIKE_BIT_FD_READ_OOB) {
	if (new_events & PIKE_BIT_FD_READ_OOB)
	  pb_MY_FD_SET(&pb->set, fd, MY_POLLRDBAND);
	else {
	  if (!(new_events & PIKE_BIT_FD_READ)) {
	    pb_MY_FD_CLR(&pb->set, fd, MY_POLLRDBAND);
	  } else {
	    pb_MY_FD_CLR_RDBAND(&pb->set, fd);
	  }
	}
      }

      if (changed_events & PIKE_BIT_FD_WRITE) {
	if (new_events & PIKE_BIT_FD_WRITE) {
	  pb_MY_FD_SET(&pb->set, fd, MY_POLLOUT);
	}
	else {
	  if (!(new_events & PIKE_BIT_FD_WRITE_OOB)) {
	    pb_MY_FD_CLR(&pb->set, fd, MY_POLLOUT);
	  }
	}
      }

      if (changed_events & PIKE_BIT_FD_WRITE_OOB) {
	if (new_events & PIKE_BIT_FD_WRITE_OOB)
	  pb_MY_FD_SET(&pb->set, fd, MY_POLLWRBAND);
	else {
	  if (!(new_events & PIKE_BIT_FD_WRITE)) {
	    pb_MY_FD_CLR(&pb->set, fd, MY_POLLWREXCEPT);
	  } else {
	    pb_MY_FD_CLR_WRBAND(&pb->set, fd);
	  }
	}
      }

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

#ifdef PIKE_DEBUG

  static void pb_backend_do_debug(struct Backend_struct *me,
				  struct PollBackend_struct *pb)
    {
      int e;

      /* FIXME: OOB? */
      for(e=0;e<pb->set.num_in_poll;e++)
      {
	PIKE_STAT_T tmp;
	int ret;
	int fd = pb->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	/* PIKE_DEBUG */

  /* Mapping of events to flags and callbacks.
   *
   * Event	select	poll		kqueue			callback
   *
   * data_in	read	POLLIN		EVFILT_READ		READ
   *			POLLRDNORM	EVFILT_READ[EOF]      
   *
   * data_out	write	POLLOUT		EVFILT_WRITE		WRITE
   *			POLLWRNORM
   *
   * oob_in	except	POLLPRI		(EVFILT_READ)		READ_OOB
   *			POLLRDBAND
   *
   * oob_out	write	POLLWRBAND	(EVFILT_WRITE)		WRITE_OOB
   *
   * close_in	read	POLLIN		EVFILT_READ[EOF]	READ
   *
   * close_out	write	POLLHUP		EVFILT_WRITE[EOF]	WRITE
   *								>WRITE_OOB
   *
   * conn_ok	write	POLLOUT		EVFILT_WRITE		WRITE
   *
   * conn_fail	read	POLLIN		EVFILT_READ[EOF]	READ
   *		except						(READ_OOB)
   *
   * new_conn	read	POLLIN		EVFILT_READ		READ
   *
   * sock_err	except	POLLERR		EVFILT_READ[ERR]	ERROR
   *								>READ
   *
   * sock_err	except	POLLERR		EVFILT_WRITE[ERR]	ERROR
   *								>WRITE
   */


  /* 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. */
  static void pb_low_backend_once(struct PollBackend_struct *pb,
				  struct timeval *timeout)
  {
    ONERROR uwp;
    int i, done_something = 0;
    struct timeval start_time = *timeout;
    struct Backend_struct *me = pb->backend;
#ifdef DECLARE_POLL_EXTRAS
    /* Declare any extra variables needed by MY_POLL(). */
    DECLARE_POLL_EXTRAS;
#endif /* DECLARE_POLL_EXTRAS */

    low_backend_once_setup(pb->backend, &start_time);
    SET_ONERROR(uwp, low_backend_cleanup, THIS->backend);

    if (me->before_callback.type != T_INT)
      call_backend_monitor_cb (me, &me->before_callback);

    {
      int poll_timeout;
      struct timeval *next_timeout = &pb->backend->next_timeout;

      me->may_need_wakeup = 1;

      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;

      pb_copy_selectors(& pb->active_set, &pb->set);

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

      check_threads_etc();
      THREADS_ALLOW();

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

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

      THREADS_DISALLOW();
      check_threads_etc();
      me->may_need_wakeup = 0;
      GETTIMEOFDAY(&current_time);
    }

    if (me->after_callback.type != T_INT)
      call_backend_monitor_cb (me, &me->after_callback);

    if (!i) {
      /* Timeout */
    } else if (i>0) {
      int num_active = i;
      struct fd_callback_box fd_list = {
	NULL, NULL, NULL,
	-1, 0, 0,
	NULL,
      };
      struct fd_callback_box *box;
      ONERROR free_fd_list;

      fd_list.backend = me;
      fd_list.next = &fd_list;

      SET_ONERROR(free_fd_list, do_free_fd_list, &fd_list);

      done_something = 1;


      /* First clear revents for all the fds.
       *
       * FIXME: This is done for paranoia reasons. If all code that
       *        messes with fds clears revents, this isn't needed.
       *
       * Note: this needs to be a separate loop, since kqueue sends
       *       read and write in two separate events.
       */
      for(i=0; i<pb->active_set.num_in_poll; i++)
      {
	int fd = PB_GET_FD(pb->active_set.poll_fds[i]);
	box = SAFE_GET_ACTIVE_BOX (me, fd);
	if (box) {
	  check_box (box, fd);
	  box->revents = 0;
	}
      }

      /* Then flag the active events.
       */
      for(i=0; i<pb->active_set.num_in_poll; i++)
      {
	int fd = PB_GET_FD(pb->active_set.poll_fds[i]);

	if (!(box = SAFE_GET_ACTIVE_BOX (me, fd))) {
	  /* The box is no longer active. */
	  continue;
	}

#ifdef MY_POLLNVAL
	if(PB_CHECK_ERROR_EVENT(pb->active_set.poll_fds[i], MY_POLLNVAL))
	{
	  struct pollfd fds;
	  int ret;
	  /* NOTE: /dev/poll returns POLLNVAL for closed descriptors. */
	  IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: POLLNVAL on %d\n",
			THR_NO, me->id, fd));
#ifdef PIKE_DEBUG
	  /* FIXME */

	  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);
	  /* Don't do anything further with this fd. */
	  continue;
#endif /* PIKE_DEBUG */
	}
#endif /* MY_POLLNVAL */

	check_box (box, fd);

	{
#ifdef PIKE_DEBUG
	  int handled = 0;
#endif /* PIKE_DEBUG */
	  if (PB_CHECK_ERROR_EVENT(pb->active_set.poll_fds[i], MY_POLLERR)) {
	    /* Errors are signalled on the first available callback. */
	    IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: POLLERR on %d\n",
			  THR_NO, me->id, fd));
	    box->revents |= PIKE_BIT_FD_ERROR;
	  }

	  if (PB_CHECK_EVENT(pb->active_set.poll_fds[i], MY_POLLRDBAND)) {
	    IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: POLLRDBAND on %d\n",
			  THR_NO, me->id, fd));
	    box->revents |= PIKE_BIT_FD_READ_OOB;
	    DO_IF_DEBUG(handled = 1);
	  }
	  if (PB_CHECK_EVENT(pb->active_set.poll_fds[i], MY_POLLIN)) {
	    IF_PD(fprintf(stderr,
			  "[%d]BACKEND[%d]: POLLRDNORM|POLLIN on %d\n",
			  THR_NO, me->id, fd));
	    box->revents |= PIKE_BIT_FD_READ;
	    DO_IF_DEBUG(handled = 1);
	  }
	  if (PB_CHECK_EVENT(pb->active_set.poll_fds[i], MY_POLLWRBAND) ||
	      PB_CHECK_ERROR_EVENT(pb->active_set.poll_fds[i], MY_POLLHUP)) {
	    IF_PD(fprintf(stderr,
			  "[%d]BACKEND[%d]: POLLWRBAND|POLLHUP on %d\n",
			  THR_NO, me->id, fd));
	    box->revents |= PIKE_BIT_FD_WRITE_OOB;
	    DO_IF_DEBUG(handled = 1);
	  }
	  if (PB_CHECK_EVENT(pb->active_set.poll_fds[i], MY_POLLOUT) ||
	      PB_CHECK_ERROR_EVENT(pb->active_set.poll_fds[i], MY_POLLHUP)) {
	    IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: POLLOUT|POLLHUP on %d\n",
			  THR_NO, me->id, fd));
	    box->revents |= PIKE_BIT_FD_WRITE;
	    DO_IF_DEBUG(handled = 1);
	  }
#ifdef PIKE_DEBUG
	  if (!handled && PB_GET_EVENTS(pb->active_set.poll_fds[i])) {
	    fprintf(stderr, "[%d]BACKEND[%d]: fd %ld has revents 0x%08lx, "
		    "but hasn't been handled.\n", THR_NO, me->id,
		    (long)PB_GET_FD(pb->active_set.poll_fds[i]),
		    (long)PB_GET_EVENTS(pb->active_set.poll_fds[i]));
	    /* pdb_describe_event(me, pb->active_set.poll_fds[i]); */
	  }
#endif /* PIKE_DEBUG */
	}
	if (box->revents) {
	next_fd:
	  /* Hook in the box on the fd_list. */
	  if (!box->next) {
	    IF_PD(fprintf(stderr,
			  "[%d]BACKEND[%d]: hooking in box for fd %d\n",
			  THR_NO, me->id, fd));
	    box->next = fd_list.next;
	    fd_list.next = box;
	    if (box->ref_obj) add_ref(box->ref_obj);
	  } else {
	    IF_PD(fprintf(stderr,
			  "[%d]BACKEND[%d]: fd %d already in list.\n",
			  THR_NO, me->id, fd));
	  }
	}
      }

      /* Common code for all variants.
       *
       * Call callbacks for the active events.
       */
      if (backend_call_active_callbacks(&fd_list, me)) {
	CALL_AND_UNSET_ONERROR(free_fd_list);
	goto backend_round_done;
      }

      CALL_AND_UNSET_ONERROR(free_fd_list);

      /* 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 ENOTSOCK
      case ENOTSOCK:
#endif
#ifdef WSAENOTSOCK
      case WSAENOTSOCK:
#endif
      case EBADF:
	/* TODO: Fix poll version! */
	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, NULL);

  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);
    }

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

  /*! @decl float|int(0..0) `()(void|float|int(0..0) sleep_time)
   *!   Perform one pass through the backend.
   *!
   *!   Calls any outstanding call-outs and non-blocking I/O
   *!   callbacks that are registred in this backend object.
   *!
   *! @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.
   *!
   *! @seealso
   *!   @[Pike.DefaultBackend], @[main()]
   */
  PIKEFUN float|int(0..0) `()(void|float|int(0..0) sleep_time)
  {
    struct timeval timeout;	/* Got correct gcc warning on timeout.tv_usec. */

    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 if (sleep_time && sleep_time->type == T_INT &&
	     sleep_time->u.integer) {
      SIMPLE_BAD_ARG_ERROR("`()", 1, "float|int(0..0)");
    }
    else
    {
      timeout.tv_sec = -1;
      timeout.tv_usec = 0;
    }

    pb_low_backend_once(THIS, &timeout);

    pop_n_elems (args);
    if (timeout.tv_sec < 0)
      push_int (0);
    else
      push_float (DO_NOT_WARN ((FLOAT_TYPE)
			       (DO_NOT_WARN ((double) timeout.tv_sec) +
				DO_NOT_WARN ((double) timeout.tv_usec) / 1e6)));
  }

  EXTRA
  {
    pb_offset = Pike_compiler->new_program->inherits[1].storage_offset -
      Pike_compiler->new_program->inherits[0].storage_offset;

    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));
  }

  INIT
  {
    struct Backend_struct *me =
      THIS->backend = (struct Backend_struct *)(((char *)THIS) + pb_offset);

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

#ifdef PIKE_DEBUG
    me->debug_handler = (debug_handler_fn *) pb_backend_do_debug;
#endif
    me->update_fd_set_handler = (update_fd_set_handler_fn *) pb_update_fd_set;
    me->handler_data = THIS;

    THIS->set.poll_fds=0;
    THIS->set.poll_fd_size=0;
    THIS->set.num_in_poll=0;

    THIS->active_set.poll_fds=0;
    THIS->active_set.poll_fd_size=0;
    THIS->active_set.num_in_poll=0;
  }
  
  EXIT
    gc_trivial;
  {
    struct Backend_struct *me = THIS->backend;
    int e;

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

    if (THIS->set.poll_fds) {
      free(THIS->set.poll_fds);
      THIS->set.poll_fds = NULL;
      THIS->set.poll_fd_size = 0;
      THIS->set.num_in_poll = 0;
    }
    if (THIS->active_set.poll_fds) {
      free(THIS->active_set.poll_fds);
      THIS->active_set.poll_fds = NULL;
      THIS->active_set.poll_fd_size = 0;
      THIS->active_set.num_in_poll = 0;
    }
  }
}

/*! @endclass
 */

#endif /* HAVE_POLL */

/*! @class SelectBackend
 *! @inherit __Backend
 *!
 *! Backend based on the classic @tt{select(2)@} system call from BSD.
 */
PIKECLASS SelectBackend
{
  INHERIT Backend;

  /* Helpers to find the above inherit. */
  static ptrdiff_t sb_offset = 0;
  CVAR struct Backend_struct *backend;

  /* 
   * POLL/SELECT fd sets
   */
  CVAR struct sb_selectors set;
  CVAR struct sb_active_selectors active_set;

  DECLARE_STORAGE

  /*
   * FD set handling
   */

  static void sb_update_fd_set (struct Backend_struct *me,
				struct SelectBackend_struct *sb, int fd,
				int old_events, int new_events)
  {
    int changed_events = old_events ^ new_events;

    IF_PD(fprintf (stderr, "[%d]BACKEND[%d]: sb_update_fd_set(.., %d, %d, %d):\n",
		   THR_NO, me->id, fd, old_events, new_events));
    

    if (changed_events) {
      if (changed_events & PIKE_BIT_FD_READ) {
	if (new_events & PIKE_BIT_FD_READ) {
	  sb_MY_FD_SET(&sb->set, fd, MY_READSET);
	  /* Got to enable the exception set to get errors (at least
	   * according to POSIX). */
	  sb_MY_FD_SET(&sb->set, fd, MY_EXCEPTSET);
	}
	else {
	  sb_MY_FD_CLR(&sb->set, fd, MY_READSET);
	  if (!(new_events & PIKE_BIT_FD_READ_OOB) &&
	      !(new_events & PIKE_BIT_FD_WRITE))
	    /* Exceptions might cause calls to read, read_oob and write. */
	    sb_MY_FD_CLR(&sb->set, fd, MY_EXCEPTSET);
	}
      }

      if (changed_events & PIKE_BIT_FD_READ_OOB) {
	if (new_events & PIKE_BIT_FD_READ_OOB)
	  sb_MY_FD_SET(&sb->set, fd, MY_EXCEPTSET);
	else {
	  if (!(new_events & PIKE_BIT_FD_READ)) {
	    if (!(new_events & PIKE_BIT_FD_WRITE))
	      /* Exceptions might cause calls to read, read_oob and write. */
	      sb_MY_FD_CLR(&sb->set, fd, MY_EXCEPTSET);
	  }
	}
      }

      if (changed_events & PIKE_BIT_FD_WRITE) {
	if (new_events & PIKE_BIT_FD_WRITE) {
	  sb_MY_FD_SET(&sb->set, fd, MY_WRITESET);
	  /* Got to enable the exception set to get errors (at least
	   * according to POSIX). */
	  sb_MY_FD_SET(&sb->set, fd, MY_EXCEPTSET);
	}
	else {
	  if (!(new_events & PIKE_BIT_FD_WRITE_OOB)) {
	    sb_MY_FD_CLR(&sb->set, fd, MY_WRITESET);
	    if (!(new_events & PIKE_BIT_FD_READ) &&
		!(new_events & PIKE_BIT_FD_READ_OOB))
	      /* Exceptions might cause calls to read, read_oob and write. */
	      sb_MY_FD_CLR(&sb->set, fd, MY_EXCEPTSET);
	  }
	}
      }

      if (changed_events & PIKE_BIT_FD_WRITE_OOB) {
	if (new_events & PIKE_BIT_FD_WRITE_OOB)
	  sb_MY_FD_SET(&sb->set, fd, MY_WRITESET);
	else {
	  if (!(new_events & PIKE_BIT_FD_WRITE)) {
	    sb_MY_FD_CLR(&sb->set, fd, MY_WRITESET);
	  }
	}
      }

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

#ifdef PIKE_DEBUG

  static void sb_backend_do_debug(struct Backend_struct *me,
				  struct SelectBackend_struct *sb)
    {
      int e;
      PIKE_STAT_T tmp;

      /* FIXME: OOB? */
      for(e=0;e<=sb->set.max_fd;e++)
      {
	if(my_FD_ISSET(e, sb->set.sets + MY_READSET)
	   || my_FD_ISSET(e, sb->set.sets + MY_WRITESET)
	   || my_FD_ISSET(e, sb->set.sets + MY_EXCEPTSET)
	  )
	{
	  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;
	    }
	  }
	}
      }
    }

#endif	/* PIKE_DEBUG */

  /* Mapping of events to flags and callbacks.
   *
   * Event	select	poll		kqueue			callback
   *
   * data_in	read	POLLIN		EVFILT_READ		READ
   *			POLLRDNORM	EVFILT_READ[EOF]      
   *
   * data_out	write	POLLOUT		EVFILT_WRITE		WRITE
   *			POLLWRNORM
   *
   * oob_in	except	POLLPRI		(EVFILT_READ)		READ_OOB
   *			POLLRDBAND
   *
   * oob_out	write	POLLWRBAND	(EVFILT_WRITE)		WRITE_OOB
   *
   * close_in	read	POLLIN		EVFILT_READ[EOF]	READ
   *
   * close_out	write	POLLHUP		EVFILT_WRITE[EOF]	WRITE
   *								>WRITE_OOB
   *
   * conn_ok	write	POLLOUT		EVFILT_WRITE		WRITE
   *
   * conn_fail	read	POLLIN		EVFILT_READ[EOF]	READ
   *		except						(READ_OOB)
   *
   * new_conn	read	POLLIN		EVFILT_READ		READ
   *
   * sock_err	except	POLLERR		EVFILT_READ[ERR]	ERROR
   *								>READ
   *
   * sock_err	except	POLLERR		EVFILT_WRITE[ERR]	ERROR
   *								>WRITE
   */


  /* 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. */
  static void sb_low_backend_once(struct SelectBackend_struct *sb,
				  struct timeval *timeout)
  {
    ONERROR uwp;
    int i, done_something = 0;
    struct timeval start_time = *timeout;
    struct Backend_struct *me = sb->backend;
#ifdef DECLARE_POLL_EXTRAS
    /* Declare any extra variables needed by MY_POLL(). */
    DECLARE_POLL_EXTRAS;
#endif /* DECLARE_POLL_EXTRAS */

    low_backend_once_setup(sb->backend, &start_time);
    SET_ONERROR(uwp, low_backend_cleanup, THIS->backend);

    if (me->before_callback.type != T_INT)
      call_backend_monitor_cb (me, &me->before_callback);

    {
      struct timeval poll_timeout;
      struct timeval *next_timeout = &sb->backend->next_timeout;

      me->may_need_wakeup = 1;

      poll_timeout = *next_timeout;

      sb_copy_selectors(& sb->active_set, &sb->set);

      IF_PD(fprintf (stderr, "[%d]BACKEND[%d]: Doing poll on fds:\n",
		     THR_NO, me->id));

      check_threads_etc();
      THREADS_ALLOW();

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

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

      THREADS_DISALLOW();
      check_threads_etc();
      me->may_need_wakeup = 0;
      GETTIMEOFDAY(&current_time);
    }

    if (me->after_callback.type != T_INT)
      call_backend_monitor_cb (me, &me->after_callback);

    if (!i) {
      /* Timeout */
    } else if (i>0) {
      int num_active = i;
      struct fd_callback_box fd_list = {
	me, NULL, &fd_list,
	-1, 0, 0,
	NULL,
      };
      struct fd_callback_box *box;
      ONERROR free_fd_list;

      SET_ONERROR(free_fd_list, do_free_fd_list, &fd_list);

      done_something = 1;

      for(i=0; i <= sb->active_set.max_fd; i++)
      {
	box = SAFE_GET_ACTIVE_BOX(me, i);
	if (!box) continue;
	check_box(box, i);

	box->revents = 0;

	if(fd_FD_ISSET(i, sb->active_set.asets + MY_EXCEPTSET)) {
	  /* 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? */
	  int err = 0;
	  ACCEPT_SIZE_T len = sizeof (err);
	  IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: exception on %d\n",
			THR_NO, me->id, i));
	  if (!getsockopt (i, SOL_SOCKET, SO_ERROR, (void *)&err, &len) &&
	      err) {
	    IF_PD (fprintf (stderr, "[%d]BACKEND[%d]: error on %d, error=%d\n",
			    THR_NO, me->id, i, err));
	    box->revents |= PIKE_BIT_FD_ERROR;
	  } else {
	    box->revents |= PIKE_BIT_FD_READ_OOB;
	  }
	}
	  
	if(fd_FD_ISSET(i, sb->active_set.asets + MY_READSET)) {
	  IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: read on %d\n",
			THR_NO, me->id, i));
	  box->revents |= PIKE_BIT_FD_READ;
	}
	  
	if(fd_FD_ISSET(i, sb->active_set.asets + MY_WRITESET)) {
	  IF_PD(fprintf(stderr, "[%d]BACKEND[%d]: write on %d\n",
			THR_NO, me->id, i));
	  /* 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. */
	  box->revents |= PIKE_BIT_FD_WRITE | PIKE_BIT_FD_WRITE_OOB;
	}

	if (box->revents) {
	  /* Hook in the box on the fd_list. */
	  if (!box->next) {
	    box->next = fd_list.next;
	    fd_list.next = box;
	    if (box->ref_obj) add_ref(box->ref_obj);
	  }
	}
      }

      /* Common code for all variants.
       *
       * Call callbacks for the active events.
       */
      if (backend_call_active_callbacks(&fd_list, me)) {
	CALL_AND_UNSET_ONERROR(free_fd_list);
	goto backend_round_done;
      }

      CALL_AND_UNSET_ONERROR(free_fd_list);

      /* 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 ENOTSOCK
      case ENOTSOCK:
#endif
#ifdef WSAENOTSOCK
      case WSAENOTSOCK:
#endif
      case EBADF:
	/* TODO: Fix poll version! */

	sb_copy_selectors(&sb->active_set, &sb->set);

	timeout->tv_usec=0;
	timeout->tv_sec=0;
	if(SB_SELECT(sb->active_set, *timeout) < 0)
	{
	  switch(errno)
	  {
#ifdef WSAEBADF
	  case WSAEBADF:
#endif
#ifdef ENOTSOCK
	  case ENOTSOCK:
#endif
#ifdef WSAENOTSOCK
	  case WSAENOTSOCK:
#endif
	  case EBADF:
	    {
	      FOR_EACH_ACTIVE_FD_BOX (me, box) {
		fd_FD_ZERO(sb->active_set.asets + MY_READSET);
		fd_FD_ZERO(sb->active_set.asets + MY_WRITESET);
		fd_FD_ZERO(sb->active_set.asets + MY_EXCEPTSET);
		    
		if(my_FD_ISSET(box->fd, sb->set.sets + MY_READSET))
		  fd_FD_SET(box->fd, sb->active_set.asets + MY_READSET);
		if(my_FD_ISSET(box->fd, sb->set.sets + MY_WRITESET))
		  fd_FD_SET(box->fd, sb->active_set.asets + MY_WRITESET);
		if(my_FD_ISSET(box->fd, sb->set.sets + MY_EXCEPTSET))
		  fd_FD_SET(box->fd, sb->active_set.asets + MY_EXCEPTSET);
		    
		timeout->tv_usec=0;
		timeout->tv_sec=0;
		    
		if(SB_SELECT(sb->active_set, *timeout) < 0)
		{
		  switch(errno)
		  {
#ifdef __NT__
		  default:
#endif
		  case EBADF:
#ifdef WSAEBADF
		  case WSAEBADF:
#endif
#ifdef ENOTSOCK
		  case ENOTSOCK:
#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 */
	}
	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, NULL);

  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);
    }

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

  /*! @decl float|int(0..0) `()(void|float|int(0..0) sleep_time)
   *!   Perform one pass through the backend.
   *!
   *!   Calls any outstanding call-outs and non-blocking I/O
   *!   callbacks that are registred in this backend object.
   *!
   *! @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.
   *!
   *! @seealso
   *!   @[Pike.DefaultBackend], @[main()]
   */
  PIKEFUN float|int(0..0) `()(void|float|int(0..0) sleep_time)
  {
    struct timeval timeout;	/* Got bogus gcc warning on timeout.tv_usec. */

    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 if (sleep_time && sleep_time->type == T_INT &&
	     sleep_time->u.integer) {
      SIMPLE_BAD_ARG_ERROR("`()", 1, "float|int(0..0)");
    }
    else
      timeout.tv_sec = -1;

    sb_low_backend_once(THIS, &timeout);

    pop_n_elems (args);
    if (timeout.tv_sec < 0)
      push_int (0);
    else
      push_float (DO_NOT_WARN ((FLOAT_TYPE)
			       (DO_NOT_WARN ((double) timeout.tv_sec) +
				DO_NOT_WARN ((double) timeout.tv_usec) / 1e6)));
  }

  EXTRA
  {
    sb_offset = Pike_compiler->new_program->inherits[1].storage_offset -
      Pike_compiler->new_program->inherits[0].storage_offset;
  }

  INIT
  {
    struct Backend_struct *me =
      THIS->backend = (struct Backend_struct *)(((char *)THIS) + sb_offset);

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

#ifdef PIKE_DEBUG
    me->debug_handler = (debug_handler_fn *) sb_backend_do_debug;
#endif
    me->update_fd_set_handler = (update_fd_set_handler_fn *) sb_update_fd_set;
    me->handler_data = THIS;

    THIS->set.max_fd=0;
    my_FD_ZERO(THIS->set.sets + MY_READSET);
    my_FD_ZERO(THIS->set.sets + MY_WRITESET);
    my_FD_ZERO(THIS->set.sets + MY_EXCEPTSET);
    /* FIXME: Should there be something else here? */
    /* me->set.num_fds=0; */
  }
  
  EXIT
    gc_trivial;
  {
    struct Backend_struct *me = THIS->backend;
    IF_PD (fprintf (stderr, "[%d]BACKEND[%d]: exit generic backend\n",
		    THR_NO, me->id));
  }
}

/*! @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", THR_NO,
		      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", THR_NO,
		      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", THR_NO,
		      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", THR_NO,
		      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", \
		    THR_NO, 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 */

static struct callback *mem_callback;

void init_backend(void)
{
  IF_CO(fprintf(stderr, "BACKEND: Init call_out...\n"));
  init_call_out_s_blocks();
  IF_PD(fprintf(stderr, "BACKEND: Init compat callback boxes...\n"));
  init_compat_cb_box_blocks();
  IF_PD(fprintf(stderr, "BACKEND: INIT...\n"));
  INIT;
  IF_PD(fprintf(stderr, "BACKEND: Creating default backend...\n"));
  {
    /* Select something suitable. */
#ifdef OPEN_POLL_DEVICE
    /* Note that creation of a poll device backend may fail. */
    JMP_BUF recovery;
    if (SETJMP(recovery)) {
#ifdef HAVE_POLL
      default_backend_obj = clone_object(PollBackend_program, 0);
#else
      default_backend_obj = clone_object(SelectBackend_program, 0);
#endif
    } else {
      default_backend_obj = clone_object(PollDeviceBackend_program, 0);
    }
    UNSETJMP(recovery);
#elif defined(HAVE_POLL)
    default_backend_obj = clone_object(PollBackend_program, 0);
#else
    default_backend_obj = clone_object(SelectBackend_program, 0);
#endif
    default_backend = (struct Backend_struct *)
      get_storage(default_backend_obj, Backend_program);

    mem_callback=add_memory_usage_callback(count_memory_in_call_outs,0,0);
    
    add_object_constant("__backend", default_backend_obj, 0);
    add_program_constant("DefaultBackendClass", default_backend_obj->prog, 0);
  }
}

#ifdef DO_PIKE_CLEANUP
void exit_backend(void)
{
  /* Note: The mem_callback has already been freed
   *       by exit_builtin_efuns() at this point.
   */
  /* if (mem_callback) remove_callback(mem_callback); */
  free_object(default_backend_obj);
  default_backend = 0;
  EXIT;
}

/* Note: This is called when the last backend object exits, which might be
 * after exit_backend if there's garbage. */
static void backend_cleanup()
{
#ifdef OPEN_POLL_DEVICE
  if (pdb_backends) {
    free(pdb_backends);
    num_pdb_backends = 0;
  }
#endif /* OPEN_POLL_DEVICE */
  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
