Moving main loop to GLib.




Moving the main loop to GLIB is something that has been
discussed for a long time.  The following is based on
discussions I had with Tim Janik a month or two ago. I may
have forgotten some things we agreed upon; if so, he can
yell at me.

Moving the main loop to GLIB is desirable, because it enables
non-GUI applications to take advantage of a good event
loop and of event driven programming. In particular, it
would be desirable to use the main loop for ORBit.

The main reason why this operation is not simply copying
code is that the X events from GDK need to be integrated
into the event loop. To facilitate this, the proposed
GLIB interface is meant to be extensible. The core
code polls and dispatches events from various "event sources".


To get to the interface. The interfaces for handling
input functions, idle functions should look extremely
familiar. They are just the interfaces from GTK+ and
GDK, regularized a bit:

 guint g_timeout_add_full (guint          priority,
			   guint32        interval, 
	  	           GFunc          function,
			   gpointer       data,
			   GDestroyNotify notify);
 guint g_timeout_add      (guint32        interval,
			   GFunc      function,
			   gpointer       data);
 void g_timeout_remove         (guint          tag);
 void g_timeout_remove_by_data (gpointer       data);
 
 guint	   g_idle_add		 (GFunc	               function,
				  gpointer	       data);
 guint	   g_idle_add_full       (gint   	       priority,
				  GFunc	               function,
				  gpointer	       data,
				  GDestroyNotify       destroy);
 void	   g_idle_remove	 (guint	               tag);
 void	   g_idle_remove_by_data (gpointer	       data);

typedef guint (*GInputFunction) (gpointer	    data,
				 gint		    source,
				 GInputCondition  condition);
typedef enum
{
  GDK_INPUT_READ       = 1 << 0,
  GDK_INPUT_WRITE      = 1 << 1,
  GDK_INPUT_EXCEPTION  = 1 << 2
} GInputCondition;

 guint     g_input_add_full (gint                 priority,
			     gint		  source,
			     GInputCondition      condition,
			     GInputFunction       function,
			     gpointer	          data,
			     GDestroyNotify       destroy);
 gint      g_input_add	    (gint		  source,
			     GInputCondition      condition,
			     GInputFunction       function,
			     gpointer	          data);
 void      g_input_remove       (guint		  tag);
 void      g_input_remove_by_id (guint		  tag);

Note that GInputFunction now returns a gboolean, like the
other functions. gdk_input_add() can use wrapper functions
to preserve the old system of void returns.

To run the main loop, we use the following interface.
The main difference for this is that run() and quit()
are done for a specific GMainLoop *, instead of 
for the current main loop. This avoids the problem
one occasionally sees where one has a main window and
a dialog up, one quits the main window, and the dialog
stays up until you quit it.

 /* Create a new "main loop".
  */
 GMainLoop *g_main_new ();

 /* Run until a paired g_main_quit() */
 void g_main_run (GMainLoop *loop);
 void g_main_quit (GMainLoop *loop);

 /* Destroy a GMainLoop. (Do we need to refcount these?) */
 void g_main_destroy (GMainLoop *loop);

 /* Run a single iteration of the mainloop. If block is FALSE,
  * will never block
  */
 void g_main_iteration (gboolean block);

 /* See if any events are pending
  */
 gboolean g_main_pending ();

Now, on to the low level part. The fundemental structure is a
GSource:

/* An event source */
struct _GSource {
  gint     priority;
  
  GSList *poll_fds;
  guint timeout;

  /* Get read for a poll */
  gboolean (*prepare)   (GSource *source);
  /* Check (after a poll) if any callbacks need dispatching */
  gboolean (*check)     (GSource *source);
  /* Dispatch any pending callbacks */
  void     (*dispatch)  (GSource *source);
  /* Continue dispatching events after a recursive call to g_main_run() */
  void     (*restart)   (GSource *source);

  /* private data for GLib. Initialize to NULL */
  GSourcePrivate *private;
}

poll_fds is a GSList, of essentially, struct poll_fd. 
We provide definitions for the important parts of GPoll
to handle systems that don't support this.

typedef enum {
  G_POLL_IN  = POLLIN,
  G_POLL_OUT = POLLOUT,
  G_POLL_PRI  = POLLPRI,
  G_POLL_ERR = POLLERR,
  G_POLL_HUP = POLLHUP,
} GPollFlags;

struct _GPollFD {
   gint fd;
   gushort events;
   gushort revents;
};

The algorithm is, for each run of g_main_iteration(), we:

1) While the list of currently pending sources is non-empty
   call (*restart) on the head, and (*dispatch) on the rest,
   removing sources from the list after each returns

2) Call (*prepare) for each source.

3) Poll with the union of the pollfd's from each source,
   and the minimum of all the timeouts from all sources.
   (timeout < 0 == infinite)

4) For each source, if (*check) returns true, add the
   source to the pending list. Once one source returns
   true, stop after checking all sources at that priority.

5) While the list of currently pending sources is non-empty,
   call (*dispatch) on each source, removing the source
   after the call.


Comments are appreciated.

Regards,
                                        Owen



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]