/*
    GSK - a library to write servers
    Copyright (C) 1999-2000 Dave Benson

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA

    Contact:
        daveb@ffem.org <Dave Benson>

*/

/* GskMainLoopPollBase: A main loop based around a system call like poll(2)
 *    or select(2) that calls a `poll' function with a collection of file
 *    descriptors.  This class implements signal handling and process-end
 *    notification using the normal unix EINTR mechanism.
 */

/* NOTE: everything here is private, except GskMainLoopPollBaseClass,
 *       which is protected. (this exposition is to permit derivation)
 */
 

#ifndef __GSK_MAIN_LOOP_POLL_BASE_H_
#define __GSK_MAIN_LOOP_POLL_BASE_H_


/* This is a derived class of GskMainLoop that uses
 * poll(2) or select(2) or similar internally.
 */

/* Note: when deriving from this class,
 *       note that its finalize method will call `config_fd(0)'
 *       perhaps, so should chain the finalize method *first*
 */

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

#include "gskmainloop.h"

/* --- Type macros --- */
GtkType gsk_main_loop_poll_base_get_type();
#define GSK_TYPE_MAIN_LOOP_POLL_BASE		(gsk_main_loop_poll_base_get_type ())
#define GSK_MAIN_LOOP_POLL_BASE(obj)              (GTK_CHECK_CAST ((obj), GSK_TYPE_MAIN_LOOP_POLL_BASE, GskMainLoopPollBase))
#define GSK_MAIN_LOOP_POLL_BASE_CLASS(klass)      (GTK_CHECK_CLASS_CAST ((klass), GSK_TYPE_MAIN_LOOP_POLL_BASE, GskMainLoopPollBaseClass))
#define GSK_MAIN_LOOP_POLL_BASE_GET_CLASS(obj)    (GSK_MAIN_LOOP_POLL_BASE_CLASS(GTK_OBJECT(obj)->klass))
#define GSK_IS_MAIN_LOOP_POLL_BASE(obj)           (GTK_CHECK_TYPE ((obj), GSK_TYPE_MAIN_LOOP_POLL_BASE))
#define GSK_IS_MAIN_LOOP_POLL_BASE_CLASS(klass)   (GTK_CHECK_CLASS_TYPE ((klass), GSK_TYPE_MAIN_LOOP_POLL_BASE))

/* --- Typedefs & structures --- */
typedef struct _GskMainLoopPollBase GskMainLoopPollBase;
typedef struct _GskMainLoopPollBaseClass GskMainLoopPollBaseClass;


/*< private >*/
typedef struct _GskPollNodeAny GskPollNodeAny;

#include <setjmp.h>			/* XXX: for sigjmp_buf */
#include <signal.h>			/* XXX: for sigset_t */

/* --- Various internal node types ---
 *
 * They are all cast to GskSource *, so they must be identifyable.
 */

typedef enum _GskPollNodeType
{
  GSK_POLL_NODE_TYPE_IDLE,
  GSK_POLL_NODE_TYPE_TIMER,
  GSK_POLL_NODE_TYPE_IO,
  GSK_POLL_NODE_TYPE_SIGNAL,
  GSK_POLL_NODE_TYPE_PROCESS
} GskPollNodeType;

/* The ``base'' class.  We derive by simply duplicating these
 * members in each node type.
 */
struct _GskPollNodeAny
{
  GskPollNodeType        node_type;
  int                    ref_count;
  gboolean               is_destroyed;
  gpointer               user_data;
  GDestroyNotify         destroy;
};

/* File descriptors status.  */
typedef struct _GskPollFdInfo GskPollFdInfo;
struct _GskPollFdInfo
{
  /* base class */
  GskPollNodeType        type;
  int                    ref_count;
  gboolean               is_destroyed;
  gpointer               user_data;
  GDestroyNotify         destroy;

  /* event information */
  int                    fd;
  GIOCondition           condition;

  /* User-associated hooks. */
  GskMainLoopIOFunc      io_func;
};

/* Idle list node */
typedef struct _GskPollIdleList GskPollIdleList;
struct _GskPollIdleList
{
  /* base class */
  GskPollNodeType        type;
  int                    ref_count;
  gboolean               is_destroyed;
  gpointer               user_data;
  GDestroyNotify         destroy;

  /* Idle function information */
  GskMainLoopIdleFunc    idle_func;
  GskPollIdleList       *prev;
  GskPollIdleList       *next;
};

/* Timeout nodes */
typedef struct _GskPollTimeoutNode GskPollTimeoutNode;
struct _GskPollTimeoutNode
{
  /* base class */
  GskPollNodeType        type;
  int                    ref_count;
  gboolean               is_destroyed;
  gpointer               user_data;
  GDestroyNotify         destroy;

  /* timeout specific information */
  GTimeVal               expire_time;
  int                    timeout_count;
  int                    milli_period;
  GskMainLoopTimeoutFunc timeout_func;
};

/* Process termination nodes. */
typedef struct _GskPollProcessNode GskPollProcessNode;
struct _GskPollProcessNode
{
  /* base class */
  GskPollNodeType        type;
  int                    ref_count;
  gboolean               is_destroyed;
  gpointer               user_data;
  GDestroyNotify         destroy;

  int                    process_id;
  GskMainLoopWaitPidFunc waitpid_func;
  GskPollProcessNode    *prev;
  GskPollProcessNode    *next;
};

/* Signal notification list. */
typedef struct _GskPollSignalList GskPollSignalList;
struct _GskPollSignalList
{
  /* base class */
  GskPollNodeType        type;
  int                    ref_count;
  gboolean               is_destroyed;
  gpointer               user_data;
  GDestroyNotify         destroy;

  /* signal-specific information */
  int                    signal_number;
  GskMainLoopSignalFunc  signal_func;
  GskPollSignalList     *prev;
  GskPollSignalList     *next;
};
  
/* Generic poll node union. */
typedef union _GskPollNodeGeneric GskPollNodeGeneric;
union _GskPollNodeGeneric
{
  GskPollNodeType        node_type;
  GskPollNodeAny         any;
  GskPollFdInfo          fd_info;
  GskPollTimeoutNode     timeout_node;
  GskPollIdleList        idle_list;
  GskPollSignalList      signal_list;
  GskPollProcessNode     process_node;
};


/* --- Helper class: contains one result from the polling system call. --- */
typedef struct _GskPollResult GskPollResult;
struct _GskPollResult
{
  int                  fd;
  GIOCondition         condition;
};

/*< protected >*/

/* --- GskMainLoopPollBase structures --- */
struct _GskMainLoopPollBaseClass 
{
  GskMainLoopClass    main_loop_class;

  void              (*config_fd)       (GskMainLoopPollBase   *main_loop,
                                        int                    fd,
				        GIOCondition           io_conditions);

  void              (*setup_do_polling)(GskMainLoopPollBase   *main_loop);

  /* returns FALSE if the poll function has an error.
   *
   * It is an unfortunate fact of life that this function may
   * be interrupted by a signal at any time -- so don't do memory
   * allocation or ever fail to maintain the invariants on your structure,
   * or otherwise forget cancellation -- do that stuff in `setup_do_polling',
   * which cannot be cancelled.
   */
  gboolean          (*do_polling)      (GskMainLoopPollBase   *main_loop,
				        int                    max_timeout,
				        guint                  max_results,
				        guint                 *num_results_out,
                                        GskPollResult         *results);
};
struct _GskMainLoopPollBase
{
  GskMainLoop  	   main_loop;

  /* Hash table from file descriptor to GskPollFdInfo */
  GHashTable      *fd_to_fd_info;

  /* Tree of timeouts. */
  GTree           *timeout_tree;

  /* List of idle functions; kept in the order they were added in. */
  GskPollIdleList *first_idle;
  GskPollIdleList *last_idle;

  /* Map from signal number to the head of a list of handlers for that
   * signal. */
  GHashTable      *signal_no_to_list;

  /* whether we have trapped SIGCHLD. */
  unsigned         have_wait_signal_handler : 1;

  /* the got_{wait,other}_signals flags and bits in `signals_raised'
   * are the only data touched by the actual system signal handlers.
   */
  unsigned         got_wait_signal : 1;
  unsigned         got_other_signals : 1;

  guint            num_signal_bytes;
  guint8          *signals_raised;

  /* Map from process id to the GskPollProcessNode */
  GHashTable      *pid_to_process_node;

  /* Finally, we have to have these guys around literally.
   * Someday we may want a private struct for them,
   * or something, but for now, just include the seminonportable
   * magic and continue.
   */
  sigjmp_buf       jmp_buf_env;
  sigset_t         signal_set;

  guint            source_count;
};

/* derived classes should call this first in their finalize
 * methods, and then clean up their data, and then
 * chain to their parent.
 */
void gsk_main_loop_poll_base_prefinalize (GskMainLoopPollBase *poll_base);

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif
