Major change in API for creating sources to handle multiple main loops
authorOwen Taylor <otaylor@redhat.com>
Tue, 5 Dec 2000 20:45:33 +0000 (20:45 +0000)
committerOwen Taylor <otaylor@src.gnome.org>
Tue, 5 Dec 2000 20:45:33 +0000 (20:45 +0000)
Tue Dec  5 12:23:04 2000  Owen Taylor  <otaylor@redhat.com>

        * gmain.[hc]: Major change in API for creating sources
to handle multiple main loops (GMainContext *).

GSources are now exposed as GSource * and implemented
with structure derivation.

* giochannel.[ch]: Changed vtable for GIOChannel to correspond
to the new mainloop API, add g_io_channel_create_watch().

* gtypes.h: Move GTimeVal here.

* gthread.h: Remove gmain.h include to avoid circularity.

        * giounix.c: Update for new GMain API.

* giowin32.c: Update for new GMain API. (No check for
proper compilation or working.)

* timeloop.c timeloop-basic.c: A benchmarking program for
the main loop comparing the main loop against a
hand-written (timeloop-basic.c) variant.

* tests/mainloop-test.c: New torture test of mainloop.

* docs/Changes-2.0.txt: Started. Added text about
changes to GMain.

* gmain.c (g_main_add_poll_unlocked): Initial fd->revents
to zero. (#8482, Benjamin Kahn)

37 files changed:
.cvsignore
ChangeLog
ChangeLog.pre-2-0
ChangeLog.pre-2-10
ChangeLog.pre-2-12
ChangeLog.pre-2-2
ChangeLog.pre-2-4
ChangeLog.pre-2-6
ChangeLog.pre-2-8
Makefile.am
docs/Changes-2.0.txt [new file with mode: 0644]
gfileutils.c
giochannel.c
giochannel.h
giounix.c
giowin32.c
glib/Makefile.am
glib/gfileutils.c
glib/giochannel.c
glib/giochannel.h
glib/giounix.c
glib/giowin32.c
glib/gmain.c
glib/gmain.h
glib/gthread.h
glib/gtypes.h
gmain.c
gmain.h
gthread.h
gtypes.h
tests/.cvsignore
tests/Makefile.am
tests/mainloop-test.c [new file with mode: 0644]
tests/timeloop-basic.c [new file with mode: 0644]
tests/timeloop.c [new file with mode: 0644]
timeloop-basic.c [new file with mode: 0644]
timeloop.c [new file with mode: 0644]

index 34f2b18838c4444edfb7ff91bfd0a27b6b13e8bc..f08502b7a78e8e3ed9aaa636d81ca36a454d41c3 100644 (file)
@@ -28,6 +28,8 @@ stamp-h.in
 testgdate
 testgdateparser
 testglib
+timeloop
+timeloop-basic
 annotations
 logs
 glib.rc
index 41e6843f5fa906f2e21c4b6458787693ae1a9b5e..92184925f8206de8510b4a2d72e7aa654be6c3ae 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,35 @@
+Tue Dec  5 12:23:04 2000  Owen Taylor  <otaylor@redhat.com>
+
+        * gmain.[hc]: Major change in API for creating sources
+       to handle multiple main loops (GMainContext *). 
+
+       GSources are now exposed as GSource * and implemented
+       with structure derivation.
+
+       * giochannel.[ch]: Changed vtable for GIOChannel to correspond
+       to the new mainloop API, add g_io_channel_create_watch().
+        
+       * gtypes.h: Move GTimeVal here.
+
+       * gthread.h: Remove gmain.h include to avoid circularity.
+       
+        * giounix.c: Update for new GMain API.
+        
+       * giowin32.c: Update for new GMain API. (No check for
+       proper compilation or working.)
+
+       * timeloop.c timeloop-basic.c: A benchmarking program for
+       the main loop comparing the main loop against a 
+       hand-written (timeloop-basic.c) variant. 
+
+       * tests/mainloop-test.c: New torture test of mainloop.
+       
+       * docs/Changes-2.0.txt: Started. Added text about
+       changes to GMain.
+
+       * gmain.c (g_main_add_poll_unlocked): Initial fd->revents
+       to zero. (#8482, Benjamin Kahn)
+
 2000-12-01  Tor Lillqvist  <tml@iki.fi>
 
        * {.,*}/makefile.msc.in: Include make.msc from GLib's build subdir.
index 41e6843f5fa906f2e21c4b6458787693ae1a9b5e..92184925f8206de8510b4a2d72e7aa654be6c3ae 100644 (file)
@@ -1,3 +1,35 @@
+Tue Dec  5 12:23:04 2000  Owen Taylor  <otaylor@redhat.com>
+
+        * gmain.[hc]: Major change in API for creating sources
+       to handle multiple main loops (GMainContext *). 
+
+       GSources are now exposed as GSource * and implemented
+       with structure derivation.
+
+       * giochannel.[ch]: Changed vtable for GIOChannel to correspond
+       to the new mainloop API, add g_io_channel_create_watch().
+        
+       * gtypes.h: Move GTimeVal here.
+
+       * gthread.h: Remove gmain.h include to avoid circularity.
+       
+        * giounix.c: Update for new GMain API.
+        
+       * giowin32.c: Update for new GMain API. (No check for
+       proper compilation or working.)
+
+       * timeloop.c timeloop-basic.c: A benchmarking program for
+       the main loop comparing the main loop against a 
+       hand-written (timeloop-basic.c) variant. 
+
+       * tests/mainloop-test.c: New torture test of mainloop.
+       
+       * docs/Changes-2.0.txt: Started. Added text about
+       changes to GMain.
+
+       * gmain.c (g_main_add_poll_unlocked): Initial fd->revents
+       to zero. (#8482, Benjamin Kahn)
+
 2000-12-01  Tor Lillqvist  <tml@iki.fi>
 
        * {.,*}/makefile.msc.in: Include make.msc from GLib's build subdir.
index 41e6843f5fa906f2e21c4b6458787693ae1a9b5e..92184925f8206de8510b4a2d72e7aa654be6c3ae 100644 (file)
@@ -1,3 +1,35 @@
+Tue Dec  5 12:23:04 2000  Owen Taylor  <otaylor@redhat.com>
+
+        * gmain.[hc]: Major change in API for creating sources
+       to handle multiple main loops (GMainContext *). 
+
+       GSources are now exposed as GSource * and implemented
+       with structure derivation.
+
+       * giochannel.[ch]: Changed vtable for GIOChannel to correspond
+       to the new mainloop API, add g_io_channel_create_watch().
+        
+       * gtypes.h: Move GTimeVal here.
+
+       * gthread.h: Remove gmain.h include to avoid circularity.
+       
+        * giounix.c: Update for new GMain API.
+        
+       * giowin32.c: Update for new GMain API. (No check for
+       proper compilation or working.)
+
+       * timeloop.c timeloop-basic.c: A benchmarking program for
+       the main loop comparing the main loop against a 
+       hand-written (timeloop-basic.c) variant. 
+
+       * tests/mainloop-test.c: New torture test of mainloop.
+       
+       * docs/Changes-2.0.txt: Started. Added text about
+       changes to GMain.
+
+       * gmain.c (g_main_add_poll_unlocked): Initial fd->revents
+       to zero. (#8482, Benjamin Kahn)
+
 2000-12-01  Tor Lillqvist  <tml@iki.fi>
 
        * {.,*}/makefile.msc.in: Include make.msc from GLib's build subdir.
index 41e6843f5fa906f2e21c4b6458787693ae1a9b5e..92184925f8206de8510b4a2d72e7aa654be6c3ae 100644 (file)
@@ -1,3 +1,35 @@
+Tue Dec  5 12:23:04 2000  Owen Taylor  <otaylor@redhat.com>
+
+        * gmain.[hc]: Major change in API for creating sources
+       to handle multiple main loops (GMainContext *). 
+
+       GSources are now exposed as GSource * and implemented
+       with structure derivation.
+
+       * giochannel.[ch]: Changed vtable for GIOChannel to correspond
+       to the new mainloop API, add g_io_channel_create_watch().
+        
+       * gtypes.h: Move GTimeVal here.
+
+       * gthread.h: Remove gmain.h include to avoid circularity.
+       
+        * giounix.c: Update for new GMain API.
+        
+       * giowin32.c: Update for new GMain API. (No check for
+       proper compilation or working.)
+
+       * timeloop.c timeloop-basic.c: A benchmarking program for
+       the main loop comparing the main loop against a 
+       hand-written (timeloop-basic.c) variant. 
+
+       * tests/mainloop-test.c: New torture test of mainloop.
+       
+       * docs/Changes-2.0.txt: Started. Added text about
+       changes to GMain.
+
+       * gmain.c (g_main_add_poll_unlocked): Initial fd->revents
+       to zero. (#8482, Benjamin Kahn)
+
 2000-12-01  Tor Lillqvist  <tml@iki.fi>
 
        * {.,*}/makefile.msc.in: Include make.msc from GLib's build subdir.
index 41e6843f5fa906f2e21c4b6458787693ae1a9b5e..92184925f8206de8510b4a2d72e7aa654be6c3ae 100644 (file)
@@ -1,3 +1,35 @@
+Tue Dec  5 12:23:04 2000  Owen Taylor  <otaylor@redhat.com>
+
+        * gmain.[hc]: Major change in API for creating sources
+       to handle multiple main loops (GMainContext *). 
+
+       GSources are now exposed as GSource * and implemented
+       with structure derivation.
+
+       * giochannel.[ch]: Changed vtable for GIOChannel to correspond
+       to the new mainloop API, add g_io_channel_create_watch().
+        
+       * gtypes.h: Move GTimeVal here.
+
+       * gthread.h: Remove gmain.h include to avoid circularity.
+       
+        * giounix.c: Update for new GMain API.
+        
+       * giowin32.c: Update for new GMain API. (No check for
+       proper compilation or working.)
+
+       * timeloop.c timeloop-basic.c: A benchmarking program for
+       the main loop comparing the main loop against a 
+       hand-written (timeloop-basic.c) variant. 
+
+       * tests/mainloop-test.c: New torture test of mainloop.
+       
+       * docs/Changes-2.0.txt: Started. Added text about
+       changes to GMain.
+
+       * gmain.c (g_main_add_poll_unlocked): Initial fd->revents
+       to zero. (#8482, Benjamin Kahn)
+
 2000-12-01  Tor Lillqvist  <tml@iki.fi>
 
        * {.,*}/makefile.msc.in: Include make.msc from GLib's build subdir.
index 41e6843f5fa906f2e21c4b6458787693ae1a9b5e..92184925f8206de8510b4a2d72e7aa654be6c3ae 100644 (file)
@@ -1,3 +1,35 @@
+Tue Dec  5 12:23:04 2000  Owen Taylor  <otaylor@redhat.com>
+
+        * gmain.[hc]: Major change in API for creating sources
+       to handle multiple main loops (GMainContext *). 
+
+       GSources are now exposed as GSource * and implemented
+       with structure derivation.
+
+       * giochannel.[ch]: Changed vtable for GIOChannel to correspond
+       to the new mainloop API, add g_io_channel_create_watch().
+        
+       * gtypes.h: Move GTimeVal here.
+
+       * gthread.h: Remove gmain.h include to avoid circularity.
+       
+        * giounix.c: Update for new GMain API.
+        
+       * giowin32.c: Update for new GMain API. (No check for
+       proper compilation or working.)
+
+       * timeloop.c timeloop-basic.c: A benchmarking program for
+       the main loop comparing the main loop against a 
+       hand-written (timeloop-basic.c) variant. 
+
+       * tests/mainloop-test.c: New torture test of mainloop.
+       
+       * docs/Changes-2.0.txt: Started. Added text about
+       changes to GMain.
+
+       * gmain.c (g_main_add_poll_unlocked): Initial fd->revents
+       to zero. (#8482, Benjamin Kahn)
+
 2000-12-01  Tor Lillqvist  <tml@iki.fi>
 
        * {.,*}/makefile.msc.in: Include make.msc from GLib's build subdir.
index 41e6843f5fa906f2e21c4b6458787693ae1a9b5e..92184925f8206de8510b4a2d72e7aa654be6c3ae 100644 (file)
@@ -1,3 +1,35 @@
+Tue Dec  5 12:23:04 2000  Owen Taylor  <otaylor@redhat.com>
+
+        * gmain.[hc]: Major change in API for creating sources
+       to handle multiple main loops (GMainContext *). 
+
+       GSources are now exposed as GSource * and implemented
+       with structure derivation.
+
+       * giochannel.[ch]: Changed vtable for GIOChannel to correspond
+       to the new mainloop API, add g_io_channel_create_watch().
+        
+       * gtypes.h: Move GTimeVal here.
+
+       * gthread.h: Remove gmain.h include to avoid circularity.
+       
+        * giounix.c: Update for new GMain API.
+        
+       * giowin32.c: Update for new GMain API. (No check for
+       proper compilation or working.)
+
+       * timeloop.c timeloop-basic.c: A benchmarking program for
+       the main loop comparing the main loop against a 
+       hand-written (timeloop-basic.c) variant. 
+
+       * tests/mainloop-test.c: New torture test of mainloop.
+       
+       * docs/Changes-2.0.txt: Started. Added text about
+       changes to GMain.
+
+       * gmain.c (g_main_add_poll_unlocked): Initial fd->revents
+       to zero. (#8482, Benjamin Kahn)
+
 2000-12-01  Tor Lillqvist  <tml@iki.fi>
 
        * {.,*}/makefile.msc.in: Include make.msc from GLib's build subdir.
index 41e6843f5fa906f2e21c4b6458787693ae1a9b5e..92184925f8206de8510b4a2d72e7aa654be6c3ae 100644 (file)
@@ -1,3 +1,35 @@
+Tue Dec  5 12:23:04 2000  Owen Taylor  <otaylor@redhat.com>
+
+        * gmain.[hc]: Major change in API for creating sources
+       to handle multiple main loops (GMainContext *). 
+
+       GSources are now exposed as GSource * and implemented
+       with structure derivation.
+
+       * giochannel.[ch]: Changed vtable for GIOChannel to correspond
+       to the new mainloop API, add g_io_channel_create_watch().
+        
+       * gtypes.h: Move GTimeVal here.
+
+       * gthread.h: Remove gmain.h include to avoid circularity.
+       
+        * giounix.c: Update for new GMain API.
+        
+       * giowin32.c: Update for new GMain API. (No check for
+       proper compilation or working.)
+
+       * timeloop.c timeloop-basic.c: A benchmarking program for
+       the main loop comparing the main loop against a 
+       hand-written (timeloop-basic.c) variant. 
+
+       * tests/mainloop-test.c: New torture test of mainloop.
+       
+       * docs/Changes-2.0.txt: Started. Added text about
+       changes to GMain.
+
+       * gmain.c (g_main_add_poll_unlocked): Initial fd->revents
+       to zero. (#8482, Benjamin Kahn)
+
 2000-12-01  Tor Lillqvist  <tml@iki.fi>
 
        * {.,*}/makefile.msc.in: Include make.msc from GLib's build subdir.
index e29f0a6dc43c71de4da861bcf68a5e0ddc3648e7..a4224af382433998b30f5232dd299e1535be2fff 100644 (file)
@@ -165,10 +165,11 @@ libglib_1_3_la_LDFLAGS = \
        -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \
        -export-dynamic
 
-noinst_PROGRAMS = testglib testgdate testgdateparser
+noinst_PROGRAMS = testglib testgdate testgdateparser timeloop
 testglib_LDADD = libglib-1.3.la
 testgdate_LDADD = libglib-1.3.la
 testgdateparser_LDADD = libglib-1.3.la
+timeloop_LDADD = libglib-1.3.la
 
 m4datadir = $(datadir)/aclocal
 m4data_DATA = glib-2.0.m4
diff --git a/docs/Changes-2.0.txt b/docs/Changes-2.0.txt
new file mode 100644 (file)
index 0000000..8bf2819
--- /dev/null
@@ -0,0 +1,24 @@
+* The event loop functionality GMain has extensively been revised to 
+  support multiple separate main loops in separate threads. All sources
+  (timeouts, idle functions, etc.) are associated with a GMainContext.
+
+  Compatibility functions exist so that most application code dealing with
+  the main loop will continue to work. However, code that creates
+  new custom types of sources will require modification.
+
+  The main changes here are:
+
+   - Sources are now exposed as GSource *, rather than simply as numeric
+     IDS.
+
+   - New types of sources are created by structure "derivation" from GSource,
+     so the source_data parameter to the GSource vfuncs has been
+     replaced with a GSource *.
+
+   - Sources are first created, then later added to a specific GMainContext
+
+   - Dispatching has been modified so both the callback and data are passed
+     in to the ->dispatch() vfunc.
+
+  To go along with this change, the vtable for GIOChannel has changed and
+  add_watch() has been replaced by create_watch().
\ No newline at end of file
index ce802426dace0e8e724dfff1f3f17c7adca4b3eb..569983f4a5fd0015ff25c9b8ba1efd0e7b9361f9 100644 (file)
@@ -27,6 +27,7 @@
 #include <unistd.h>
 #endif
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <errno.h>
 #include <sys/types.h>
index 29a20023136c43d5e13c0c8526052301d5fb7997..853a0adc719660c95fe921a6f05b20c755d12bd0 100644 (file)
@@ -104,6 +104,15 @@ g_io_channel_close (GIOChannel *channel)
   channel->funcs->io_close (channel);
 }
 
+GSource *
+g_io_create_watch (GIOChannel  *channel,
+                  GIOCondition condition)
+{
+  g_return_val_if_fail (channel != NULL, NULL);
+
+  return channel->funcs->io_create_watch (channel, condition);
+}
+
 guint 
 g_io_add_watch_full (GIOChannel    *channel,
                     gint           priority,
@@ -112,10 +121,17 @@ g_io_add_watch_full (GIOChannel    *channel,
                     gpointer       user_data,
                     GDestroyNotify notify)
 {
+  GSource *source;
+  
   g_return_val_if_fail (channel != NULL, 0);
 
-  return channel->funcs->io_add_watch (channel, priority, condition,
-                                      func, user_data, notify);
+  source = g_io_create_watch (channel, condition);
+
+  if (priority != G_PRIORITY_DEFAULT)
+    g_source_set_priority (source, priority);
+  g_source_set_callback (source, (GSourceFunc)func, user_data, notify);
+
+  return g_source_attach (source, NULL);
 }
 
 guint 
@@ -124,5 +140,5 @@ g_io_add_watch (GIOChannel    *channel,
                GIOFunc        func,
                gpointer       user_data)
 {
-  return g_io_add_watch_full (channel, 0, condition, func, user_data, NULL);
+  return g_io_add_watch_full (channel, G_PRIORITY_DEFAULT, condition, func, user_data, NULL);
 }
index 7992a69e4858094581c4d1244245611e329bbac8..e9a64c0b3700fd3115fa0cc06509e140da0ffa3c 100644 (file)
@@ -27,6 +27,7 @@
 #ifndef __G_IOCHANNEL_H__
 #define __G_IOCHANNEL_H__
 
+#include <gmain.h>
 #include <gtypes.h>
 
 G_BEGIN_DECLS
@@ -71,25 +72,21 @@ typedef gboolean (*GIOFunc) (GIOChannel   *source,
                             gpointer      data);
 struct _GIOFuncs
 {
-  GIOError (*io_read)   (GIOChannel    *channel, 
-                        gchar          *buf, 
-                        guint           count,
-                        guint          *bytes_read);
-  GIOError (*io_write)  (GIOChannel    *channel, 
-                        gchar          *buf, 
-                        guint           count,
-                        guint          *bytes_written);
-  GIOError (*io_seek)   (GIOChannel    *channel, 
-                        gint            offset, 
-                        GSeekType       type);
-  void (*io_close)      (GIOChannel    *channel);
-  guint (*io_add_watch) (GIOChannel     *channel,
-                        gint            priority,
-                        GIOCondition    condition,
-                        GIOFunc         func,
-                        gpointer        user_data,
-                        GDestroyNotify  notify);
-  void (*io_free)       (GIOChannel    *channel);
+  GIOError  (*io_read)         (GIOChannel   *channel, 
+                               gchar        *buf, 
+                               guint         count,
+                               guint        *bytes_read);
+  GIOError  (*io_write)        (GIOChannel   *channel, 
+                               gchar        *buf, 
+                               guint         count,
+                               guint        *bytes_written);
+  GIOError  (*io_seek)         (GIOChannel   *channel, 
+                               gint          offset, 
+                               GSeekType     type);
+  void      (*io_close)        (GIOChannel   *channel);
+  GSource * (*io_create_watch) (GIOChannel   *channel,
+                               GIOCondition  condition);
+  void      (*io_free)         (GIOChannel   *channel);
 };
 
 void        g_io_channel_init   (GIOChannel    *channel);
@@ -113,6 +110,8 @@ guint     g_io_add_watch_full   (GIOChannel    *channel,
                                 GIOFunc        func,
                                 gpointer       user_data,
                                 GDestroyNotify notify);
+GSource *g_io_create_watch      (GIOChannel   *channel,
+                                GIOCondition  condition);
 guint    g_io_add_watch         (GIOChannel    *channel,
                                 GIOCondition   condition,
                                 GIOFunc        func,
index 2a06dac80403326264327310fef0999efa09487d..ca98e0c860f4b27aca3eef05706068036d88d055 100644 (file)
--- a/giounix.c
+++ b/giounix.c
 typedef struct _GIOUnixChannel GIOUnixChannel;
 typedef struct _GIOUnixWatch GIOUnixWatch;
 
-struct _GIOUnixChannel {
+struct _GIOUnixChannel
+{
   GIOChannel channel;
   gint fd;
 };
 
-struct _GIOUnixWatch {
+struct _GIOUnixWatch
+{
+  GSource       source;
   GPollFD       pollfd;
   GIOChannel   *channel;
   GIOCondition  condition;
@@ -56,37 +59,29 @@ struct _GIOUnixWatch {
 };
 
 
-static GIOError g_io_unix_read (GIOChannel *channel, 
-                      gchar     *buf, 
-                      guint      count,
-                      guint     *bytes_written);
-                      
-static GIOError g_io_unix_write(GIOChannel *channel, 
-                               gchar     *buf, 
-                               guint      count,
-                               guint     *bytes_written);
-static GIOError g_io_unix_seek (GIOChannel *channel,
-                               gint      offset, 
-                               GSeekType type);
-static void g_io_unix_close    (GIOChannel *channel);
-static void g_io_unix_free     (GIOChannel *channel);
-static guint  g_io_unix_add_watch (GIOChannel      *channel,
-                                  gint           priority,
-                                  GIOCondition   condition,
-                                  GIOFunc        func,
-                                  gpointer       user_data,
-                                  GDestroyNotify notify);
-static gboolean g_io_unix_prepare  (gpointer source_data, 
-                                   GTimeVal *current_time,
-                                   gint *timeout,
-                                   gpointer user_data);
-static gboolean g_io_unix_check    (gpointer source_data,
-                                   GTimeVal *current_time,
-                                   gpointer user_data);
-static gboolean g_io_unix_dispatch (gpointer source_data,
-                                   GTimeVal *current_time,
-                                   gpointer user_data);
-static void g_io_unix_destroy  (gpointer source_data);
+static GIOError g_io_unix_read         (GIOChannel   *channel,
+                                       gchar        *buf,
+                                       guint         count,
+                                       guint        *bytes_written);
+static GIOError g_io_unix_write        (GIOChannel   *channel,
+                                       gchar        *buf,
+                                       guint         count,
+                                       guint        *bytes_written);
+static GIOError g_io_unix_seek         (GIOChannel   *channel,
+                                       gint          offset,
+                                       GSeekType     type);
+static void     g_io_unix_close        (GIOChannel   *channel);
+static void     g_io_unix_free         (GIOChannel   *channel);
+static GSource *g_io_unix_create_watch (GIOChannel   *channel,
+                                       GIOCondition  condition);
+
+static gboolean g_io_unix_prepare  (GSource     *source,
+                                   gint        *timeout);
+static gboolean g_io_unix_check    (GSource     *source);
+static gboolean g_io_unix_dispatch (GSource     *source,
+                                   GSourceFunc  callback,
+                                   gpointer     user_data);
+static void     g_io_unix_destroy  (GSource     *source);
 
 GSourceFuncs unix_watch_funcs = {
   g_io_unix_prepare,
@@ -100,51 +95,53 @@ GIOFuncs unix_channel_funcs = {
   g_io_unix_write,
   g_io_unix_seek,
   g_io_unix_close,
-  g_io_unix_add_watch,
+  g_io_unix_create_watch,
   g_io_unix_free,
 };
 
 static gboolean 
-g_io_unix_prepare  (gpointer source_data, 
-                   GTimeVal *current_time,
-                   gint     *timeout,
-                   gpointer user_data)
+g_io_unix_prepare (GSource  *source,
+                  gint     *timeout)
 {
   *timeout = -1;
   return FALSE;
 }
 
 static gboolean 
-g_io_unix_check    (gpointer source_data,
-                   GTimeVal *current_time,
-                   gpointer user_data)
+g_io_unix_check (GSource  *source)
 {
-  GIOUnixWatch *data = source_data;
+  GIOUnixWatch *watch = (GIOUnixWatch *)source;
 
-  return (data->pollfd.revents & data->condition);
+  return (watch->pollfd.revents & watch->condition);
 }
 
 static gboolean
-g_io_unix_dispatch (gpointer source_data, 
-                   GTimeVal *current_time,
-                   gpointer user_data)
+g_io_unix_dispatch (GSource     *source,
+                   GSourceFunc  callback,
+                   gpointer     user_data)
 
 {
-  GIOUnixWatch *data = source_data;
+  GIOFunc func = (GIOFunc)callback;
+  GIOUnixWatch *watch = (GIOUnixWatch *)source;
 
-  return (*data->callback)(data->channel,
-                          data->pollfd.revents & data->condition,
-                          user_data);
+  if (!func)
+    {
+      g_warning ("IO watch dispatched without callback\n"
+                "You must call g_source_connect().");
+      return FALSE;
+    }
+  
+  return (*func) (watch->channel,
+                 watch->pollfd.revents & watch->condition,
+                 user_data);
 }
 
 static void 
-g_io_unix_destroy (gpointer source_data)
+g_io_unix_destroy (GSource *source)
 {
-  GIOUnixWatch *data = source_data;
+  GIOUnixWatch *watch = (GIOUnixWatch *)source;
 
-  g_main_remove_poll (&data->pollfd);
-  g_io_channel_unref (data->channel);
-  g_free (data);
+  g_io_channel_unref (watch->channel);
 }
 
 static GIOError 
@@ -269,29 +266,29 @@ g_io_unix_free (GIOChannel *channel)
   g_free (unix_channel);
 }
 
-static guint 
-g_io_unix_add_watch (GIOChannel    *channel,
-                    gint           priority,
-                    GIOCondition   condition,
-                    GIOFunc        func,
-                    gpointer       user_data,
-                    GDestroyNotify notify)
+static GSource *
+g_io_unix_create_watch (GIOChannel   *channel,
+                       GIOCondition  condition)
 {
-  GIOUnixWatch *watch = g_new (GIOUnixWatch, 1);
   GIOUnixChannel *unix_channel = (GIOUnixChannel *)channel;
+  GSource *source;
+  GIOUnixWatch *watch;
+
+
+  source = g_source_new (&unix_watch_funcs, sizeof (GIOUnixWatch));
+  watch = (GIOUnixWatch *)source;
   
   watch->channel = channel;
   g_io_channel_ref (channel);
-
-  watch->callback = func;
+  
   watch->condition = condition;
 
   watch->pollfd.fd = unix_channel->fd;
   watch->pollfd.events = condition;
 
-  g_main_add_poll (&watch->pollfd, priority);
+  g_source_add_poll (source, &watch->pollfd);
 
-  return g_source_add (priority, TRUE, &unix_watch_funcs, watch, user_data, notify);
+  return source;
 }
 
 GIOChannel *
index 560d714ba285cf16c940f54c52e6e21b0a30e973..f4e74ccc8940af806a043d78a0b5cb5a5c8ccb76 100644 (file)
@@ -102,6 +102,7 @@ struct _GIOWin32Channel {
 #define UNLOCK(mutex) LeaveCriticalSection (&mutex)
 
 struct _GIOWin32Watch {
+  GSource       source;
   GPollFD       pollfd;
   GIOChannel   *channel;
   GIOCondition  condition;
@@ -319,10 +320,8 @@ buffer_read (GIOWin32Channel *channel,
 }
 
 static gboolean
-g_io_win32_prepare (gpointer  source_data,
-                   GTimeVal *current_time,
-                   gint     *timeout,
-                   gpointer  user_data)
+g_io_win32_prepare (GSource *source,
+                   gint    *timeout)
 {
   *timeout = -1;
   
@@ -330,12 +329,10 @@ g_io_win32_prepare (gpointer  source_data,
 }
 
 static gboolean
-g_io_win32_check (gpointer  source_data,
-                 GTimeVal *current_time,
-                 gpointer  user_data)
+g_io_win32_check (GSource *source)
 {
-  GIOWin32Watch *data = source_data;
-  GIOWin32Channel *channel = (GIOWin32Channel *) data->channel;
+  GIOWin32Watch *watch = (GIOWin32Watch *)source;
+  GIOWin32Channel *channel = (GIOWin32Channel *) watch->channel;
   
   /* If the thread has died, we have encountered EOF. If the buffer
    * also is emtpty set the HUP bit.
@@ -345,34 +342,31 @@ g_io_win32_check (gpointer  source_data,
       if (channel->debug)
        g_print ("g_io_win32_check: setting G_IO_HUP thread %#x rdp=%d wrp=%d\n",
                 channel->thread_id, channel->rdp, channel->wrp);
-      data->pollfd.revents |= G_IO_HUP;
+      watch->pollfd.revents |= G_IO_HUP;
       return TRUE;
     }
   
-  return (data->pollfd.revents & data->condition);
+  return (watch->pollfd.revents & watch->condition);
 }
 
 static gboolean
-g_io_win32_dispatch (gpointer  source_data,
-                    GTimeVal *current_time,
-                    gpointer  user_data)
-     
+g_io_win32_dispatch (GSource     *source,
+                    GSourceFunc  callback,
+                    gpointer     user_data)
 {
-  GIOWin32Watch *data = source_data;
+  GIOWin32Watch *watch = (GIOWin32Watch *)source;
   
-  return (*data->callback) (data->channel,
-                           data->pollfd.revents & data->condition,
-                           user_data);
+  return (*callback) (watch->channel,
+                     watch->pollfd.revents & watch->condition,
+                     user_data);
 }
 
 static void
-g_io_win32_destroy (gpointer source_data)
+g_io_win32_destroy (GSource *source)
 {
-  GIOWin32Watch *data = source_data;
+  GIOWin32Watch *watch = (GIOWin32Watch *)source;
   
-  g_main_remove_poll (&data->pollfd);
-  g_io_channel_unref (data->channel);
-  g_free (data);
+  g_io_channel_unref (watch->channel);
 }
 
 static GSourceFuncs win32_watch_funcs = {
@@ -382,22 +376,21 @@ static GSourceFuncs win32_watch_funcs = {
   g_io_win32_destroy
 };
 
-static guint
-g_io_win32_add_watch (GIOChannel    *channel,
-                     gint           priority,
-                     GIOCondition   condition,
-                     GIOFunc        func,
-                     gpointer       user_data,
-                     GDestroyNotify notify,
-                     int (*reader) (int, guchar *, int))
+static GSource *
+g_io_win32_create_watch (GIOChannel    *channel,
+                        GIOCondition   condition,
+                        int (*reader) (int, guchar *, int)))
 {
-  GIOWin32Watch *watch = g_new (GIOWin32Watch, 1);
   GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
+  GIOWin32Watch *watch;
+  GSource *source;
+
+  source = g_source_new (&win32_watch_funcs, sizeof (GIOWin32Watch));
+  watch = (GIOWin32Watch *)source;
   
   watch->channel = channel;
   g_io_channel_ref (channel);
   
-  watch->callback = func;
   watch->condition = condition;
   
   if (win32_channel->data_avail_event == NULL)
@@ -407,16 +400,15 @@ g_io_win32_add_watch (GIOChannel    *channel,
   watch->pollfd.events = condition;
   
   if (win32_channel->debug)
-    g_print ("g_io_win32_add_watch: fd:%d handle:%#x\n",
+    g_print ("g_io_win32_create_watch: fd:%d handle:%#x\n",
             win32_channel->fd, watch->pollfd.fd);
   
   if (win32_channel->thread_id == 0)
     create_reader_thread (win32_channel, reader);
 
-  g_main_add_poll (&watch->pollfd, priority);
+  g_source_add_poll (source, &watch->pollfd);
   
-  return g_source_add (priority, TRUE, &win32_watch_funcs, watch,
-                      user_data, notify);
+  return source;
 }
 
 static GIOError
@@ -490,29 +482,27 @@ g_io_win32_free (GIOChannel *channel)
   g_free (win32_channel);
 }
 
-static guint
-g_io_win32_msg_add_watch (GIOChannel    *channel,
-                         gint           priority,
-                         GIOCondition   condition,
-                         GIOFunc        func,
-                         gpointer       user_data,
-                         GDestroyNotify notify)
+static GSource *
+g_io_win32_msg_create_watch (GIOChannel    *channel,
+                            GIOCondition   condition)
 {
-  GIOWin32Watch *watch = g_new (GIOWin32Watch, 1);
+  GIOWin32Watch *watch;
+  GSource *source;
+
+  source = g_source_new (&win32_watch_funcs, sizeof (GIOWin32Watch));
+  watch = (GIOWin32Watch *)source;
   
   watch->channel = channel;
   g_io_channel_ref (channel);
   
-  watch->callback = func;
   watch->condition = condition;
   
   watch->pollfd.fd = G_WIN32_MSG_HANDLE;
   watch->pollfd.events = condition;
   
-  g_main_add_poll (&watch->pollfd, priority);
+  g_source_add_poll (source, &watch->pollfd);
   
-  return g_source_add (priority, TRUE, &win32_watch_funcs,
-                      watch, user_data, notify);
+  return source;
 }
 
 static GIOError
@@ -653,16 +643,11 @@ fd_reader (int     fd,
   return read (fd, buf, len);
 }
 
-static guint
-g_io_win32_fd_add_watch (GIOChannel    *channel,
-                        gint           priority,
-                        GIOCondition   condition,
-                        GIOFunc        func,
-                        gpointer       user_data,
-                        GDestroyNotify notify)
+static GSource *
+g_io_win32_fd_create_watch (GIOChannel    *channel,
+                           GIOCondition   condition)
 {
-  return g_io_win32_add_watch (channel, priority, condition,
-                              func, user_data, notify, fd_reader);
+  return g_io_win32_create_watch (channel, condition, fd_reader);
 }
 
 static GIOError
@@ -752,16 +737,11 @@ sock_reader (int     fd,
   return recv (fd, buf, len, 0);
 }
 
-static guint
-g_io_win32_sock_add_watch (GIOChannel    *channel,
-                          gint           priority,
-                          GIOCondition   condition,
-                          GIOFunc        func,
-                          gpointer       user_data,
-                          GDestroyNotify notify)
+static GSource *
+g_io_win32_sock_create_watch (GIOChannel    *channel,
+                             GIOCondition   condition)
 {
-  return g_io_win32_add_watch (channel, priority, condition,
-                              func, user_data, notify, sock_reader);
+  return g_io_win32_add_watch (channel, condition, sock_reader);
 }
 
 static GIOFuncs win32_channel_msg_funcs = {
@@ -769,7 +749,7 @@ static GIOFuncs win32_channel_msg_funcs = {
   g_io_win32_msg_write,
   g_io_win32_no_seek,
   g_io_win32_msg_close,
-  g_io_win32_msg_add_watch,
+  g_io_win32_msg_create_watch,
   g_io_win32_free
 };
 
@@ -778,7 +758,7 @@ static GIOFuncs win32_channel_fd_funcs = {
   g_io_win32_fd_write,
   g_io_win32_fd_seek,
   g_io_win32_fd_close,
-  g_io_win32_fd_add_watch,
+  g_io_win32_fd_create_watch,
   g_io_win32_free
 };
 
@@ -787,7 +767,7 @@ static GIOFuncs win32_channel_sock_funcs = {
   g_io_win32_sock_write,
   g_io_win32_no_seek,
   g_io_win32_sock_close,
-  g_io_win32_sock_add_watch,
+  g_io_win32_sock_create_watch,
   g_io_win32_free
 };
 
index e29f0a6dc43c71de4da861bcf68a5e0ddc3648e7..a4224af382433998b30f5232dd299e1535be2fff 100644 (file)
@@ -165,10 +165,11 @@ libglib_1_3_la_LDFLAGS = \
        -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \
        -export-dynamic
 
-noinst_PROGRAMS = testglib testgdate testgdateparser
+noinst_PROGRAMS = testglib testgdate testgdateparser timeloop
 testglib_LDADD = libglib-1.3.la
 testgdate_LDADD = libglib-1.3.la
 testgdateparser_LDADD = libglib-1.3.la
+timeloop_LDADD = libglib-1.3.la
 
 m4datadir = $(datadir)/aclocal
 m4data_DATA = glib-2.0.m4
index ce802426dace0e8e724dfff1f3f17c7adca4b3eb..569983f4a5fd0015ff25c9b8ba1efd0e7b9361f9 100644 (file)
@@ -27,6 +27,7 @@
 #include <unistd.h>
 #endif
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <errno.h>
 #include <sys/types.h>
index 29a20023136c43d5e13c0c8526052301d5fb7997..853a0adc719660c95fe921a6f05b20c755d12bd0 100644 (file)
@@ -104,6 +104,15 @@ g_io_channel_close (GIOChannel *channel)
   channel->funcs->io_close (channel);
 }
 
+GSource *
+g_io_create_watch (GIOChannel  *channel,
+                  GIOCondition condition)
+{
+  g_return_val_if_fail (channel != NULL, NULL);
+
+  return channel->funcs->io_create_watch (channel, condition);
+}
+
 guint 
 g_io_add_watch_full (GIOChannel    *channel,
                     gint           priority,
@@ -112,10 +121,17 @@ g_io_add_watch_full (GIOChannel    *channel,
                     gpointer       user_data,
                     GDestroyNotify notify)
 {
+  GSource *source;
+  
   g_return_val_if_fail (channel != NULL, 0);
 
-  return channel->funcs->io_add_watch (channel, priority, condition,
-                                      func, user_data, notify);
+  source = g_io_create_watch (channel, condition);
+
+  if (priority != G_PRIORITY_DEFAULT)
+    g_source_set_priority (source, priority);
+  g_source_set_callback (source, (GSourceFunc)func, user_data, notify);
+
+  return g_source_attach (source, NULL);
 }
 
 guint 
@@ -124,5 +140,5 @@ g_io_add_watch (GIOChannel    *channel,
                GIOFunc        func,
                gpointer       user_data)
 {
-  return g_io_add_watch_full (channel, 0, condition, func, user_data, NULL);
+  return g_io_add_watch_full (channel, G_PRIORITY_DEFAULT, condition, func, user_data, NULL);
 }
index 7992a69e4858094581c4d1244245611e329bbac8..e9a64c0b3700fd3115fa0cc06509e140da0ffa3c 100644 (file)
@@ -27,6 +27,7 @@
 #ifndef __G_IOCHANNEL_H__
 #define __G_IOCHANNEL_H__
 
+#include <gmain.h>
 #include <gtypes.h>
 
 G_BEGIN_DECLS
@@ -71,25 +72,21 @@ typedef gboolean (*GIOFunc) (GIOChannel   *source,
                             gpointer      data);
 struct _GIOFuncs
 {
-  GIOError (*io_read)   (GIOChannel    *channel, 
-                        gchar          *buf, 
-                        guint           count,
-                        guint          *bytes_read);
-  GIOError (*io_write)  (GIOChannel    *channel, 
-                        gchar          *buf, 
-                        guint           count,
-                        guint          *bytes_written);
-  GIOError (*io_seek)   (GIOChannel    *channel, 
-                        gint            offset, 
-                        GSeekType       type);
-  void (*io_close)      (GIOChannel    *channel);
-  guint (*io_add_watch) (GIOChannel     *channel,
-                        gint            priority,
-                        GIOCondition    condition,
-                        GIOFunc         func,
-                        gpointer        user_data,
-                        GDestroyNotify  notify);
-  void (*io_free)       (GIOChannel    *channel);
+  GIOError  (*io_read)         (GIOChannel   *channel, 
+                               gchar        *buf, 
+                               guint         count,
+                               guint        *bytes_read);
+  GIOError  (*io_write)        (GIOChannel   *channel, 
+                               gchar        *buf, 
+                               guint         count,
+                               guint        *bytes_written);
+  GIOError  (*io_seek)         (GIOChannel   *channel, 
+                               gint          offset, 
+                               GSeekType     type);
+  void      (*io_close)        (GIOChannel   *channel);
+  GSource * (*io_create_watch) (GIOChannel   *channel,
+                               GIOCondition  condition);
+  void      (*io_free)         (GIOChannel   *channel);
 };
 
 void        g_io_channel_init   (GIOChannel    *channel);
@@ -113,6 +110,8 @@ guint     g_io_add_watch_full   (GIOChannel    *channel,
                                 GIOFunc        func,
                                 gpointer       user_data,
                                 GDestroyNotify notify);
+GSource *g_io_create_watch      (GIOChannel   *channel,
+                                GIOCondition  condition);
 guint    g_io_add_watch         (GIOChannel    *channel,
                                 GIOCondition   condition,
                                 GIOFunc        func,
index 2a06dac80403326264327310fef0999efa09487d..ca98e0c860f4b27aca3eef05706068036d88d055 100644 (file)
 typedef struct _GIOUnixChannel GIOUnixChannel;
 typedef struct _GIOUnixWatch GIOUnixWatch;
 
-struct _GIOUnixChannel {
+struct _GIOUnixChannel
+{
   GIOChannel channel;
   gint fd;
 };
 
-struct _GIOUnixWatch {
+struct _GIOUnixWatch
+{
+  GSource       source;
   GPollFD       pollfd;
   GIOChannel   *channel;
   GIOCondition  condition;
@@ -56,37 +59,29 @@ struct _GIOUnixWatch {
 };
 
 
-static GIOError g_io_unix_read (GIOChannel *channel, 
-                      gchar     *buf, 
-                      guint      count,
-                      guint     *bytes_written);
-                      
-static GIOError g_io_unix_write(GIOChannel *channel, 
-                               gchar     *buf, 
-                               guint      count,
-                               guint     *bytes_written);
-static GIOError g_io_unix_seek (GIOChannel *channel,
-                               gint      offset, 
-                               GSeekType type);
-static void g_io_unix_close    (GIOChannel *channel);
-static void g_io_unix_free     (GIOChannel *channel);
-static guint  g_io_unix_add_watch (GIOChannel      *channel,
-                                  gint           priority,
-                                  GIOCondition   condition,
-                                  GIOFunc        func,
-                                  gpointer       user_data,
-                                  GDestroyNotify notify);
-static gboolean g_io_unix_prepare  (gpointer source_data, 
-                                   GTimeVal *current_time,
-                                   gint *timeout,
-                                   gpointer user_data);
-static gboolean g_io_unix_check    (gpointer source_data,
-                                   GTimeVal *current_time,
-                                   gpointer user_data);
-static gboolean g_io_unix_dispatch (gpointer source_data,
-                                   GTimeVal *current_time,
-                                   gpointer user_data);
-static void g_io_unix_destroy  (gpointer source_data);
+static GIOError g_io_unix_read         (GIOChannel   *channel,
+                                       gchar        *buf,
+                                       guint         count,
+                                       guint        *bytes_written);
+static GIOError g_io_unix_write        (GIOChannel   *channel,
+                                       gchar        *buf,
+                                       guint         count,
+                                       guint        *bytes_written);
+static GIOError g_io_unix_seek         (GIOChannel   *channel,
+                                       gint          offset,
+                                       GSeekType     type);
+static void     g_io_unix_close        (GIOChannel   *channel);
+static void     g_io_unix_free         (GIOChannel   *channel);
+static GSource *g_io_unix_create_watch (GIOChannel   *channel,
+                                       GIOCondition  condition);
+
+static gboolean g_io_unix_prepare  (GSource     *source,
+                                   gint        *timeout);
+static gboolean g_io_unix_check    (GSource     *source);
+static gboolean g_io_unix_dispatch (GSource     *source,
+                                   GSourceFunc  callback,
+                                   gpointer     user_data);
+static void     g_io_unix_destroy  (GSource     *source);
 
 GSourceFuncs unix_watch_funcs = {
   g_io_unix_prepare,
@@ -100,51 +95,53 @@ GIOFuncs unix_channel_funcs = {
   g_io_unix_write,
   g_io_unix_seek,
   g_io_unix_close,
-  g_io_unix_add_watch,
+  g_io_unix_create_watch,
   g_io_unix_free,
 };
 
 static gboolean 
-g_io_unix_prepare  (gpointer source_data, 
-                   GTimeVal *current_time,
-                   gint     *timeout,
-                   gpointer user_data)
+g_io_unix_prepare (GSource  *source,
+                  gint     *timeout)
 {
   *timeout = -1;
   return FALSE;
 }
 
 static gboolean 
-g_io_unix_check    (gpointer source_data,
-                   GTimeVal *current_time,
-                   gpointer user_data)
+g_io_unix_check (GSource  *source)
 {
-  GIOUnixWatch *data = source_data;
+  GIOUnixWatch *watch = (GIOUnixWatch *)source;
 
-  return (data->pollfd.revents & data->condition);
+  return (watch->pollfd.revents & watch->condition);
 }
 
 static gboolean
-g_io_unix_dispatch (gpointer source_data, 
-                   GTimeVal *current_time,
-                   gpointer user_data)
+g_io_unix_dispatch (GSource     *source,
+                   GSourceFunc  callback,
+                   gpointer     user_data)
 
 {
-  GIOUnixWatch *data = source_data;
+  GIOFunc func = (GIOFunc)callback;
+  GIOUnixWatch *watch = (GIOUnixWatch *)source;
 
-  return (*data->callback)(data->channel,
-                          data->pollfd.revents & data->condition,
-                          user_data);
+  if (!func)
+    {
+      g_warning ("IO watch dispatched without callback\n"
+                "You must call g_source_connect().");
+      return FALSE;
+    }
+  
+  return (*func) (watch->channel,
+                 watch->pollfd.revents & watch->condition,
+                 user_data);
 }
 
 static void 
-g_io_unix_destroy (gpointer source_data)
+g_io_unix_destroy (GSource *source)
 {
-  GIOUnixWatch *data = source_data;
+  GIOUnixWatch *watch = (GIOUnixWatch *)source;
 
-  g_main_remove_poll (&data->pollfd);
-  g_io_channel_unref (data->channel);
-  g_free (data);
+  g_io_channel_unref (watch->channel);
 }
 
 static GIOError 
@@ -269,29 +266,29 @@ g_io_unix_free (GIOChannel *channel)
   g_free (unix_channel);
 }
 
-static guint 
-g_io_unix_add_watch (GIOChannel    *channel,
-                    gint           priority,
-                    GIOCondition   condition,
-                    GIOFunc        func,
-                    gpointer       user_data,
-                    GDestroyNotify notify)
+static GSource *
+g_io_unix_create_watch (GIOChannel   *channel,
+                       GIOCondition  condition)
 {
-  GIOUnixWatch *watch = g_new (GIOUnixWatch, 1);
   GIOUnixChannel *unix_channel = (GIOUnixChannel *)channel;
+  GSource *source;
+  GIOUnixWatch *watch;
+
+
+  source = g_source_new (&unix_watch_funcs, sizeof (GIOUnixWatch));
+  watch = (GIOUnixWatch *)source;
   
   watch->channel = channel;
   g_io_channel_ref (channel);
-
-  watch->callback = func;
+  
   watch->condition = condition;
 
   watch->pollfd.fd = unix_channel->fd;
   watch->pollfd.events = condition;
 
-  g_main_add_poll (&watch->pollfd, priority);
+  g_source_add_poll (source, &watch->pollfd);
 
-  return g_source_add (priority, TRUE, &unix_watch_funcs, watch, user_data, notify);
+  return source;
 }
 
 GIOChannel *
index 560d714ba285cf16c940f54c52e6e21b0a30e973..f4e74ccc8940af806a043d78a0b5cb5a5c8ccb76 100644 (file)
@@ -102,6 +102,7 @@ struct _GIOWin32Channel {
 #define UNLOCK(mutex) LeaveCriticalSection (&mutex)
 
 struct _GIOWin32Watch {
+  GSource       source;
   GPollFD       pollfd;
   GIOChannel   *channel;
   GIOCondition  condition;
@@ -319,10 +320,8 @@ buffer_read (GIOWin32Channel *channel,
 }
 
 static gboolean
-g_io_win32_prepare (gpointer  source_data,
-                   GTimeVal *current_time,
-                   gint     *timeout,
-                   gpointer  user_data)
+g_io_win32_prepare (GSource *source,
+                   gint    *timeout)
 {
   *timeout = -1;
   
@@ -330,12 +329,10 @@ g_io_win32_prepare (gpointer  source_data,
 }
 
 static gboolean
-g_io_win32_check (gpointer  source_data,
-                 GTimeVal *current_time,
-                 gpointer  user_data)
+g_io_win32_check (GSource *source)
 {
-  GIOWin32Watch *data = source_data;
-  GIOWin32Channel *channel = (GIOWin32Channel *) data->channel;
+  GIOWin32Watch *watch = (GIOWin32Watch *)source;
+  GIOWin32Channel *channel = (GIOWin32Channel *) watch->channel;
   
   /* If the thread has died, we have encountered EOF. If the buffer
    * also is emtpty set the HUP bit.
@@ -345,34 +342,31 @@ g_io_win32_check (gpointer  source_data,
       if (channel->debug)
        g_print ("g_io_win32_check: setting G_IO_HUP thread %#x rdp=%d wrp=%d\n",
                 channel->thread_id, channel->rdp, channel->wrp);
-      data->pollfd.revents |= G_IO_HUP;
+      watch->pollfd.revents |= G_IO_HUP;
       return TRUE;
     }
   
-  return (data->pollfd.revents & data->condition);
+  return (watch->pollfd.revents & watch->condition);
 }
 
 static gboolean
-g_io_win32_dispatch (gpointer  source_data,
-                    GTimeVal *current_time,
-                    gpointer  user_data)
-     
+g_io_win32_dispatch (GSource     *source,
+                    GSourceFunc  callback,
+                    gpointer     user_data)
 {
-  GIOWin32Watch *data = source_data;
+  GIOWin32Watch *watch = (GIOWin32Watch *)source;
   
-  return (*data->callback) (data->channel,
-                           data->pollfd.revents & data->condition,
-                           user_data);
+  return (*callback) (watch->channel,
+                     watch->pollfd.revents & watch->condition,
+                     user_data);
 }
 
 static void
-g_io_win32_destroy (gpointer source_data)
+g_io_win32_destroy (GSource *source)
 {
-  GIOWin32Watch *data = source_data;
+  GIOWin32Watch *watch = (GIOWin32Watch *)source;
   
-  g_main_remove_poll (&data->pollfd);
-  g_io_channel_unref (data->channel);
-  g_free (data);
+  g_io_channel_unref (watch->channel);
 }
 
 static GSourceFuncs win32_watch_funcs = {
@@ -382,22 +376,21 @@ static GSourceFuncs win32_watch_funcs = {
   g_io_win32_destroy
 };
 
-static guint
-g_io_win32_add_watch (GIOChannel    *channel,
-                     gint           priority,
-                     GIOCondition   condition,
-                     GIOFunc        func,
-                     gpointer       user_data,
-                     GDestroyNotify notify,
-                     int (*reader) (int, guchar *, int))
+static GSource *
+g_io_win32_create_watch (GIOChannel    *channel,
+                        GIOCondition   condition,
+                        int (*reader) (int, guchar *, int)))
 {
-  GIOWin32Watch *watch = g_new (GIOWin32Watch, 1);
   GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
+  GIOWin32Watch *watch;
+  GSource *source;
+
+  source = g_source_new (&win32_watch_funcs, sizeof (GIOWin32Watch));
+  watch = (GIOWin32Watch *)source;
   
   watch->channel = channel;
   g_io_channel_ref (channel);
   
-  watch->callback = func;
   watch->condition = condition;
   
   if (win32_channel->data_avail_event == NULL)
@@ -407,16 +400,15 @@ g_io_win32_add_watch (GIOChannel    *channel,
   watch->pollfd.events = condition;
   
   if (win32_channel->debug)
-    g_print ("g_io_win32_add_watch: fd:%d handle:%#x\n",
+    g_print ("g_io_win32_create_watch: fd:%d handle:%#x\n",
             win32_channel->fd, watch->pollfd.fd);
   
   if (win32_channel->thread_id == 0)
     create_reader_thread (win32_channel, reader);
 
-  g_main_add_poll (&watch->pollfd, priority);
+  g_source_add_poll (source, &watch->pollfd);
   
-  return g_source_add (priority, TRUE, &win32_watch_funcs, watch,
-                      user_data, notify);
+  return source;
 }
 
 static GIOError
@@ -490,29 +482,27 @@ g_io_win32_free (GIOChannel *channel)
   g_free (win32_channel);
 }
 
-static guint
-g_io_win32_msg_add_watch (GIOChannel    *channel,
-                         gint           priority,
-                         GIOCondition   condition,
-                         GIOFunc        func,
-                         gpointer       user_data,
-                         GDestroyNotify notify)
+static GSource *
+g_io_win32_msg_create_watch (GIOChannel    *channel,
+                            GIOCondition   condition)
 {
-  GIOWin32Watch *watch = g_new (GIOWin32Watch, 1);
+  GIOWin32Watch *watch;
+  GSource *source;
+
+  source = g_source_new (&win32_watch_funcs, sizeof (GIOWin32Watch));
+  watch = (GIOWin32Watch *)source;
   
   watch->channel = channel;
   g_io_channel_ref (channel);
   
-  watch->callback = func;
   watch->condition = condition;
   
   watch->pollfd.fd = G_WIN32_MSG_HANDLE;
   watch->pollfd.events = condition;
   
-  g_main_add_poll (&watch->pollfd, priority);
+  g_source_add_poll (source, &watch->pollfd);
   
-  return g_source_add (priority, TRUE, &win32_watch_funcs,
-                      watch, user_data, notify);
+  return source;
 }
 
 static GIOError
@@ -653,16 +643,11 @@ fd_reader (int     fd,
   return read (fd, buf, len);
 }
 
-static guint
-g_io_win32_fd_add_watch (GIOChannel    *channel,
-                        gint           priority,
-                        GIOCondition   condition,
-                        GIOFunc        func,
-                        gpointer       user_data,
-                        GDestroyNotify notify)
+static GSource *
+g_io_win32_fd_create_watch (GIOChannel    *channel,
+                           GIOCondition   condition)
 {
-  return g_io_win32_add_watch (channel, priority, condition,
-                              func, user_data, notify, fd_reader);
+  return g_io_win32_create_watch (channel, condition, fd_reader);
 }
 
 static GIOError
@@ -752,16 +737,11 @@ sock_reader (int     fd,
   return recv (fd, buf, len, 0);
 }
 
-static guint
-g_io_win32_sock_add_watch (GIOChannel    *channel,
-                          gint           priority,
-                          GIOCondition   condition,
-                          GIOFunc        func,
-                          gpointer       user_data,
-                          GDestroyNotify notify)
+static GSource *
+g_io_win32_sock_create_watch (GIOChannel    *channel,
+                             GIOCondition   condition)
 {
-  return g_io_win32_add_watch (channel, priority, condition,
-                              func, user_data, notify, sock_reader);
+  return g_io_win32_add_watch (channel, condition, sock_reader);
 }
 
 static GIOFuncs win32_channel_msg_funcs = {
@@ -769,7 +749,7 @@ static GIOFuncs win32_channel_msg_funcs = {
   g_io_win32_msg_write,
   g_io_win32_no_seek,
   g_io_win32_msg_close,
-  g_io_win32_msg_add_watch,
+  g_io_win32_msg_create_watch,
   g_io_win32_free
 };
 
@@ -778,7 +758,7 @@ static GIOFuncs win32_channel_fd_funcs = {
   g_io_win32_fd_write,
   g_io_win32_fd_seek,
   g_io_win32_fd_close,
-  g_io_win32_fd_add_watch,
+  g_io_win32_fd_create_watch,
   g_io_win32_free
 };
 
@@ -787,7 +767,7 @@ static GIOFuncs win32_channel_sock_funcs = {
   g_io_win32_sock_write,
   g_io_win32_no_seek,
   g_io_win32_sock_close,
-  g_io_win32_sock_add_watch,
+  g_io_win32_sock_create_watch,
   g_io_win32_free
 };
 
index ad03a35c0457d47549df15fd45fe75cc9a6505f0..d87022c30c62c90c60e0be4331d8a78dbf5709e2 100644 (file)
@@ -63,9 +63,9 @@
 
 /* Types */
 
-typedef struct _GTimeoutData GTimeoutData;
-typedef struct _GSource GSource;
+typedef struct _GTimeoutSource GTimeoutSource;
 typedef struct _GPollRec GPollRec;
+typedef struct _GSourceCallback GSourceCallback;
 
 typedef enum
 {
@@ -73,23 +73,76 @@ typedef enum
   G_SOURCE_CAN_RECURSE = 1 << (G_HOOK_FLAG_USER_SHIFT + 1)
 } GSourceFlags;
 
-struct _GSource
+struct _GMainContext
 {
-  GHook hook;
-  gint priority;
-  gpointer source_data;
+#ifdef G_THREADS_ENABLED
+  /* The following lock is used for both the list of sources
+   * and the list of poll records
+   */
+  GMutex *mutex;
+  GThread *thread;
+#endif  
+
+  GPtrArray *pending_dispatches;
+  gint timeout;                        /* Timeout for current iteration */
+
+  guint next_id;
+  GSource *source_list;
+  gint in_check_or_prepare;
+
+  GPollRec *poll_records;
+  GPollRec *poll_free_list;
+  GMemChunk *poll_chunk;
+  guint n_poll_records;
+  GPollFD *cached_poll_array;
+  gint cached_poll_array_size;
+
+#ifdef G_THREADS_ENABLED  
+#ifndef G_OS_WIN32
+/* this pipe is used to wake up the main loop when a source is added.
+ */
+  gint wake_up_pipe[2];
+#else /* G_OS_WIN32 */
+  HANDLE wake_up_semaphore;
+#endif /* G_OS_WIN32 */
+
+  GPollFD wake_up_rec;
+  gboolean poll_waiting;
+
+/* Flag indicating whether the set of fd's changed during a poll */
+  gboolean poll_changed;
+#endif /* G_THREADS_ENABLED */
+
+  GPollFunc poll_func;
+
+  GTimeVal current_time;
+  gboolean time_is_current;
+};
+
+struct _GSourceCallback
+{
+  guint ref_count;
+  GSourceFunc func;
+  gpointer    data;
+  GDestroyNotify notify;
 };
 
 struct _GMainLoop
 {
+  GMainContext *context;
   gboolean is_running;
+
+#ifdef G_THREADS_ENABLED
+  GMutex *mutex;
+  GCond *sem_cond;
+#endif G_THREADS_ENABLED  
 };
 
-struct _GTimeoutData
+struct _GTimeoutSource
 {
+  GSource     source;
   GTimeVal    expiration;
   gint        interval;
-  GSourceFunc callback;
 };
 
 struct _GPollRec
@@ -99,57 +152,71 @@ struct _GPollRec
   GPollRec *next;
 };
 
+#ifdef G_THREADS_ENABLED
+#define LOCK_CONTEXT(context) g_mutex_lock(context->mutex)
+#define UNLOCK_CONTEXT(context) g_mutex_unlock(context->mutex)
+#define LOCK_LOOP(loop) g_mutex_lock(loop->mutex)
+#define UNLOCK_LOOP(loop) g_mutex_unlock(loop->mutex)
+#else
+#define LOCK_CONTEXT(context) (void)0
+#define UNLOCK_CONTEXT(context) (void)0
+#define LOCK_LOOP(context) (void)0
+#define UNLOCK_LOOP(context) (void)0
+#endif
+
+#define SOURCE_DESTROYED(source) (((source)->flags & G_HOOK_FLAG_ACTIVE) == 0)
+
+#define SOURCE_UNREF(source, context)                       \
+   G_STMT_START {                                           \
+    if ((source)->ref_count > 1)                            \
+      (source)->ref_count--;                                \
+    else                                                    \
+      g_source_unref_internal ((source), (context), TRUE);  \
+   } G_STMT_END
+
+
 /* Forward declarations */
 
-static gint     g_source_compare          (GHook      *a,
-                                          GHook      *b);
-static void     g_source_destroy_func     (GHookList  *hook_list,
-                                          GHook      *hook);
-static void     g_main_poll               (gint      timeout,
-                                          gboolean  use_priority, 
-                                          gint      priority);
-static void     g_main_add_poll_unlocked  (gint      priority,
-                                          GPollFD  *fd);
-static void     g_main_wakeup             (void);
-
-static gboolean g_timeout_prepare      (gpointer  source_data, 
-                                       GTimeVal *current_time,
-                                       gint     *timeout,
-                                       gpointer  user_data);
-static gboolean g_timeout_check        (gpointer  source_data,
-                                       GTimeVal *current_time,
-                                       gpointer  user_data);
-static gboolean g_timeout_dispatch     (gpointer  source_data,
-                                       GTimeVal *dispatch_time,
-                                       gpointer  user_data);
-static gboolean g_idle_prepare         (gpointer  source_data, 
-                                       GTimeVal *current_time,
-                                       gint     *timeout,
-                                       gpointer  user_data);
-static gboolean g_idle_check           (gpointer  source_data,
-                                       GTimeVal *current_time,
-                                       gpointer  user_data);
-static gboolean g_idle_dispatch        (gpointer  source_data,
-                                       GTimeVal *dispatch_time,
-                                       gpointer  user_data);
-
-/* Data */
-
-static GSList *pending_dispatches = NULL;
-static GHookList source_list = { 0 };
-static gint in_check_or_prepare = 0;
-
-/* The following lock is used for both the list of sources
- * and the list of poll records
- */
+static void g_source_unref_internal             (GSource      *source,
+                                                GMainContext *context,
+                                                gboolean      have_lock);
+static void g_source_destroy_internal           (GSource      *source,
+                                                GMainContext *context,
+                                                gboolean      have_lock);
+static void g_main_context_poll                 (GMainContext *context,
+                                                gint          timeout,
+                                                gint          priority,
+                                                GPollFD      *fds,
+                                                gint          n_fds);
+static void g_main_context_add_poll_unlocked    (GMainContext *context,
+                                                gint          priority,
+                                                GPollFD      *fd);
+static void g_main_context_remove_poll_unlocked (GMainContext *context,
+                                                GPollFD      *fd);
+static void g_main_context_wakeup               (GMainContext *context);
+
+static gboolean g_timeout_prepare  (GSource     *source,
+                                   gint        *timeout);
+static gboolean g_timeout_check    (GSource     *source);
+static gboolean g_timeout_dispatch (GSource     *source,
+                                   GSourceFunc  callback,
+                                   gpointer     user_data);
+static gboolean g_idle_prepare     (GSource     *source,
+                                   gint        *timeout);
+static gboolean g_idle_check       (GSource     *source);
+static gboolean g_idle_dispatch    (GSource     *source,
+                                   GSourceFunc  callback,
+                                   gpointer     user_data);
+
 G_LOCK_DEFINE_STATIC (main_loop);
+static GMainContext *default_main_context;
 
 static GSourceFuncs timeout_funcs =
 {
   g_timeout_prepare,
   g_timeout_check,
   g_timeout_dispatch,
-  g_free,
+  NULL
 };
 
 static GSourceFuncs idle_funcs =
@@ -157,36 +224,16 @@ static GSourceFuncs idle_funcs =
   g_idle_prepare,
   g_idle_check,
   g_idle_dispatch,
-  NULL,
+  NULL
 };
 
-static GPollRec *poll_records = NULL;
-static GPollRec *poll_free_list = NULL;
-static GMemChunk *poll_chunk;
-static guint n_poll_records = 0;
-
-#ifdef G_THREADS_ENABLED
-#ifndef G_OS_WIN32
-/* this pipe is used to wake up the main loop when a source is added.
- */
-static gint wake_up_pipe[2] = { -1, -1 };
-#else /* G_OS_WIN32 */
-static HANDLE wake_up_semaphore = NULL;
-#endif /* G_OS_WIN32 */
-static GPollFD wake_up_rec;
-static gboolean poll_waiting = FALSE;
-
-/* Flag indicating whether the set of fd's changed during a poll */
-static gboolean poll_changed = FALSE;
-#endif /* G_THREADS_ENABLED */
-
 #ifdef HAVE_POLL
 /* SunOS has poll, but doesn't provide a prototype. */
 #  if defined (sun) && !defined (__SVR4)
 extern gint poll (GPollFD *ufds, guint nfsd, gint timeout);
 #  endif  /* !sun */
-static GPollFunc poll_func = (GPollFunc) poll;
 #else  /* !HAVE_POLL */
+
 #ifdef G_OS_WIN32
 
 static gint
@@ -436,656 +483,1723 @@ g_poll (GPollFD *fds,
 
 #endif /* !G_OS_WIN32 */
 
-static GPollFunc poll_func = g_poll;
 #endif /* !HAVE_POLL */
 
-/* Hooks for adding to the main loop */
-
-/* Use knowledge of insert_sorted algorithm here to make
- * sure we insert at the end of equal priority items
+/* Called to clean up when a thread terminates
  */
-static gint
-g_source_compare (GHook *a,
-                 GHook *b)
-{
-  GSource *source_a = (GSource *)a;
-  GSource *source_b = (GSource *)b;
-
-  return (source_a->priority < source_b->priority) ? -1 : 1;
-}
-
-/* HOLDS: main_loop lock */
 static void
-g_source_destroy_func (GHookList *hook_list,
-                      GHook     *hook)
+g_main_context_destroy (GMainContext *context)
 {
-  GSource *source = (GSource*) hook;
-  GDestroyNotify destroy;
+  GSource *source;
 
-  G_UNLOCK (main_loop);
+  /* We need the lock here only because g_source_destroy expects
+   * to be able to unlock when destroying the source's data
+   */
+  LOCK_CONTEXT (context);
+  source = context->source_list;
+  while (source)
+    {
+      GSource *next = source->next;
+      g_source_destroy_internal (source, context, TRUE);
+      source = next;
+    }
+  UNLOCK_CONTEXT (context);
 
-  destroy = hook->destroy;
-  if (destroy)
-    destroy (hook->data);
+#ifdef G_THREADS_ENABLED  
+  g_mutex_free (context->mutex);
+#endif
 
-  destroy = ((GSourceFuncs*) hook->func)->destroy;
-  if (destroy)
-    destroy (source->source_data);
+  g_ptr_array_free (context->pending_dispatches, TRUE);
+  g_free (context->cached_poll_array);
+  
+  g_mem_chunk_destroy (context->poll_chunk);
 
-  G_LOCK (main_loop);
+  if (g_thread_supported())
+    {
+#ifndef G_OS_WIN32
+      close (context->wake_up_pipe[0]);
+      close (context->wake_up_pipe[1]);
+#else
+      CloseHandle (context->wake_up_semaphore);
+#endif
+    }
+  
+  g_free (context);
 }
 
-guint 
-g_source_add (gint           priority,
-             gboolean       can_recurse,
-             GSourceFuncs  *funcs,
-             gpointer       source_data, 
-             gpointer       user_data,
-             GDestroyNotify notify)
-{
-  guint return_val;
-  GSource *source;
+/**
+ * g_main_context_get:
+ * @thread: a #GThread
+ * 
+ * Retrieves the main loop context for a particular thread. This
+ * will create the main context for the thread if none previously
+ * existed. The context will exist until the thread terminates.
+ * 
+ * Return value: the main loop context for @thread.
+ **/
+GMainContext *
+g_main_context_get (GThread *thread)
+{
+  static GStaticPrivate private_key = G_STATIC_PRIVATE_INIT;
+  GMainContext *context;
 
-  G_LOCK (main_loop);
+  g_return_val_if_fail (thread != NULL, NULL);
+
+  if (g_thread_supported ())
+    context = g_static_private_get_for_thread (&private_key, thread);
+  else
+    context = default_main_context;
 
-  if (!source_list.is_setup)
+  if (!context)
     {
-      g_hook_list_init (&source_list, sizeof (GSource));
+      context = g_new0 (GMainContext, 1);
 
-      source_list.hook_destroy = G_HOOK_DEFERRED_DESTROY;
-      source_list.hook_free = g_source_destroy_func;
-    }
+#ifdef G_THREADS_ENABLED
+      if (g_thread_supported ())
+       context->mutex = g_mutex_new();
 
-  source = (GSource*) g_hook_alloc (&source_list);
-  source->priority = priority;
-  source->source_data = source_data;
-  source->hook.func = funcs;
-  source->hook.data = user_data;
-  source->hook.destroy = notify;
-  
-  g_hook_insert_sorted (&source_list, 
-                       (GHook *)source, 
-                       g_source_compare);
+      context->thread = thread;
+#endif
+      
+      context->next_id = 1;
+      
+      context->source_list = NULL;
 
-  if (can_recurse)
-    source->hook.flags |= G_SOURCE_CAN_RECURSE;
+#if HAVE_POLL
+      context->poll_func = (GPollFunc)poll;
+#else
+      context->poll_func = g_poll;
+#endif
 
-  return_val = source->hook.hook_id;
+      context->cached_poll_array = NULL;
+      context->cached_poll_array_size = 0;
+      
+      context->pending_dispatches = g_ptr_array_new ();
+      
+      context->time_is_current = FALSE;
 
 #ifdef G_THREADS_ENABLED
-  /* Now wake up the main loop if it is waiting in the poll() */
-  g_main_wakeup ();
+      if (g_thread_supported ())
+       {
+#ifndef G_OS_WIN32
+         if (pipe (context->wake_up_pipe) < 0)
+           g_error ("Cannot create pipe main loop wake-up: %s\n",
+                    g_strerror (errno));
+         
+         context->wake_up_rec.fd = context->wake_up_pipe[0];
+         context->wake_up_rec.events = G_IO_IN;
+         g_main_context_add_poll_unlocked (context, 0, &context->wake_up_rec);
+#else
+         if ((context->wake_up_semaphore = CreateSemaphore (NULL, 0, 100, NULL)) == NULL)
+           g_error ("Cannot create wake-up semaphore: %s", g_win32_error_message (GetLastError ()));
+         context->wake_up_rec.fd = (gint) context->wake_up_semaphore;
+         context->wake_up_rec.events = G_IO_IN;
+#ifdef G_MAIN_POLL_DEBUG
+         g_print ("wake-up semaphore: %#x\n", (guint) context->wake_up_semaphore);
 #endif
-  
-  G_UNLOCK (main_loop);
+         g_main_context_add_poll_unlocked (context, 0, &context->wake_up_rec);
+#endif
+       }
+#endif
+
+      if (g_thread_supported ())
+       g_static_private_set_for_thread (&private_key, thread,
+                                        context,
+                                        (GDestroyNotify)g_main_context_destroy);
+      else
+       default_main_context = context;
+    }
 
-  return return_val;
+  return context;
 }
 
-gboolean
-g_source_remove (guint tag)
+/**
+ * g_main_context_default:
+ * 
+ * Return the default main context. This is the main context used
+ * for main loop functions when a main loop is not explicitly
+ * specified.
+ * 
+ * Return value: the default main context.
+ **/
+GMainContext *
+g_main_context_default (void)
 {
-  GHook *hook;
-
-  g_return_val_if_fail (tag > 0, FALSE);
-
+  /* Slow, but safe */
+  
   G_LOCK (main_loop);
 
-  hook = g_hook_get (&source_list, tag);
-  if (hook)
-    g_hook_destroy_link (&source_list, hook);
+  if (!default_main_context)
+    default_main_context = g_main_context_get (g_thread_self ());
 
   G_UNLOCK (main_loop);
 
-  return hook != NULL;
+  return default_main_context;
 }
 
-gboolean
-g_source_remove_by_user_data (gpointer user_data)
+/* Hooks for adding to the main loop */
+
+/**
+ * g_source_new:
+ * @source_funcs: structure containing functions that implement
+ *                the sources behavior.
+ * @struct_size: size of the #GSource structure to create
+ * 
+ * Create a new GSource structure. The size is specified to
+ * allow creating structures derived from GSource that contain
+ * additional data. The size passed in must be at least
+ * sizeof(GSource).
+ * 
+ * The source will not initially be associated with any #GMainContext
+ * and must be added to one with g_source_add() before it will be
+ * executed.
+ * 
+ * Return value: the newly create #GSource
+ **/
+GSource *
+g_source_new (GSourceFuncs *source_funcs,
+             guint         struct_size)
 {
-  GHook *hook;
+  GSource *source;
+
+  g_return_val_if_fail (source_funcs != NULL, NULL);
+  g_return_val_if_fail (struct_size >= sizeof (GSource), NULL);
   
-  G_LOCK (main_loop);
+  source = (GSource*) g_malloc0 (struct_size);
+
+  source->source_funcs = source_funcs;
+  source->ref_count = 1;
   
-  hook = g_hook_find_data (&source_list, TRUE, user_data);
-  if (hook)
-    g_hook_destroy_link (&source_list, hook);
+  source->priority = G_PRIORITY_DEFAULT;
 
-  G_UNLOCK (main_loop);
+  source->flags = G_HOOK_FLAG_ACTIVE;
 
-  return hook != NULL;
+  /* NULL/0 initialization for all other fields */
+  
+  return source;
 }
 
-static gboolean
-g_source_find_source_data (GHook       *hook,
-                          gpointer      data)
+/* Holds context's lock
+ */
+static void
+g_source_list_add (GSource      *source,
+                  GMainContext *context)
 {
-  GSource *source = (GSource *)hook;
+  GSource *tmp_source, *last_source;
+  
+  last_source = NULL;
+  tmp_source = context->source_list;
+  while (tmp_source && tmp_source->priority <= source->priority)
+    {
+      last_source = tmp_source;
+      tmp_source = tmp_source->next;
+    }
 
-  return (source->source_data == data);
+  source->next = tmp_source;
+  if (tmp_source)
+    tmp_source->prev = source;
+  
+  source->prev = last_source;
+  if (last_source)
+    last_source->next = source;
+  else
+    context->source_list = source;
 }
 
-gboolean
-g_source_remove_by_source_data (gpointer source_data)
+/* Holds context's lock
+ */
+static void
+g_source_list_remove (GSource      *source,
+                     GMainContext *context)
 {
-  GHook *hook;
-
-  G_LOCK (main_loop);
-
-  hook = g_hook_find (&source_list, TRUE, 
-                     g_source_find_source_data, source_data);
-  if (hook)
-    g_hook_destroy_link (&source_list, hook);
+  if (source->prev)
+    source->prev->next = source->next;
+  else
+    context->source_list = source->next;
 
-  G_UNLOCK (main_loop);
+  if (source->next)
+    source->next->prev = source->prev;
 
-  return hook != NULL;
+  source->prev = NULL;
+  source->next = NULL;
 }
 
-static gboolean
-g_source_find_funcs_user_data (GHook   *hook,
-                              gpointer data)
+/**
+ * g_source_attach:
+ * @source: a #GSource
+ * @context: a #GMainContext (if %NULL, the default context will be used)
+ * 
+ * Adds a #GSource to a @context so that it will be executed within
+ * that context.
+ *
+ * Return value: the ID for the source within the #GMainContext
+ **/
+guint
+g_source_attach (GSource      *source,
+                GMainContext *context)
 {
-  gpointer *d = data;
+  guint result = 0;
+  GSList *tmp_list;
 
-  return hook->func == d[0] && hook->data == d[1];
-}
+  g_return_val_if_fail (source->context == NULL, 0);
+  g_return_val_if_fail (!SOURCE_DESTROYED (source), 0);
+  
+  if (!context)
+    context = g_main_context_default ();
 
-gboolean
-g_source_remove_by_funcs_user_data (GSourceFuncs *funcs,
-                                   gpointer      user_data)
-{
-  gpointer d[2];
-  GHook *hook;
+  LOCK_CONTEXT (context);
 
-  g_return_val_if_fail (funcs != NULL, FALSE);
+  source->context = context;
+  result = source->id = context->next_id++;
 
-  G_LOCK (main_loop);
+  g_source_list_add (source, context);
 
-  d[0] = funcs;
-  d[1] = user_data;
+  tmp_list = source->poll_fds;
+  while (tmp_list)
+    {
+      g_main_context_add_poll_unlocked (context, source->priority, tmp_list->data);
+      tmp_list = tmp_list->next;
+    }
 
-  hook = g_hook_find (&source_list, TRUE,
-                     g_source_find_funcs_user_data, d);
-  if (hook)
-    g_hook_destroy_link (&source_list, hook);
+#ifdef G_THREADS_ENABLED
+  /* Now wake up the main loop if it is waiting in the poll() */
+  g_main_context_wakeup (context);
+#endif
 
-  G_UNLOCK (main_loop);
+  UNLOCK_CONTEXT (context);
 
-  return hook != NULL;
+  return result;
 }
 
-void
-g_get_current_time (GTimeVal *result)
+static void
+g_source_destroy_internal (GSource      *source,
+                          GMainContext *context,
+                          gboolean      have_lock)
 {
-#ifndef G_OS_WIN32
-  struct timeval r;
-
-  g_return_if_fail (result != NULL);
+  if (!have_lock)
+    LOCK_CONTEXT (context);
+  
+  if (!SOURCE_DESTROYED (source))
+    {
+      gpointer old_cb_data;
+      GSourceCallbackFuncs *old_cb_funcs;
+      
+      source->flags &= ~G_HOOK_FLAG_ACTIVE;
 
-  /*this is required on alpha, there the timeval structs are int's
-    not longs and a cast only would fail horribly*/
-  gettimeofday (&r, NULL);
-  result->tv_sec = r.tv_sec;
-  result->tv_usec = r.tv_usec;
-#else
-  FILETIME filetime;
-  gint64 t;
+      old_cb_data = source->callback_data;
+      old_cb_funcs = source->callback_funcs;
 
-  g_return_if_fail (result != NULL);
+      source->callback_data = NULL;
+      source->callback_funcs = NULL;
 
-  GetSystemTimeAsFileTime (&filetime);
-  t = ((gint64) filetime.dwLowDateTime) +
-    ((gint64) filetime.dwHighDateTime) * G_GINT64_CONSTANT (0x100000000);
+      if (old_cb_funcs)
+       {
+         UNLOCK_CONTEXT (context);
+         old_cb_funcs->unref (old_cb_data);
+         LOCK_CONTEXT (context);
+       }
+      
+      g_source_unref_internal (source, context, TRUE);
+    }
 
-  result->tv_sec = (t - G_GINT64_CONSTANT (0x19db1ded53e8000)) / 10000000;
-  result->tv_usec = (t % 10000000) / 10;
-#endif
+  if (!have_lock)
+    UNLOCK_CONTEXT (context);
 }
 
-/* Running the main loop */
+/**
+ * g_source_destroy:
+ * @source: a #GSource
+ * 
+ * Remove a source from its #GMainContext, if any, and mark it as
+ * destroyed.  The source cannot be subsequently added to another
+ * context.
+ **/
+void
+g_source_destroy (GSource *source)
+{
+  GMainContext *context;
+  
+  g_return_if_fail (source != NULL);
+  
+  context = source->context;
+  
+  if (context)
+    g_source_destroy_internal (source, context, FALSE);
+  else
+    source->flags &= ~G_HOOK_FLAG_ACTIVE;
+}
 
-/* HOLDS: main_loop_lock */
-static void
-g_main_dispatch (GTimeVal *dispatch_time)
+/**
+ * g_source_get_id:
+ * @source: a #GSource
+ * 
+ * Return the numeric ID for a particular source. The ID of a source
+ * is unique within a particular main loop context. The reverse
+ * mapping from ID to source is done by g_main_context_find_source_by_id().
+ *
+ * Return value: the ID for the source
+ **/
+guint
+g_source_get_id (GSource *source)
 {
-  while (pending_dispatches != NULL)
-    {
-      gboolean need_destroy;
-      GSource *source = pending_dispatches->data;
-      GSList *tmp_list;
+  guint result;
+  
+  g_return_val_if_fail (source != NULL, 0);
+  g_return_val_if_fail (source->context != NULL, 0);
+
+  LOCK_CONTEXT (source->context);
+  result = source->id;
+  UNLOCK_CONTEXT (source->context);
+  
+  return result;
+}
 
-      tmp_list = pending_dispatches;
-      pending_dispatches = g_slist_remove_link (pending_dispatches, pending_dispatches);
-      g_slist_free_1 (tmp_list);
+/**
+ * g_source_get_context:
+ * @source: a #GSource
+ * 
+ * Get the #GMainContext with which the source is associated.
+ * Calling this function on a destroyed source is an error.
+ * 
+ * Return value: the #GMainContext with which the source is associated,
+ *               or %NULL if the context has not yet been added
+ *               to a source.
+ **/
+GMainContext *
+g_source_get_context (GSource *source)
+{
+  g_return_val_if_fail (!SOURCE_DESTROYED (source), NULL);
 
-      if (G_HOOK_IS_VALID (source))
-       {
-         gboolean was_in_call;
-         gpointer hook_data = source->hook.data;
-         gpointer source_data = source->source_data;
-         gboolean (*dispatch) (gpointer,
-                               GTimeVal *,
-                               gpointer);
+  return source->context;
+}
 
-         dispatch = ((GSourceFuncs *) source->hook.func)->dispatch;
-         
-         was_in_call = G_HOOK_IN_CALL (source);
-         source->hook.flags |= G_HOOK_FLAG_IN_CALL;
+/**
+ * g_main_source_add_poll:
+ * @source:a #GSource 
+ * @fd: a #GPollFD structure holding information about a file
+ *      descriptor to watch.
+ * 
+ * Add a file descriptor to the set of file descriptors polled * for
+ * this source. This is usually combined with g_source_new() to add an
+ * event source. The event source's check function will typically test
+ * the revents field in the #GPollFD struct and return %TRUE if events need
+ * to be processed.
+ **/
+void
+g_source_add_poll (GSource *source,
+                  GPollFD *fd)
+{
+  GMainContext *context;
+  
+  g_return_if_fail (source != NULL);
+  g_return_if_fail (fd != NULL);
 
-         G_UNLOCK (main_loop);
-         need_destroy = ! dispatch (source_data,
-                                    dispatch_time,
-                                    hook_data);
-         G_LOCK (main_loop);
+  context = source->context;
 
-         if (!was_in_call)
-           source->hook.flags &= ~G_HOOK_FLAG_IN_CALL;
-         
-         if (need_destroy && G_HOOK_IS_VALID (source))
-           g_hook_destroy_link (&source_list, (GHook *) source);
-       }
+  if (context)
+    LOCK_CONTEXT (context);
+  
+  source->poll_fds = g_slist_prepend (source->poll_fds, fd);
 
-      g_hook_unref (&source_list, (GHook*) source);
+  if (context)
+    {
+      g_main_context_add_poll_unlocked (context, source->priority, fd);
+      UNLOCK_CONTEXT (context);
     }
 }
 
-/* g_main_iterate () runs a single iteration of the mainloop, or,
- * if !dispatch checks to see if any sources need dispatching.
- * basic algorithm for dispatch=TRUE:
- *
- * 1) while the list of currently pending sources is non-empty,
- *    we call (*dispatch) on those that are !IN_CALL or can_recurse,
- *    removing sources from the list after each returns.
- *    the return value of (*dispatch) determines whether the source
- *    itself is kept alive.
- *
- * 2) call (*prepare) for sources that are not yet SOURCE_READY and
- *    are !IN_CALL or can_recurse. a return value of TRUE determines
- *    that the source would like to be dispatched immediatedly, it
- *    is then flagged as SOURCE_READY.
- *
- * 3) poll with the pollfds from all sources at the priority of the
- *    first source flagged as SOURCE_READY. if there are any sources
- *    flagged as SOURCE_READY, we use a timeout of 0 or the minimum
- *    of all timouts otherwise.
- *
- * 4) for each source !IN_CALL or can_recurse, if SOURCE_READY or
- *    (*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.
- *
- */
-static gboolean
-g_main_iterate (gboolean block,
-               gboolean dispatch)
+/**
+ * g_source_set_callback_indirect:
+ * @source: the source
+ * @callback_data: pointer to callback data "object"
+ * @callback_funcs: functions for reference counting callback_data
+ *                  and getting the callback and data
+ * 
+ * Set the callback function storing the data as a refcounted callback
+ * "object". This is used to implement g_source_set_callback_closure()
+ * and internally. Note that calling g_source_set_callback_indirect() assumes
+ * an initial reference count on @callback_data, and thus
+ * @callback_funcs->unref will eventually be called once more
+ * than @callback_funcs->ref.
+ **/
+void
+g_source_set_callback_indirect (GSource              *source,
+                               gpointer              callback_data,
+                               GSourceCallbackFuncs *callback_funcs)
 {
-  GHook *hook;
-  GTimeVal current_time  = { 0, 0 };
-  gint n_ready = 0;
-  gint current_priority = 0;
-  gint timeout;
-  gboolean retval = FALSE;
+  GMainContext *context;
+  gpointer old_cb_data;
+  GSourceCallbackFuncs *old_cb_funcs;
+  
+  g_return_if_fail (source != NULL);
+  g_return_if_fail (callback_funcs != NULL || callback_data == NULL);
 
-  g_return_val_if_fail (!block || dispatch, FALSE);
+  context = source->context;
 
-  g_get_current_time (&current_time);
+  if (context)
+    LOCK_CONTEXT (context);
 
-  G_LOCK (main_loop);
+  old_cb_data = source->callback_data;
+  old_cb_funcs = source->callback_funcs;
 
-#ifdef G_THREADS_ENABLED
-  if (poll_waiting)
-    {
-      g_warning("g_main_iterate(): main loop already active in another thread");
-      G_UNLOCK (main_loop);
-      return FALSE;
-    }
-#endif /* G_THREADS_ENABLED */
+  source->callback_data = callback_data;
+  source->callback_funcs = callback_funcs;
   
-  /* If recursing, finish up current dispatch, before starting over */
-  if (pending_dispatches)
-    {
-      if (dispatch)
-       g_main_dispatch (&current_time);
-      
-      G_UNLOCK (main_loop);
+  if (context)
+    UNLOCK_CONTEXT (context);
+  
+  if (old_cb_funcs)
+    old_cb_funcs->unref (old_cb_data);
+}
 
-      return TRUE;
-    }
+static void
+g_source_callback_ref (gpointer cb_data)
+{
+  GSourceCallback *callback = cb_data;
 
-  /* Prepare all sources */
+  callback->ref_count++;
+}
 
-  timeout = block ? -1 : 0;
-  
-  hook = g_hook_first_valid (&source_list, TRUE);
-  while (hook)
+
+static void
+g_source_callback_unref (gpointer cb_data)
+{
+  GSourceCallback *callback = cb_data;
+
+  callback->ref_count--;
+  if (callback->ref_count == 0)
     {
-      GSource *source = (GSource*) hook;
-      gint source_timeout = -1;
+      if (callback->notify)
+       callback->notify (callback->data);
+    }
+}
 
-      if ((n_ready > 0) && (source->priority > current_priority))
-       {
-         g_hook_unref (&source_list, hook);
-         break;
-       }
-      if (G_HOOK_IN_CALL (hook) && !(hook->flags & G_SOURCE_CAN_RECURSE))
-       {
-         hook = g_hook_next_valid (&source_list, hook, TRUE);
-         continue;
-       }
+static void
+g_source_callback_get (gpointer     cb_data,
+                      GSourceFunc *func,
+                      gpointer    *data)
+{
+  GSourceCallback *callback = cb_data;
 
-      if (!(hook->flags & G_SOURCE_READY))
-       {
-         gboolean (*prepare)  (gpointer  source_data, 
-                               GTimeVal *current_time,
-                               gint     *timeout,
-                               gpointer  user_data);
+  *func = callback->func;
+  *data = callback->data;
+}
 
-         prepare = ((GSourceFuncs *) hook->func)->prepare;
-         in_check_or_prepare++;
-         G_UNLOCK (main_loop);
+static GSourceCallbackFuncs g_source_callback_funcs = {
+  g_source_callback_ref,
+  g_source_callback_unref,
+  g_source_callback_get,
+};
 
-         if ((*prepare) (source->source_data, &current_time, &source_timeout, source->hook.data))
-           hook->flags |= G_SOURCE_READY;
-         
-         G_LOCK (main_loop);
-         in_check_or_prepare--;
-       }
+/**
+ * g_source_set_callback:
+ * @source: the source
+ * @func: a callback function
+ * @data: the data to pass to callback function
+ * @notify: a function to call when @data is no longer in use, or %NULL.
+ * 
+ * Set the callback function for a source.
+ **/
+void
+g_source_set_callback (GSource        *source,
+                      GSourceFunc     func,
+                      gpointer        data,
+                      GDestroyNotify  notify)
+{
+  GSourceCallback *new_callback;
 
-      if (hook->flags & G_SOURCE_READY)
-       {
-         if (!dispatch)
-           {
-             g_hook_unref (&source_list, hook);
-             G_UNLOCK (main_loop);
+  g_return_if_fail (source != NULL);
 
-             return TRUE;
-           }
-         else
+  new_callback = g_new (GSourceCallback, 1);
+  
+  new_callback->func = func;
+  new_callback->data = data;
+  new_callback->notify = notify;
+
+  g_source_set_callback_indirect (source, new_callback, &g_source_callback_funcs);
+}
+
+/**
+ * g_source_set_priority:
+ * @source: a #GSource
+ * @priority: the new priority.
+ * 
+ * Set the priority of a source. While the main loop is being
+ * run, a source will 
+ **/
+void
+g_source_set_priority (GSource  *source,
+                      gint      priority)
+{
+  GSList *tmp_list;
+  GMainContext *context;
+  
+  g_return_if_fail (source != NULL);
+
+  context = source->context;
+
+  if (context)
+    LOCK_CONTEXT (context);
+  
+  source->priority = priority;
+
+  if (context)
+    {
+      source->next = NULL;
+      source->prev = NULL;
+      
+      tmp_list = source->poll_fds;
+      while (tmp_list)
+       {
+         g_main_context_remove_poll_unlocked (context, tmp_list->data);
+         g_main_context_add_poll_unlocked (context, priority, tmp_list->data);
+      
+         tmp_list = tmp_list->next;
+       }
+      
+      UNLOCK_CONTEXT (source->context);
+    }
+}
+
+/**
+ * g_source_get_priority:
+ * @source: a #GSource
+ * 
+ * Gets the priority of a surce
+ * 
+ * Return value: the priority of the source
+ **/
+gint
+g_source_get_priority (GSource *source)
+{
+  g_return_val_if_fail (source != NULL, 0);
+
+  return source->priority;
+}
+
+/**
+ * g_source_set_can_recurse:
+ * @source: a #GSource
+ * @can_recurse: whether recursion is allowed for this source
+ * 
+ * Sets whether a source can be called recursively. If @can_recurse is
+ * %TRUE, then while the source is being dispatched then this source
+ * will be processed normally. Otherwise, all processing of this
+ * source is blocked until the dispatch function returns.
+ **/
+void
+g_source_set_can_recurse (GSource  *source,
+                         gboolean  can_recurse)
+{
+  GMainContext *context;
+  
+  g_return_if_fail (source != NULL);
+
+  context = source->context;
+
+  if (context)
+    LOCK_CONTEXT (context);
+  
+  if (can_recurse)
+    source->flags |= G_SOURCE_CAN_RECURSE;
+  else
+    source->flags &= ~G_SOURCE_CAN_RECURSE;
+
+  if (context)
+    UNLOCK_CONTEXT (context);
+}
+
+/**
+ * g_source_get_can_recurse:
+ * @source: a #GSource
+ * 
+ * Checks whether a source is allowed to be called recursively.
+ * see g_source_set_can_recurse.
+ * 
+ * Return value: whether recursion is allowed.
+ **/
+gboolean
+g_source_get_can_recurse (GSource  *source)
+{
+  g_return_val_if_fail (source != NULL, FALSE);
+  
+  return (source->flags & G_SOURCE_CAN_RECURSE) != 0;
+}
+
+/**
+ * g_source_ref:
+ * @source: a #GSource
+ * 
+ * Increases the reference count on a source by one.
+ * 
+ * Return value: @source
+ **/
+GSource *
+g_source_ref (GSource *source)
+{
+  GMainContext *context;
+  
+  g_return_val_if_fail (source != NULL, NULL);
+
+  context = source->context;
+
+  if (context)
+    LOCK_CONTEXT (context);
+
+  source->ref_count++;
+
+  if (context)
+    UNLOCK_CONTEXT (context);
+
+  return source;
+}
+
+/* g_source_unref() but possible to call within context lock
+ */
+static void
+g_source_unref_internal (GSource      *source,
+                        GMainContext *context,
+                        gboolean      have_lock)
+{
+  gpointer cb_data = NULL;
+  GSourceCallbackFuncs *cb_funcs = NULL;
+  GSList *tmp_list;
+
+  g_return_if_fail (source != NULL);
+  
+  if (!have_lock && context)
+    LOCK_CONTEXT (context);
+
+  source->ref_count--;
+  if (source->ref_count == 0)
+    {
+      if (context && !SOURCE_DESTROYED (source))
+       {
+         g_warning (G_STRLOC ": ref_count == 0, but source is still attached to a context!");
+         source->ref_count++;
+       }
+      else
+       {
+         g_source_list_remove (source, context);
+         
+         tmp_list = source->poll_fds;
+         while (tmp_list)
            {
-             n_ready++;
-             current_priority = source->priority;
-             timeout = 0;
+             g_main_context_remove_poll_unlocked (context, tmp_list->data);
+             tmp_list = tmp_list->next;
            }
        }
+    }
+  
+  if (!have_lock && context)
+    UNLOCK_CONTEXT (context);
+
+  if (cb_data)
+    {
+      if (have_lock)
+       UNLOCK_CONTEXT (context);
+      
+      cb_funcs->unref (cb_data);
+
+      if (have_lock)
+       LOCK_CONTEXT (context);
+    }
+}
+
+/**
+ * g_source_unref:
+ * @source: a #GSource
+ * 
+ * Decreases the reference count of a source by one. If the
+ * resulting reference count is zero the source and associated
+ * memory will be destroyed. 
+ **/
+void
+g_source_unref (GSource *source)
+{
+  g_return_if_fail (source != NULL);
+
+  g_source_unref_internal (source, source->context, FALSE);
+}
+
+/**
+ * g_main_context_find_source_by_id:
+ * @context: a #GMainContext (if %NULL, the default context will be used)
+ * @id: the source ID, as returned by g_source_get_id()
+ * 
+ * Finds a #GSource given a pair of context and ID
+ * 
+ * Return value: the #GSource if found, otherwise, %NULL
+ **/
+GSource *
+g_main_context_find_source_by_id (GMainContext *context,
+                                 guint         id)
+{
+  GSource *source;
+  
+  g_return_val_if_fail (id > 0, FALSE);
+
+  if (context == NULL)
+    context = g_main_context_default ();
+  
+  LOCK_CONTEXT (context);
+  
+  source = context->source_list;
+  while (source)
+    {
+      if (!SOURCE_DESTROYED (source) &&
+         source->id == id)
+       break;
+      source = source->next;
+    }
+
+  UNLOCK_CONTEXT (context);
+
+  return source;
+}
+
+/**
+ * g_main_context_find_source_by_funcs_user_data:
+ * @context: a #GMainContext (if %NULL, the default context will be used).
+ * @funcs: the @source_funcs passed to g_source_new().
+ * @user_data: the user data from the callback.
+ * 
+ * Finds a source with the given source functions and user data.  If
+ * multiple sources exist with the same source function and user data,
+ * the first one found will be returned.
+ * 
+ * Return value: the source, if one was found, otherwise %NULL
+ **/
+GSource *
+g_main_context_find_source_by_funcs_user_data (GMainContext *context,
+                                              GSourceFuncs *funcs,
+                                              gpointer      user_data)
+{
+  GSource *source;
+  
+  g_return_val_if_fail (funcs != NULL, FALSE);
+
+  if (context == NULL)
+    context = g_main_context_default ();
+  
+  LOCK_CONTEXT (context);
+
+  source = context->source_list;
+  while (source)
+    {
+      if (!SOURCE_DESTROYED (source) &&
+         source->source_funcs == funcs &&
+         source->callback_data == user_data)
+       break;
+      source = source->next;
+    }
+
+  UNLOCK_CONTEXT (context);
+
+  return source;
+}
+
+/**
+ * g_main_context_find_source_by_user_data:
+ * @context: a #GMainContext
+ * @user_data: the user_data for the callback.
+ * 
+ * Finds a source with the given user data for the callback.  If
+ * multiple sources exist with the same user data, the first
+ * one found will be returned.
+ * 
+ * Return value: the source, if one was found, otherwise %NULL
+ **/
+GSource *
+g_main_context_find_source_by_user_data (GMainContext *context,
+                                        gpointer      user_data)
+{
+  GSource *source;
+  
+  if (context == NULL)
+    context = g_main_context_default ();
+  
+  LOCK_CONTEXT (context);
+
+  source = context->source_list;
+  while (source)
+    {
+      if (!SOURCE_DESTROYED (source) &&
+         source->callback_data == user_data)
+       break;
+      source = source->next;
+    }
+
+  UNLOCK_CONTEXT (context);
+
+  return source;
+}
+
+/**
+ * g_source_remove:
+ * @tag: the id of the source to remove.
+ * 
+ * Removes the source with the given id from the default main
+ * context. The id of a #GSource is given by g_source_get_id(),
+ * or will be returned by the functions g_source_attach(),
+ * g_idle_add(), g_idle_add_full(), g_timeout_add(),
+ * g_timeout_add_full(), g_io_add_watch, and g_io_add_watch_full().
+ *
+ * See also g_source_destroy().
+ *
+ * Return value: %TRUE if the source was found and removed.
+ **/
+gboolean
+g_source_remove (guint tag)
+{
+  GSource *source;
+  
+  g_return_val_if_fail (tag > 0, FALSE);
+
+  source = g_main_context_find_source_by_id (NULL, tag);
+  if (source)
+    g_source_destroy (source);
+
+  return source != NULL;
+}
+
+/**
+ * g_source_remove_by_user_data:
+ * @user_data: the user_data for the callback.
+ * 
+ * Removes a source from the default main loop context given the user
+ * data for the callback. If multiple sources exist with the same user
+ * data, only one will be destroyed.
+ * 
+ * Return value: %TRUE if a source was found and removed. 
+ **/
+gboolean
+g_source_remove_by_user_data (gpointer user_data)
+{
+  GSource *source;
+  
+  source = g_main_context_find_source_by_user_data (NULL, user_data);
+  if (source)
+    {
+      g_source_destroy (source);
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+/**
+ * g_source_remove_by_funcs_user_data:
+ * @funcs: The @source_funcs passed to g_source_new()
+ * @user_data: the user data for the callback
+ * 
+ * Removes a source from the default main loop context given the
+ * source functions and user data. If multiple sources exist with the
+ * same source functions and user data, only one will be destroyed.
+ * 
+ * Return value: %TRUE if a source was found and removed. 
+ **/
+gboolean
+g_source_remove_by_funcs_user_data (GSourceFuncs *funcs,
+                                   gpointer      user_data)
+{
+  GSource *source;
+
+  g_return_val_if_fail (funcs != NULL, FALSE);
+
+  source = g_main_context_find_source_by_funcs_user_data (NULL, funcs, user_data);
+  if (source)
+    {
+      g_source_destroy (source);
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+/**
+ * g_get_current_time:
+ * @result: #GTimeVal structure in which to store current time.
+ * 
+ * Equivalent to Unix's <function>gettimeofday()</function>, but portable
+ **/
+void
+g_get_current_time (GTimeVal *result)
+{
+#ifndef G_OS_WIN32
+  struct timeval r;
+
+  g_return_if_fail (result != NULL);
+
+  /*this is required on alpha, there the timeval structs are int's
+    not longs and a cast only would fail horribly*/
+  gettimeofday (&r, NULL);
+  result->tv_sec = r.tv_sec;
+  result->tv_usec = r.tv_usec;
+#else
+  /* Avoid calling time() except for the first time.
+   * GetTickCount() should be pretty fast and low-level?
+   * I could also use ftime() but it seems unnecessarily overheady.
+   */
+  static DWORD start_tick = 0;
+  static time_t start_time;
+  DWORD tick;
+
+  g_return_if_fail (result != NULL);
+  if (start_tick == 0)
+    {
+      start_tick = GetTickCount ();
+      time (&start_time);
+    }
+
+  tick = GetTickCount ();
+
+  result->tv_sec = (tick - start_tick) / 1000 + start_time;
+  result->tv_usec = ((tick - start_tick) % 1000) * 1000;
+#endif
+}
+
+/* Running the main loop */
+
+/* HOLDS: context's lock */
+static void
+g_main_dispatch (GMainContext *context)
+{
+  gint i;
+
+  for (i = 0; i < context->pending_dispatches->len; i++)
+    {
+      GSource *source = context->pending_dispatches->pdata[i];
+
+      context->pending_dispatches->pdata[i] = NULL;
+      g_assert (source);
+
+      source->flags &= ~G_SOURCE_READY;
+
+      if (!SOURCE_DESTROYED (source))
+       {
+         gboolean was_in_call;
+         gpointer user_data = NULL;
+         GSourceFunc callback = NULL;
+         GSourceCallbackFuncs *cb_funcs;
+         gpointer cb_data;
+         gboolean need_destroy;
+
+         gboolean (*dispatch) (GSource *,
+                               GSourceFunc,
+                               gpointer);
+
+         dispatch = source->source_funcs->dispatch;
+         cb_funcs = source->callback_funcs;
+         cb_data = source->callback_data;
+
+         if (cb_funcs)
+           cb_funcs->ref (cb_data);
+         
+         was_in_call = source->flags & G_HOOK_FLAG_IN_CALL;
+         source->flags |= G_HOOK_FLAG_IN_CALL;
+
+         UNLOCK_CONTEXT (context);
+
+         if (cb_funcs)
+           cb_funcs->get (cb_data, &callback, &user_data);
+
+         need_destroy = ! dispatch (source,
+                                    callback,
+                                    user_data);
+         LOCK_CONTEXT (context);
+
+         if (cb_funcs)
+           cb_funcs->unref (cb_data);
+
+        if (!was_in_call)
+           source->flags &= ~G_HOOK_FLAG_IN_CALL;
+
+         /* Note: this depends on the fact that we can't switch
+          * sources from one main context to another
+          */
+         if (need_destroy && !SOURCE_DESTROYED (source))
+           {
+             g_assert (source->context == context);
+             g_source_destroy_internal (source, context, TRUE);
+           }
+       }
+      
+      SOURCE_UNREF (source, context);
+    }
+
+  g_ptr_array_set_size (context->pending_dispatches, 0);
+}
+
+/* Holds context's lock */
+static inline GSource *
+next_valid_source (GMainContext *context,
+                  GSource      *source)
+{
+  GSource *new_source = source ? source->next : context->source_list;
+
+  while (new_source)
+    {
+      if (!SOURCE_DESTROYED (new_source))
+       {
+         new_source->ref_count++;
+         break;
+       }
+      
+      new_source = new_source->next;
+    }
+
+  if (source)
+    SOURCE_UNREF (source, context);
+         
+  return new_source;
+}
+
+
+/**
+ * g_main_context_prepare:
+ * @context: a #GMainContext
+ * @priority: location to store priority of highest priority
+ *            source already ready.
+ * 
+ * Prepares to poll sources within a main loop. The resulting information
+ * for polling is determined by calling g_main_context_query ().
+ * 
+ * Return value: %TRUE if some source is ready to be dispatched
+ *               prior to polling.
+ **/
+gboolean
+g_main_context_prepare (GMainContext *context,
+                       gint         *priority)
+{
+  gint n_ready = 0;
+  gint current_priority = G_MAXINT;
+  GSource *source;
+
+  if (context == NULL)
+    context = g_main_context_default ();
+  
+  LOCK_CONTEXT (context);
+
+  context->time_is_current = FALSE;
+
+  if (context->in_check_or_prepare)
+    {
+      g_warning ("g_main_context_prepare() called recursively from within a source's check() or "
+                "prepare() member.");
+      return FALSE;
+    }
+
+#ifdef G_THREADS_ENABLED
+  if (context->poll_waiting)
+    {
+      g_warning("g_main_context_prepare(): main loop already active in another thread");
+      UNLOCK_CONTEXT (context);
+      return FALSE;
+    }
+  
+  context->poll_waiting = TRUE;
+#endif G_THREADS_ENABLED  
+
+#if 0
+  /* If recursing, finish up current dispatch, before starting over */
+  if (context->pending_dispatches)
+    {
+      if (dispatch)
+       g_main_dispatch (context, &current_time);
+      
+      UNLOCK_CONTEXT (context);
+      return TRUE;
+    }
+#endif
+
+  /* If recursing, clear list of pending dispatches */
+  g_ptr_array_set_size (context->pending_dispatches, 0);
+  
+  /* Prepare all sources */
+
+  context->timeout = -1;
+  
+  source = next_valid_source (context, NULL);
+  while (source)
+    {
+      gint source_timeout = -1;
+
+      if ((n_ready > 0) && (source->priority > current_priority))
+       {
+         SOURCE_UNREF (source, context);
+         break;
+       }
+      if ((source->flags & G_HOOK_FLAG_IN_CALL) && !(source->flags & G_SOURCE_CAN_RECURSE))
+       goto next;
+
+      if (!(source->flags & G_SOURCE_READY))
+       {
+         gboolean result;
+         gboolean (*prepare)  (GSource  *source, 
+                               gint     *timeout);
+
+         prepare = source->source_funcs->prepare;
+         context->in_check_or_prepare++;
+         UNLOCK_CONTEXT (context);
+
+         result = (*prepare) (source, &source_timeout);
+
+         LOCK_CONTEXT (context);
+         context->in_check_or_prepare--;
+
+         if (result)
+           source->flags |= G_SOURCE_READY;
+       }
+
+      if (source->flags & G_SOURCE_READY)
+       {
+         n_ready++;
+         current_priority = source->priority;
+         context->timeout = 0;
+       }
       
       if (source_timeout >= 0)
        {
-         if (timeout < 0)
-           timeout = source_timeout;
+         if (context->timeout < 0)
+           context->timeout = source_timeout;
          else
-           timeout = MIN (timeout, source_timeout);
+           context->timeout = MIN (context->timeout, source_timeout);
        }
 
-      hook = g_hook_next_valid (&source_list, hook, TRUE);
+    next:
+      source = next_valid_source (context, source);
+    }
+
+  UNLOCK_CONTEXT (context);
+  
+  if (priority)
+    *priority = current_priority;
+  
+  return (n_ready > 0);
+}
+
+/**
+ * g_main_context_query:
+ * @context: a #GMainContext
+ * @max_priority: maximum priority source to check
+ * @timeout: location to store timeout to be used in polling
+ * @fds: location to store #GPollFD records that need to be polled.
+ * @n_fds: length of @fds.
+ * 
+ * Determines information necessary to poll this main loop.
+ * 
+ * Return value: 
+ **/
+gint
+g_main_context_query (GMainContext *context,
+                     gint          max_priority,
+                     gint         *timeout,
+                     GPollFD      *fds,
+                     gint          n_fds)
+{
+  gint n_poll;
+  GPollRec *pollrec;
+  
+  LOCK_CONTEXT (context);
+
+  pollrec = context->poll_records;
+  n_poll = 0;
+  while (pollrec && max_priority >= pollrec->priority)
+    {
+      if (pollrec->fd->events)
+       {
+         if (n_poll < n_fds)
+           {
+             fds[n_poll].fd = pollrec->fd->fd;
+             /* In direct contradiction to the Unix98 spec, IRIX runs into
+              * difficulty if you pass in POLLERR, POLLHUP or POLLNVAL
+              * flags in the events field of the pollfd while it should
+              * just ignoring them. So we mask them out here.
+              */
+             fds[n_poll].events = pollrec->fd->events & ~(G_IO_ERR|G_IO_HUP|G_IO_NVAL);
+             fds[n_poll].revents = 0;
+           }
+         n_poll++;
+       }
+      
+      pollrec = pollrec->next;
     }
 
-  /* poll(), if necessary */
+  context->poll_changed = FALSE;
+
+  if (timeout)
+    {
+      *timeout = context->timeout;
+      if (timeout != 0)
+       context->time_is_current = FALSE;
+    }
+  
+  UNLOCK_CONTEXT (context);
 
-  g_main_poll (timeout, n_ready > 0, current_priority);
+  return n_poll;
+}
 
-  if (timeout != 0)
-    g_get_current_time (&current_time);
+/**
+ * g_main_context_check:
+ * @context: a #GMainContext
+ * @max_priority: the maximum numerical priority of sources to check
+ * @fds: array of #GPollFD's that was passed to the last call to
+ *       g_main_context_query()
+ * @n_fds: return value of g_main_context_query()
+ * 
+ * Pass the results of polling back to the main loop.
+ * 
+ * Return value: %TRUE if some sources are ready to be dispatched.
+ **/
+gboolean
+g_main_context_check (GMainContext *context,
+                     gint          max_priority,
+                     GPollFD      *fds,
+                     gint          n_fds)
+{
+  GSource *source;
+  GPollRec *pollrec;
+  gint n_ready = 0;
+  gint i;
   
-  /* Check to see what sources need to be dispatched */
+  LOCK_CONTEXT (context);
 
-  n_ready = 0;
+  if (context->in_check_or_prepare)
+    {
+      g_warning ("g_main_context_check() called recursively from within a source's check() or "
+                "prepare() member.");
+      return FALSE;
+    }
   
-  hook = g_hook_first_valid (&source_list, TRUE);
-  while (hook)
+#ifdef G_THREADS_ENABLED
+  if (!context->poll_waiting)
     {
-      GSource *source = (GSource *)hook;
+#ifndef G_OS_WIN32
+      gchar c;
+      read (context->wake_up_pipe[0], &c, 1);
+#endif
+    }
+  else
+    context->poll_waiting = FALSE;
+#endif /* G_THREADS_ENABLED */
 
-      if ((n_ready > 0) && (source->priority > current_priority))
+  /* If the set of poll file descriptors changed, bail out
+   * and let the main loop rerun
+   */
+  if (context->poll_changed)
+    return 0;
+
+  pollrec = context->poll_records;
+  i = 0;
+  while (i < n_fds)
+    {
+      if (pollrec->fd->events)
        {
-         g_hook_unref (&source_list, hook);
-         break;
+         pollrec->fd->revents = fds[i].revents;
+         i++;
        }
-      if (G_HOOK_IN_CALL (hook) && !(hook->flags & G_SOURCE_CAN_RECURSE))
+      pollrec = pollrec->next;
+    }
+
+  source = next_valid_source (context, NULL);
+  while (source)
+    {
+      if ((n_ready > 0) && (source->priority > max_priority))
        {
-         hook = g_hook_next_valid (&source_list, hook, TRUE);
-         continue;
+         SOURCE_UNREF (source, context);
+         break;
        }
+      if ((source->flags & G_HOOK_FLAG_IN_CALL) && !(source->flags & G_SOURCE_CAN_RECURSE))
+       goto next;
 
-      if (!(hook->flags & G_SOURCE_READY))
+      if (!(source->flags & G_SOURCE_READY))
        {
-         gboolean (*check) (gpointer  source_data,
-                            GTimeVal *current_time,
-                            gpointer  user_data);
+         gboolean result;
+         gboolean (*check) (GSource  *source);
 
-         check = ((GSourceFuncs *) hook->func)->check;
-         in_check_or_prepare++;
-         G_UNLOCK (main_loop);
+         check = source->source_funcs->check;
          
-         if ((*check) (source->source_data, &current_time, source->hook.data))
-           hook->flags |= G_SOURCE_READY;
-
-         G_LOCK (main_loop);
-         in_check_or_prepare--;
+         context->in_check_or_prepare++;
+         UNLOCK_CONTEXT (context);
+         
+         result = (*check) (source);
+         
+         LOCK_CONTEXT (context);
+         context->in_check_or_prepare--;
+         
+         if (result)
+           source->flags |= G_SOURCE_READY;
        }
 
-      if (hook->flags & G_SOURCE_READY)
+      if (source->flags & G_SOURCE_READY)
        {
-         if (dispatch)
-           {
-             hook->flags &= ~G_SOURCE_READY;
-             g_hook_ref (&source_list, hook);
-             pending_dispatches = g_slist_prepend (pending_dispatches, source);
-             current_priority = source->priority;
-             n_ready++;
-           }
-         else
-           {
-             g_hook_unref (&source_list, hook);
-             G_UNLOCK (main_loop);
+         source->ref_count++;
+         g_ptr_array_add (context->pending_dispatches, source);
 
-             return TRUE;
-           }
+         n_ready++;
        }
-      
-      hook = g_hook_next_valid (&source_list, hook, TRUE);
+
+    next:
+      source = next_valid_source (context, source);
     }
-  /* Now invoke the callbacks */
 
-  if (pending_dispatches)
+  UNLOCK_CONTEXT (context);
+
+  return n_ready > 0;
+}
+
+/**
+ * g_main_context_dispatch:
+ * @context: a #GMainContext
+ * 
+ * Dispatch all pending sources()
+ **/
+void
+g_main_context_dispatch (GMainContext *context)
+{
+  LOCK_CONTEXT (context);
+
+  if (context->pending_dispatches->len > 0)
     {
-      pending_dispatches = g_slist_reverse (pending_dispatches);
-      g_main_dispatch (&current_time);
-      retval = TRUE;
+      g_main_dispatch (context);
     }
 
-  G_UNLOCK (main_loop);
+  UNLOCK_CONTEXT (context);
+}
+
+static gboolean
+g_main_context_iterate (GMainContext *context,
+                       gboolean      block,
+                       gboolean      dispatch)
+{
+  gint max_priority;
+  gint timeout;
+  gboolean some_ready;
+  gint nfds, new_nfds;
+  GPollFD *fds;
+  
+  some_ready = g_main_context_prepare (context, &max_priority);
+
+  do
+    {
+      LOCK_CONTEXT (context);
+
+      if (context->cached_poll_array)
+       {
+         nfds = context->cached_poll_array_size;
+         fds = context->cached_poll_array;
+         context->cached_poll_array = NULL;
+       }
+      else
+       {
+         nfds = context->cached_poll_array_size = context->n_poll_records;
+         fds = g_new (GPollFD, nfds);
+       }
+
+      UNLOCK_CONTEXT (context);
+  
+      new_nfds = g_main_context_query (context, max_priority,
+                                      &timeout, fds, nfds);
+    }
+  while (new_nfds > nfds);
+
+  if (!block)
+    timeout = 0;
+  
+  g_main_context_poll (context, timeout, max_priority,
+                      fds, new_nfds);
 
-  return retval;
+  g_main_context_check (context,
+                       max_priority,
+                       fds, new_nfds);
+
+  LOCK_CONTEXT (context);
+
+  g_assert (!context->cached_poll_array);
+  
+  context->cached_poll_array = fds;
+  context->cached_poll_array_size = nfds;
+
+  UNLOCK_CONTEXT (context);
+  
+  if (dispatch)
+    g_main_context_dispatch (context);
+
+  return some_ready;
 }
 
-/* See if any events are pending
- */
+/**
+ * g_main_context_pending:
+ * @context: a #GMainContext (if %NULL, the default context will be used)
+ *
+ * Check if any sources have pending events for the given context.
+ * 
+ * Return value: %TRUE if events are pending.
+ **/
 gboolean 
-g_main_pending (void)
+g_main_context_pending (GMainContext *context)
 {
-  return in_check_or_prepare ? FALSE : g_main_iterate (FALSE, FALSE);
+  if (!context)
+    context = g_main_context_default();
+  
+  return g_main_context_iterate (context, FALSE, FALSE);
 }
 
-/* Run a single iteration of the mainloop. If block is FALSE,
- * will never block
- */
+/**
+ * g_main_context_iteration:
+ * @context: a #GMainContext (if %NULL, the default context will be used) 
+ * @may_block: whether the call may block.
+ * 
+ * Run a single iteration for the given main loop. This involves
+ * checking to see if any event sources are ready to be processed,
+ * then if no events sources are ready and @may_block is %TRUE, waiting
+ * for a source to become ready, then dispatching the highest priority
+ * events sources that are ready. Note that even when @may_block is %TRUE,
+ * it is still possible for g_main_context_iteration() to return
+ * %FALSE, since the the wait may be interrupted for other
+ * reasons than an event source becoming ready.
+ * 
+ * Return value: %TRUE if events were dispatched.
+ **/
 gboolean
-g_main_iteration (gboolean block)
+g_main_context_iteration (GMainContext *context, gboolean may_block)
 {
-  if (in_check_or_prepare)
-    {
-      g_warning ("g_main_iteration(): called recursively from within a source's check() or "
-                "prepare() member or from a second thread, iteration not possible");
-      return FALSE;
-    }
-  else
-    return g_main_iterate (block, TRUE);
+  if (!context)
+    context = g_main_context_default();
+  
+  return g_main_context_iterate (context, may_block, TRUE);
 }
 
-GMainLoop*
-g_main_new (gboolean is_running)
+/**
+ * g_main_loop_new:
+ * @context: a #GMainContext  (if %NULL, the default context will be used).
+ * @is_running: set to TRUE to indicate that the loop is running. This
+ * is not very important since calling g_main_run() will set this to
+ * TRUE anyway.
+ * 
+ * Create a new #GMainLoop structure
+ * 
+ * Return value: 
+ **/
+GMainLoop *
+g_main_loop_new (GMainContext *context,
+                gboolean      is_running)
 {
   GMainLoop *loop;
-
+  
+  if (!context)
+    context = g_main_context_default();
+  
   loop = g_new0 (GMainLoop, 1);
+  loop->context = context;
   loop->is_running = is_running != FALSE;
 
+#ifdef G_THREADS_ENABLED
+  if (g_thread_supported ())
+    loop->mutex = g_mutex_new ();
+  else
+    loop->mutex = NULL;
+  loop->sem_cond = NULL;
+#endif G_THREADS_ENABLED  
+
   return loop;
 }
 
+/**
+ * g_main_loop_run:
+ * @loop: a #GMainLoop
+ * 
+ * Run a main loop until g_main_quit() is called on the loop.
+ * If this is called for the thread of the loop's #GMainContext,
+ * it will process events from the loop, otherwise it will
+ * simply wait.
+ **/
 void 
-g_main_run (GMainLoop *loop)
+g_main_loop_run (GMainLoop *loop)
 {
   g_return_if_fail (loop != NULL);
 
-  if (in_check_or_prepare)
+#ifdef G_THREADS_ENABLED
+  if (loop->context->thread != g_thread_self ())
+    {
+      LOCK_LOOP (loop);
+
+      if (!g_thread_supported ())
+       {
+         g_warning ("g_main_loop_run() was called from second thread but"
+                    "g_thread_init() was never called.");
+       }
+      else
+       {
+         if (!loop->sem_cond)
+           loop->sem_cond = g_cond_new ();
+         
+         if (!loop->is_running)
+           loop->is_running = TRUE;
+         
+         while (loop->is_running)
+           g_cond_wait (loop->sem_cond, loop->mutex);
+       }
+       
+      UNLOCK_LOOP (loop);
+    }
+  else
+#endif /* G_THREADS_ENABLED */    
     {
-      g_warning ("g_main_run(): called recursively from within a source's check() or "
-                "prepare() member or from a second thread, iteration not possible");
-      return;
+      LOCK_CONTEXT (loop->context);
+      if (loop->context->in_check_or_prepare)
+       {
+         g_warning ("g_main_run(): called recursively from within a source's check() or "
+                    "prepare() member, iteration not possible.");
+         return;
+       }
+      UNLOCK_CONTEXT (loop->context);
+      
+      LOCK_LOOP (loop);
+
+      loop->is_running = TRUE;
+      while (loop->is_running)
+       {
+         UNLOCK_LOOP (loop);
+         g_main_context_iterate (loop->context, TRUE, TRUE);
+         LOCK_LOOP (loop);
+       }
+      UNLOCK_LOOP (loop);
     }
-  
-  loop->is_running = TRUE;
-  while (loop->is_running)
-    g_main_iterate (TRUE, TRUE);
 }
 
+/**
+ * g_main_loop_quit:
+ * @loop: a #GMainLoop
+ * 
+ * Stops a #GMainLoop from running. Any calls to g_main_loop_run()
+ * for the loop will return.
+ **/
 void 
-g_main_quit (GMainLoop *loop)
+g_main_loop_quit (GMainLoop *loop)
 {
   g_return_if_fail (loop != NULL);
 
+  LOCK_LOOP (loop);
   loop->is_running = FALSE;
+
+  if (loop->sem_cond)
+    g_cond_broadcast (loop->sem_cond);
+  
+  UNLOCK_LOOP (loop);
+
+  LOCK_CONTEXT (loop->context);
+  
+  g_main_context_wakeup (loop->context);
+  UNLOCK_CONTEXT (loop->context);
 }
 
+/**
+ * g_main_loop_destroy:
+ * @loop: a #GMainLoop
+ * 
+ * Destroy a #GMainLoop object and free all associated memory.
+ * The loop must not currently be running via g_main_run().
+ **/
 void 
-g_main_destroy (GMainLoop *loop)
+g_main_loop_destroy (GMainLoop *loop)
 {
   g_return_if_fail (loop != NULL);
+  g_return_if_fail (!loop->is_running);
+
+#ifdef G_THREADS_ENABLED
+  g_mutex_free (loop->mutex);
+  if (loop->sem_cond)
+    g_cond_free (loop->sem_cond);
+#endif /* G_THREADS_ENABLED */  
 
   g_free (loop);
 }
 
+/**
+ * g_main_loop_is_running:
+ * @loop: a #GMainLoop.
+ * 
+ * Check to see if the main loop is currently being run via g_main_run()
+ * 
+ * Return value: %TRUE if the mainloop is currently being run.
+ **/
 gboolean
-g_main_is_running (GMainLoop *loop)
+g_main_loop_is_running (GMainLoop *loop)
 {
+  gboolean result;
+  
   g_return_val_if_fail (loop != NULL, FALSE);
 
-  return loop->is_running;
+  LOCK_LOOP (loop);
+  result = loop->is_running;
+  UNLOCK_LOOP (loop);
+
+  return result;
 }
 
-/* HOLDS: main_loop_lock */
+/* HOLDS: context's lock */
 static void
-g_main_poll (gint     timeout,
-            gboolean use_priority,
-            gint     priority)
+g_main_context_poll (GMainContext *context,
+                    gint          timeout,
+                    gint          priority,
+                    GPollFD      *fds,
+                    gint          n_fds)
 {
 #ifdef  G_MAIN_POLL_DEBUG
   GTimer *poll_timer;
-#endif
-  GPollFD *fd_array;
   GPollRec *pollrec;
   gint i;
-  gint npoll;
+#endif
 
-#ifdef G_THREADS_ENABLED
-#ifndef G_OS_WIN32
-  if (wake_up_pipe[0] < 0)
-    {
-      if (pipe (wake_up_pipe) < 0)
-       g_error ("Cannot create pipe main loop wake-up: %s\n",
-                g_strerror (errno));
+  GPollFunc poll_func;
 
-      wake_up_rec.fd = wake_up_pipe[0];
-      wake_up_rec.events = G_IO_IN;
-      g_main_add_poll_unlocked (0, &wake_up_rec);
-    }
-#else
-  if (wake_up_semaphore == NULL)
-    {
-      if ((wake_up_semaphore = CreateSemaphore (NULL, 0, 100, NULL)) == NULL)
-       g_error ("Cannot create wake-up semaphore: %s", g_win32_error_message (GetLastError ()));
-      wake_up_rec.fd = (gint) wake_up_semaphore;
-      wake_up_rec.events = G_IO_IN;
-#ifdef G_MAIN_POLL_DEBUG
-      g_print ("wake-up semaphore: %#x\n", (guint) wake_up_semaphore);
-#endif
-      g_main_add_poll_unlocked (0, &wake_up_rec);
-    }
-#endif
-#endif
-  fd_array = g_new (GPollFD, n_poll_records);
-  pollrec = poll_records;
-  i = 0;
-  while (pollrec && (!use_priority || priority >= pollrec->priority))
-    {
-      if (pollrec->fd->events)
-       {
-         fd_array[i].fd = pollrec->fd->fd;
-         /* In direct contradiction to the Unix98 spec, IRIX runs into
-          * difficulty if you pass in POLLERR, POLLHUP or POLLNVAL
-          * flags in the events field of the pollfd while it should
-          * just ignoring them. So we mask them out here.
-          */
-         fd_array[i].events = pollrec->fd->events & ~(G_IO_ERR|G_IO_HUP|G_IO_NVAL);
-         fd_array[i].revents = 0;
-         i++;
-       }
-      
-      pollrec = pollrec->next;
-    }
-#ifdef G_THREADS_ENABLED
-  poll_waiting = TRUE;
-  poll_changed = FALSE;
-#endif
-  
-  npoll = i;
-  if (npoll || timeout != 0)
+  if (n_fds || timeout != 0)
     {
 #ifdef G_MAIN_POLL_DEBUG
-      g_print ("g_main_poll(%d) timeout: %d\n", npoll, timeout);
+      g_print ("g_main_poll(%d) timeout: %d\n", n_fds, timeout);
       poll_timer = g_timer_new ();
 #endif
+
+      LOCK_CONTEXT (context);
+
+      poll_func = context->poll_func;
       
-      G_UNLOCK (main_loop);
-      if ((*poll_func) (fd_array, npoll, timeout) < 0)
+      UNLOCK_CONTEXT (context);
+      if ((*poll_func) (fds, n_fds, timeout) < 0 && errno != EINTR)
        g_warning ("poll(2) failed due to: %s.",
                   g_strerror (errno));
-      G_LOCK (main_loop);
       
 #ifdef G_MAIN_POLL_DEBUG
+      LOCK_CONTEXT (context);
+
       g_print ("g_main_poll(%d) timeout: %d - elapsed %12.10f seconds",
-              npoll,
+              n_fds,
               timeout,
               g_timer_elapsed (poll_timer, NULL));
       g_timer_destroy (poll_timer);
-      pollrec = poll_records;
+      pollrec = context->poll_records;
       i = 0;
-      while (i < npoll)
+      while (i < n_fds)
        {
          if (pollrec->fd->events)
            {
-             if (fd_array[i].revents)
+             if (fds[i].revents)
                {
-                 g_print (" [%d:", fd_array[i].fd);
-                 if (fd_array[i].revents & G_IO_IN)
+                 g_print (" [%d:", fds[i].fd);
+                 if (fds[i].revents & G_IO_IN)
                    g_print ("i");
-                 if (fd_array[i].revents & G_IO_OUT)
+                 if (fds[i].revents & G_IO_OUT)
                    g_print ("o");
-                 if (fd_array[i].revents & G_IO_PRI)
+                 if (fds[i].revents & G_IO_PRI)
                    g_print ("p");
-                 if (fd_array[i].revents & G_IO_ERR)
+                 if (fds[i].revents & G_IO_ERR)
                    g_print ("e");
-                 if (fd_array[i].revents & G_IO_HUP)
+                 if (fds[i].revents & G_IO_HUP)
                    g_print ("h");
-                 if (fd_array[i].revents & G_IO_NVAL)
+                 if (fds[i].revents & G_IO_NVAL)
                    g_print ("n");
                  g_print ("]");
                }
@@ -1094,77 +2208,64 @@ g_main_poll (gint     timeout,
          pollrec = pollrec->next;
        }
       g_print ("\n");
+      
+      UNLOCK_CONTEXT (context);
 #endif
-    } /* if (npoll || timeout != 0) */
-  
-#ifdef G_THREADS_ENABLED
-  if (!poll_waiting)
-    {
-#ifndef G_OS_WIN32
-      gchar c;
-      read (wake_up_pipe[0], &c, 1);
-#endif
-    }
-  else
-    poll_waiting = FALSE;
-
-  /* If the set of poll file descriptors changed, bail out
-   * and let the main loop rerun
-   */
-  if (poll_changed)
-    {
-      g_free (fd_array);
-      return;
-    }
-#endif
-
-  pollrec = poll_records;
-  i = 0;
-  while (i < npoll)
-    {
-      if (pollrec->fd->events)
-       {
-         pollrec->fd->revents = fd_array[i].revents;
-         i++;
-       }
-      pollrec = pollrec->next;
-    }
-
-  g_free (fd_array);
+    } /* if (n_fds || timeout != 0) */
 }
 
-void 
-g_main_add_poll (GPollFD *fd,
-                gint     priority)
+/**
+ * g_main_context_add_poll:
+ * @context: a #GMainContext (or %NULL for the default context)
+ * @fd: a #GPollFD structure holding information about a file
+ *      descriptor to watch.
+ * @priority: the priority for this file descriptor which should be
+ *      the same as the priority used for g_source_attach() to ensure that the
+ *      file descriptor is polled whenever the results may be needed.
+ * 
+ * Add a file descriptor to the set of file descriptors polled * for
+ * this context. This will very seldom be used directly. Instead
+ * a typical event source will use g_source_add_poll() instead.
+ **/
+void
+g_main_context_add_poll (GMainContext *context,
+                        GPollFD      *fd,
+                        gint          priority)
 {
-  G_LOCK (main_loop);
-  g_main_add_poll_unlocked (priority, fd);
-  G_UNLOCK (main_loop);
+  if (!context)
+    context = g_main_context_default ();
+  
+  LOCK_CONTEXT (context);
+  g_main_context_add_poll_unlocked (context, priority, fd);
+  UNLOCK_CONTEXT (context);
 }
 
 /* HOLDS: main_loop_lock */
 static void 
-g_main_add_poll_unlocked (gint     priority,
-                         GPollFD *fd)
+g_main_context_add_poll_unlocked (GMainContext *context,
+                                 gint          priority,
+                                 GPollFD      *fd)
 {
   GPollRec *lastrec, *pollrec, *newrec;
 
-  if (!poll_chunk)
-    poll_chunk = g_mem_chunk_create (GPollRec, 32, G_ALLOC_ONLY);
+  if (!context->poll_chunk)
+    context->poll_chunk = g_mem_chunk_create (GPollRec, 32, G_ALLOC_ONLY);
 
-  if (poll_free_list)
+  if (context->poll_free_list)
     {
-      newrec = poll_free_list;
-      poll_free_list = newrec->next;
+      newrec = context->poll_free_list;
+      context->poll_free_list = newrec->next;
     }
   else
-    newrec = g_chunk_new (GPollRec, poll_chunk);
+    newrec = g_chunk_new (GPollRec, context->poll_chunk);
 
+  /* This file descriptor may be checked before we ever poll */
+  fd->revents = 0;
   newrec->fd = fd;
   newrec->priority = priority;
 
   lastrec = NULL;
-  pollrec = poll_records;
+  pollrec = context->poll_records;
   while (pollrec && priority >= pollrec->priority)
     {
       lastrec = pollrec;
@@ -1174,29 +2275,56 @@ g_main_add_poll_unlocked (gint     priority,
   if (lastrec)
     lastrec->next = newrec;
   else
-    poll_records = newrec;
+    context->poll_records = newrec;
 
   newrec->next = pollrec;
 
-  n_poll_records++;
+  context->n_poll_records++;
+  if (context->cached_poll_array &&
+      context->cached_poll_array_size < context->n_poll_records)
+    {
+      g_free (context->cached_poll_array);
+      context->cached_poll_array = NULL;
+    }
 
 #ifdef G_THREADS_ENABLED
-  poll_changed = TRUE;
+  context->poll_changed = TRUE;
 
   /* Now wake up the main loop if it is waiting in the poll() */
-  g_main_wakeup ();
+  g_main_context_wakeup (context);
 #endif
 }
 
-void 
-g_main_remove_poll (GPollFD *fd)
+/**
+ * g_main_context_remove_poll:
+ * @context:a #GMainContext 
+ * @fd: a #GPollFD descriptor previously added with g_main_context_add_poll()
+ * 
+ * Remove file descriptor from the set of file descriptors to be
+ * polled for a particular context.
+ **/
+void
+g_main_context_remove_poll (GMainContext *context,
+                           GPollFD      *fd)
 {
-  GPollRec *pollrec, *lastrec;
+  if (!context)
+    context = g_main_context_default ();
+  
+  LOCK_CONTEXT (context);
 
-  G_LOCK (main_loop);
+  g_main_context_remove_poll_unlocked (context, fd);
   
+  UNLOCK_CONTEXT (context);
+}
+
+static void
+g_main_context_remove_poll_unlocked (GMainContext *context,
+                                    GPollFD      *fd)
+{
+  GPollRec *pollrec, *lastrec;
+
   lastrec = NULL;
-  pollrec = poll_records;
+  pollrec = context->poll_records;
 
   while (pollrec)
     {
@@ -1205,16 +2333,16 @@ g_main_remove_poll (GPollFD *fd)
          if (lastrec != NULL)
            lastrec->next = pollrec->next;
          else
-           poll_records = pollrec->next;
+           context->poll_records = pollrec->next;
 
 #ifdef ENABLE_GC_FRIENDLY
          pollrec->fd = NULL;  
 #endif /* ENABLE_GC_FRIENDLY */
 
-         pollrec->next = poll_free_list;
-         poll_free_list = pollrec;
+         pollrec->next = context->poll_free_list;
+         context->poll_free_list = pollrec;
 
-         n_poll_records--;
+         context->n_poll_records--;
          break;
        }
       lastrec = pollrec;
@@ -1222,52 +2350,120 @@ g_main_remove_poll (GPollFD *fd)
     }
 
 #ifdef G_THREADS_ENABLED
-  poll_changed = TRUE;
+  context->poll_changed = TRUE;
   
   /* Now wake up the main loop if it is waiting in the poll() */
-  g_main_wakeup ();
+  g_main_context_wakeup (context);
 #endif
+}
 
-  G_UNLOCK (main_loop);
+/**
+ * g_source_get_current_time:
+ * @source:  a #GSource
+ * @timeval: #GTimeVal structure in which to store current time.
+ * 
+ * Gets the "current time" to be used when checking 
+ * this source. The advantage of calling this function over
+ * calling g_get_current_time() directly is that when 
+ * checking multiple sources, GLib can cache a single value
+ * instead of having to repeatedly get the system time.
+ **/
+void
+g_source_get_current_time (GSource  *source,
+                          GTimeVal *timeval)
+{
+  GMainContext *context;
+  
+  g_return_if_fail (source->context != NULL);
+  context = source->context;
+
+  LOCK_CONTEXT (context);
+
+  if (!context->time_is_current)
+    {
+      g_get_current_time (&context->current_time);
+      context->time_is_current = TRUE;
+    }
+  
+  *timeval = context->current_time;
+  
+  UNLOCK_CONTEXT (context);
 }
 
-void 
-g_main_set_poll_func (GPollFunc func)
+/**
+ * g_main_context_set_poll_func:
+ * @context: a #GMainContext
+ * @func: the function to call to poll all file descriptors
+ * 
+ * Sets the function to use to handle polling of file descriptors. It
+ * will be used instead of the poll() system call (or GLib's
+ * replacement function, which is used where poll() isn't available).
+ *
+ * This function could possibly be used to integrate the GLib event
+ * loop with an external event loop.
+ **/
+void
+g_main_context_set_poll_func (GMainContext *context,
+                             GPollFunc     func)
 {
+  if (!context)
+    context = g_main_context_default ();
+  
+  LOCK_CONTEXT (context);
+  
   if (func)
-    poll_func = func;
+    context->poll_func = func;
   else
+    {
 #ifdef HAVE_POLL
-    poll_func = (GPollFunc) poll;
+      context->poll_func = (GPollFunc) poll;
 #else
-    poll_func = (GPollFunc) g_poll;
+      context->poll_func = (GPollFunc) g_poll;
 #endif
+    }
+
+  UNLOCK_CONTEXT (context);
 }
 
 #ifdef G_OS_WIN32
 
-/* Useful on other platforms, too? */
-
+/**
+ * g_main_context_get_poll_func:
+ * @context: a #GMainContext
+ * 
+ * Gets the poll function set by g_main_context_set_poll_func()
+ * 
+ * Return value: the poll function
+ **/
 GPollFunc
-g_main_win32_get_poll_func (void)
+g_main_context_get_poll_func (GMainContext *context)
 {
-  return poll_func;
+  GPollFunc *result;
+  
+  if (!context)
+    context = g_main_context_default ();
+  
+  LOCK_CONTEXT (context);
+  result = context->poll_func;
+  UNLOCK_CONTEXT (context);
 }
 
 #endif
 
+/* HOLDS: context's lock */
 /* Wake the main loop up from a poll() */
 static void
-g_main_wakeup (void)
+g_main_context_wakeup (GMainContext *context)
 {
 #ifdef G_THREADS_ENABLED
-  if (poll_waiting)
+  if (g_thread_supported() && context->poll_waiting)
     {
-      poll_waiting = FALSE;
+      context->poll_waiting = FALSE;
 #ifndef G_OS_WIN32
-      write (wake_up_pipe[1], "A", 1);
+      write (context->wake_up_pipe[1], "A", 1);
 #else
-      ReleaseSemaphore (wake_up_semaphore, 1, NULL);
+      ReleaseSemaphore (context->context->wake_up_semaphore, 1, NULL);
 #endif
     }
 #endif
@@ -1276,43 +2472,45 @@ g_main_wakeup (void)
 /* Timeouts */
 
 static void
-g_timeout_set_expiration (GTimeoutData *data,
-                         GTimeVal     *current_time)
+g_timeout_set_expiration (GTimeoutSource *timeout_source,
+                         GTimeVal       *current_time)
 {
-  guint seconds = data->interval / 1000;
-  guint msecs = data->interval - seconds * 1000;
+  guint seconds = timeout_source->interval / 1000;
+  guint msecs = timeout_source->interval - seconds * 1000;
 
-  data->expiration.tv_sec = current_time->tv_sec + seconds;
-  data->expiration.tv_usec = current_time->tv_usec + msecs * 1000;
-  if (data->expiration.tv_usec >= 1000000)
+  timeout_source->expiration.tv_sec = current_time->tv_sec + seconds;
+  timeout_source->expiration.tv_usec = current_time->tv_usec + msecs * 1000;
+  if (timeout_source->expiration.tv_usec >= 1000000)
     {
-      data->expiration.tv_usec -= 1000000;
-      data->expiration.tv_sec++;
+      timeout_source->expiration.tv_usec -= 1000000;
+      timeout_source->expiration.tv_sec++;
     }
 }
 
 static gboolean
-g_timeout_prepare  (gpointer  source_data,
-                   GTimeVal *current_time,
-                   gint     *timeout,
-                   gpointer  user_data)
+g_timeout_prepare  (GSource  *source,
+                   gint     *timeout)
 {
   glong msec;
-  GTimeoutData *data = source_data;
+  GTimeVal current_time;
   
-  msec = ((data->expiration.tv_sec  - current_time->tv_sec) * 1000 +
-         (data->expiration.tv_usec - current_time->tv_usec) / 1000);
+  GTimeoutSource *timeout_source = (GTimeoutSource *)source;
+
+  g_source_get_current_time (source, &current_time);
   
+  msec = ((timeout_source->expiration.tv_sec  - current_time.tv_sec) * 1000 +
+         (timeout_source->expiration.tv_usec - current_time.tv_usec) / 1000);
+
   if (msec < 0)
     msec = 0;
-  else if (msec > data->interval)
+  else if (msec > timeout_source->interval)
     {
       /* The system time has been set backwards, so we
-       * reset the expiration time to now + data->interval;
+       * reset the expiration time to now + timeout_source->interval;
        * this at least avoids hanging for long periods of time.
        */
-      g_timeout_set_expiration (data, current_time);
-      msec = data->interval;
+      g_timeout_set_expiration (timeout_source, &current_time);
+      msec = timeout_source->interval;
     }
   
   *timeout = msec;
@@ -1321,27 +2519,38 @@ g_timeout_prepare  (gpointer  source_data,
 }
 
 static gboolean 
-g_timeout_check (gpointer  source_data,
-                GTimeVal *current_time,
-                gpointer  user_data)
+g_timeout_check (GSource  *source)
 {
-  GTimeoutData *data = source_data;
+  GTimeVal current_time;
+  GTimeoutSource *timeout_source = (GTimeoutSource *)source;
+
+  g_source_get_current_time (source, &current_time);
   
-  return ((data->expiration.tv_sec < current_time->tv_sec) ||
-         ((data->expiration.tv_sec == current_time->tv_sec) &&
-          (data->expiration.tv_usec <= current_time->tv_usec)));
+  return ((timeout_source->expiration.tv_sec < current_time.tv_sec) ||
+         ((timeout_source->expiration.tv_sec == current_time.tv_sec) &&
+          (timeout_source->expiration.tv_usec <= current_time.tv_usec)));
 }
 
 static gboolean
-g_timeout_dispatch (gpointer source_data,
-                   GTimeVal *dispatch_time,
-                   gpointer user_data)
+g_timeout_dispatch (GSource    *source,
+                   GSourceFunc callback,
+                   gpointer    user_data)
 {
-  GTimeoutData *data = source_data;
+  GTimeoutSource *timeout_source = (GTimeoutSource *)source;
 
-  if (data->callback (user_data))
+  if (!callback)
+    {
+      g_warning ("Timeout source dispatched without callback\n"
+                "You must call g_source_set_callback().");
+      return FALSE;
+    }
+  if (callback (user_data))
     {
-      g_timeout_set_expiration (data, dispatch_time);
+      GTimeVal current_time;
+
+      g_source_get_current_time (source, &current_time);
+      g_timeout_set_expiration (timeout_source, &current_time);
 
       return TRUE;
     }
@@ -1349,6 +2558,58 @@ g_timeout_dispatch (gpointer source_data,
     return FALSE;
 }
 
+/**
+ * g_timeout_source_new:
+ * @interval: the timeout interval in milliseconds.
+ * 
+ * Create a new timeout source.
+ *
+ * The source will not initially be associated with any #GMainContext
+ * and must be added to one with g_source_attach() before it will be
+ * executed.
+ * 
+ * Return value: the newly create timeout source
+ **/
+GSource *
+g_timeout_source_new (guint interval)
+{
+  GSource *source = g_source_new (&timeout_funcs, sizeof (GTimeoutSource));
+  GTimeoutSource *timeout_source = (GTimeoutSource *)source;
+  GTimeVal current_time;
+
+  timeout_source->interval = interval;
+
+  g_get_current_time (&current_time);
+  g_timeout_set_expiration (timeout_source, &current_time);
+  
+  return source;
+}
+
+/**
+ * g_timeout_add_full:
+ * @priority: the priority of the idle source. Typically this will be in the
+ *            range btweeen #G_PRIORITY_DEFAULT_IDLE and #G_PRIORITY_HIGH_IDLE.
+ * @interval: the time between calls to the function, in milliseconds
+ *             (1/1000ths of a second.)
+ * @function: function to call
+ * @data:     data to pass to @function
+ * @notify:   function to call when the idle is removed, or %NULL
+ * 
+ * Sets a function to be called at regular intervals, with the given
+ * priority.  The function is called repeatedly until it returns
+ * FALSE, at which point the timeout is automatically destroyed and
+ * the function will not be called again.  The @notify function is
+ * called when the timeout is destroyed.  The first call to the
+ * function will be at the end of the first @interval.
+ *
+ * Note that timeout functions may be delayed, due to the processing of other
+ * event sources. Thus they should not be relied on for precise timing.
+ * After each call to the timeout function, the time of the next
+ * timeout is recalculated based on the current time and the given interval
+ * (it does not try to 'catch up' time lost in delays).
+ * 
+ * Return value: the id of event source.
+ **/
 guint
 g_timeout_add_full (gint           priority,
                    guint          interval,
@@ -1356,18 +2617,41 @@ g_timeout_add_full (gint           priority,
                    gpointer       data,
                    GDestroyNotify notify)
 {
-  GTimeoutData *timeout_data = g_new (GTimeoutData, 1);
-  GTimeVal current_time;
+  GSource *source;
+  
+  g_return_val_if_fail (function != NULL, 0);
 
-  timeout_data->interval = interval;
-  timeout_data->callback = function;
-  g_get_current_time (&current_time);
+  source = g_timeout_source_new (interval);
 
-  g_timeout_set_expiration (timeout_data, &current_time);
+  if (priority != G_PRIORITY_DEFAULT)
+    g_source_set_priority (source, priority);
 
-  return g_source_add (priority, FALSE, &timeout_funcs, timeout_data, data, notify);
+  g_source_set_callback (source, function, data, notify);
+  return g_source_attach (source, NULL);
 }
 
+/**
+ * g_timeout_add:
+ * @interval: the time between calls to the function, in milliseconds
+ *             (1/1000ths of a second.)
+ * @function: function to call
+ * @data:     data to pass to @function
+ * 
+ * Sets a function to be called at regular intervals, with the default
+ * priority, #G_PRIORITY_DEFAULT.  The function is called repeatedly
+ * until it returns FALSE, at which point the timeout is automatically
+ * destroyed and the function will not be called again.  The @notify
+ * function is called when the timeout is destroyed.  The first call
+ * to the function will be at the end of the first @interval.
+ *
+ * Note that timeout functions may be delayed, due to the processing of other
+ * event sources. Thus they should not be relied on for precise timing.
+ * After each call to the timeout function, the time of the next
+ * timeout is recalculated based on the current time and the given interval
+ * (it does not try to 'catch up' time lost in delays).
+ * 
+ * Return value: the id of event source.
+ **/
 guint 
 g_timeout_add (guint32        interval,
               GSourceFunc    function,
@@ -1380,10 +2664,8 @@ g_timeout_add (guint32        interval,
 /* Idle functions */
 
 static gboolean 
-g_idle_prepare  (gpointer  source_data, 
-                GTimeVal *current_time,
-                gint     *timeout,
-                gpointer  user_data)
+g_idle_prepare  (GSource  *source,
+                gint     *timeout)
 {
   *timeout = 0;
 
@@ -1391,34 +2673,89 @@ g_idle_prepare  (gpointer  source_data,
 }
 
 static gboolean 
-g_idle_check    (gpointer  source_data,
-                GTimeVal *current_time,
-                gpointer  user_data)
+g_idle_check    (GSource  *source)
 {
   return TRUE;
 }
 
 static gboolean
-g_idle_dispatch (gpointer source_data
-                GTimeVal *dispatch_time,
-                gpointer user_data)
+g_idle_dispatch (GSource    *source
+                GSourceFunc callback,
+                gpointer    user_data)
 {
-  GSourceFunc func = (GSourceFunc) source_data;
+  if (!callback)
+    {
+      g_warning ("Idle source dispatched without callback\n"
+                "You must call g_source_set_callback().");
+      return FALSE;
+    }
+  
+  return callback (user_data);
+}
 
-  return func (user_data);
+/**
+ * g_idle_source_new:
+ * 
+ * Create a new idle source.
+ *
+ * The source will not initially be associated with any #GMainContext
+ * and must be added to one with g_source_attach() before it will be
+ * executed.
+ * 
+ * Return value: the newly created idle source
+ **/
+GSource *
+g_idle_source_new (void)
+{
+  return g_source_new (&idle_funcs, sizeof (GSource));
 }
 
+/**
+ * g_idle_add_full:
+ * @priority: the priority of the idle source. Typically this will be in the
+ *            range btweeen #G_PRIORITY_DEFAULT_IDLE and #G_PRIORITY_HIGH_IDLE.
+ * @function: function to call
+ * @data:     data to pass to @function
+ * @notify:   function to call when the idle is removed, or %NULL
+ * 
+ * Adds a function to be called whenever there are no higher priority
+ * events pending.  If the function returns FALSE it is automatically
+ * removed from the list of event sources and will not be called again.
+ * 
+ * Return value: the id of the event source.
+ **/
 guint 
 g_idle_add_full (gint           priority,
                 GSourceFunc    function,
                 gpointer       data,
                 GDestroyNotify notify)
 {
+  GSource *source;
+  
   g_return_val_if_fail (function != NULL, 0);
 
-  return g_source_add (priority, FALSE, &idle_funcs, (gpointer) function, data, notify);
+  source = g_idle_source_new ();
+
+  if (priority != G_PRIORITY_DEFAULT)
+    g_source_set_priority (source, priority);
+
+  g_source_set_callback (source, function, data, notify);
+  return g_source_attach (source, NULL);
 }
 
+/**
+ * g_idle_add:
+ * @function: function to call 
+ * @data: data to pass to @function.
+ * 
+ * Adds a function to be called whenever there are no higher priority
+ * events pending to the default main loop. The function is given the
+ * default idle priority, #G_PRIORITY_DEFAULT_IDLE.  If the function
+ * returns FALSE it is automatically removed from the list of event
+ * sources and will not be called again.
+ * 
+ * Return value: the id of the event source.
+ **/
 guint 
 g_idle_add (GSourceFunc    function,
            gpointer       data)
@@ -1426,8 +2763,17 @@ g_idle_add (GSourceFunc    function,
   return g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, function, data, NULL);
 }
 
+/**
+ * g_idle_remove_by_data:
+ * @data: the data for the idle source's callback.
+ * 
+ * Removes the idle function with the given data.
+ * 
+ * Return value: %TRUE if an idle source was found and removed.
+ **/
 gboolean
 g_idle_remove_by_data (gpointer data)
 {
   return g_source_remove_by_funcs_user_data (&idle_funcs, data);
 }
+
index 7f08c3dfbd07b04eca85d2af0b665d859fcf72ba..8e4d7fce7456fccf0e7acfadfca77e81ef71a064 100644 (file)
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
+/* gmain.h - the GLib Main loop
+ * Copyright (C) 1998-2000 Red Hat, Inc.
  *
  * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
+ * modify it under the terms of the GNU Library 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.
+ * Library General Public License for more details.
  *
- * You should have received a copy of the GNU Lesser General Public
+ * You should have received a copy of the GNU Library 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.
  */
 
-/*
- * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
- * file for a list of people on the GLib Team.  See the ChangeLog
- * files for a list of changes.  These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/. 
- */
-
 #ifndef __G_MAIN_H__
 #define __G_MAIN_H__
 
-#include <gtypes.h>
+#include <gslist.h>
+#include <gthread.h>
 
 G_BEGIN_DECLS
 
-/* Main loop
- */
-typedef struct _GTimeVal        GTimeVal;
-typedef struct _GSourceFuncs    GSourceFuncs;
-typedef struct _GMainLoop       GMainLoop;      /* Opaque */
-
-struct _GTimeVal
-{
-  glong tv_sec;
-  glong tv_usec;
-};
-struct _GSourceFuncs
-{
-  gboolean (*prepare)  (gpointer  source_data,
-                        GTimeVal *current_time,
-                        gint     *timeout,
-                        gpointer  user_data);
-  gboolean (*check)    (gpointer  source_data,
-                        GTimeVal *current_time,
-                        gpointer  user_data);
-  gboolean (*dispatch) (gpointer  source_data,
-                        GTimeVal *dispatch_time,
-                        gpointer  user_data);
-  GDestroyNotify destroy;
-};
+typedef struct _GMainContext           GMainContext;   /* Opaque */
+typedef struct _GMainLoop              GMainLoop;      /* Opaque */
+typedef struct _GSource                        GSource;
+typedef struct _GSourceCallbackFuncs   GSourceCallbackFuncs;
+typedef struct _GSourceFuncs           GSourceFuncs;
 
-/* Standard priorities */
+typedef gboolean (*GSourceFunc)       (gpointer data);
 
-#define G_PRIORITY_HIGH            -100
-#define G_PRIORITY_DEFAULT          0
-#define G_PRIORITY_HIGH_IDLE        100
-#define G_PRIORITY_DEFAULT_IDLE     200
-#define G_PRIORITY_LOW              300
+struct _GSource
+{
+  /*< private >*/
+  gpointer callback_data;
+  GSourceCallbackFuncs *callback_funcs;
 
-typedef gboolean (*GSourceFunc) (gpointer data);
+  GSourceFuncs *source_funcs;
+  guint ref_count;
 
-/* Hooks for adding to the main loop */
-guint    g_source_add                        (gint           priority,
-                                              gboolean       can_recurse,
-                                              GSourceFuncs  *funcs,
-                                              gpointer       source_data,
-                                              gpointer       user_data,
-                                              GDestroyNotify notify);
-gboolean g_source_remove                     (guint          tag);
-gboolean g_source_remove_by_user_data        (gpointer       user_data);
-gboolean g_source_remove_by_source_data      (gpointer       source_data);
-gboolean g_source_remove_by_funcs_user_data  (GSourceFuncs  *funcs,
-                                              gpointer       user_data);
+  GMainContext *context;
 
-void g_get_current_time                 (GTimeVal       *result);
+  gint priority;
+  guint flags;
+  guint id;
 
-/* Running the main loop */
-GMainLoop*      g_main_new              (gboolean        is_running);
-void            g_main_run              (GMainLoop      *loop);
-void            g_main_quit             (GMainLoop      *loop);
-void            g_main_destroy          (GMainLoop      *loop);
-gboolean        g_main_is_running       (GMainLoop      *loop);
+  GSList *poll_fds;
+  
+  GSource *prev;
+  GSource *next;
+};
 
-/* Run a single iteration of the mainloop. If block is FALSE,
- * will never block
- */
-gboolean        g_main_iteration        (gboolean       may_block);
+struct _GSourceCallbackFuncs
+{
+  void (*ref)   (gpointer     cb_data);
+  void (*unref) (gpointer     cb_data);
+  void (*get)   (gpointer     cb_data,
+                GSourceFunc *func,
+                gpointer    *data);
+};
 
-/* See if any events are pending */
-gboolean        g_main_pending          (void);
+struct _GSourceFuncs
+{
+  gboolean (*prepare)  (GSource    *source,
+                       gint       *timeout);
+  gboolean (*check)    (GSource    *source);
+  gboolean (*dispatch) (GSource    *source,
+                       GSourceFunc callback,
+                       gpointer    user_data);
+  void     (*destroy)  (GSource    *source);
+};
 
-/* Idles and timeouts */
-guint           g_timeout_add_full      (gint           priority,
-                                         guint          interval,
-                                         GSourceFunc    function,
-                                         gpointer       data,
-                                         GDestroyNotify notify);
-guint           g_timeout_add           (guint          interval,
-                                         GSourceFunc    function,
-                                         gpointer       data);
-guint           g_idle_add              (GSourceFunc    function,
-                                         gpointer       data);
-guint           g_idle_add_full         (gint           priority,
-                                         GSourceFunc    function,
-                                         gpointer       data,
-                                         GDestroyNotify destroy);
-gboolean        g_idle_remove_by_data   (gpointer       data);
-
-/* GPollFD
+/* Any definitions using GPollFD or GPollFunc are primarily
+ * for Unix and not guaranteed to be the compatible on all
+ * operating systems on which GLib runs. Right now, the
+ * GLib does use these functions on Win32 as well, but interprets
+ * them in a fairly different way than on Unix. If you use
+ * these definitions, you are should be prepared to recode
+ * for different operating systems.
  *
- * System-specific IO and main loop calls
  *
  * On Win32, the fd in a GPollFD should be Win32 HANDLE (*not* a file
  * descriptor as provided by the C runtime) that can be used by
@@ -137,32 +102,185 @@ gboolean        g_idle_remove_by_data   (gpointer       data);
  * Win32. It's really only written for the GIMP's needs so
  * far.
  */
-
 typedef struct _GPollFD GPollFD;
-typedef gint    (*GPollFunc)    (GPollFD *ufds,
-                                 guint    nfsd,
-                                 gint     timeout);
+typedef gint   (*GPollFunc)    (GPollFD *ufds,
+                                guint    nfsd,
+                                gint     timeout);
+
 struct _GPollFD
 {
-  gint          fd;
-  gushort       events;
-  gushort       revents;
+  gint         fd;
+  gushort      events;
+  gushort      revents;
 };
 
-void        g_main_add_poll          (GPollFD    *fd,
-                                      gint        priority);
-void        g_main_remove_poll       (GPollFD    *fd);
-void        g_main_set_poll_func     (GPollFunc   func);
+/* Standard priorities */
 
-#ifdef G_OS_WIN32
+#define G_PRIORITY_HIGH            -100
+#define G_PRIORITY_DEFAULT          0
+#define G_PRIORITY_HIGH_IDLE        100
+#define G_PRIORITY_DEFAULT_IDLE     200
+#define G_PRIORITY_LOW             300
+
+/* GMainContext: */
 
-/* Useful on other platforms, too? */
+GMainContext *g_main_context_get       (GThread      *thread);
+GMainContext *g_main_context_default   (void);
 
-GPollFunc   g_main_win32_get_poll_func (void);
+gboolean      g_main_context_iteration (GMainContext *context,
+                                       gboolean      may_block);
+gboolean      g_main_context_pending   (GMainContext *context);
+
+/* For implementation of legacy interfaces
+ */
+GSource      *g_main_context_find_source_by_id              (GMainContext *context,
+                                                            guint         id);
+GSource      *g_main_context_find_source_by_user_data       (GMainContext *context,
+                                                            gpointer      user_data);
+GSource      *g_main_context_find_source_by_funcs_user_data (GMainContext *context,
+                                                            GSourceFuncs *funcs,
+                                                            gpointer      user_data);
+
+/* Low level functions for implementing custom main loops.
+ */
+gboolean g_main_context_prepare  (GMainContext *context,
+                                 gint         *priority);
+gint     g_main_context_query    (GMainContext *context,
+                                 gint          max_priority,
+                                 gint         *timeout,
+                                 GPollFD      *fds,
+                                 gint          n_fds);
+gint     g_main_context_check    (GMainContext *context,
+                                 gint          max_priority,
+                                 GPollFD      *fds,
+                                 gint          n_fds);
+void     g_main_context_dispatch (GMainContext *context);
 
-#endif
+void      g_main_context_set_poll_func (GMainContext *context,
+                                       GPollFunc     func);
+GPollFunc g_main_context_get_poll_func (GMainContext *context);
+
+/* Low level functions for use by source implementations
+ */
+void g_main_context_add_poll      (GMainContext *context,
+                                  GPollFD      *fd,
+                                  gint          priority);
+void g_main_context_remove_poll   (GMainContext *context,
+                                  GPollFD      *fd);
+
+/* GMainLoop: */
+
+GMainLoop *g_main_loop_new        (GMainContext *context,
+                                  gboolean      is_running);
+void       g_main_loop_run        (GMainLoop    *loop);
+void       g_main_loop_quit       (GMainLoop    *loop);
+void       g_main_loop_destroy    (GMainLoop    *loop);
+gboolean   g_main_loop_is_running (GMainLoop    *loop);
+
+/* GSource: */
+
+GSource *g_source_new             (GSourceFuncs   *source_funcs,
+                                  guint           struct_size);
+GSource *g_source_ref             (GSource        *source);
+void     g_source_unref           (GSource        *source);
+
+guint    g_source_attach          (GSource        *source,
+                                  GMainContext   *context);
+void     g_source_destroy         (GSource        *source);
+
+void     g_source_set_priority    (GSource        *source,
+                                  gint            priority);
+gint     g_source_get_priority    (GSource        *source);
+void     g_source_set_can_recurse (GSource        *source,
+                                  gboolean        can_recurse);
+gboolean g_source_get_can_recurse (GSource        *source);
+guint    g_source_get_id          (GSource        *source);
+
+GMainContext *g_source_get_context (GSource       *source);
+
+void g_source_set_callback          (GSource              *source,
+                                    GSourceFunc           func,
+                                    gpointer              data,
+                                    GDestroyNotify        notify);
+
+
+/* Used to implement g_source_connect_closure and internally*/
+void g_source_set_callback_indirect (GSource              *source,
+                                    gpointer              callback_data,
+                                    GSourceCallbackFuncs *callback_funcs);
+
+void     g_source_add_poll         (GSource        *source,
+                                   GPollFD        *fd);
+void     g_source_remove_poll      (GSource        *source,
+                                   GPollFD        *fd);
+
+void     g_source_get_current_time (GSource        *source,
+                                   GTimeVal       *timeval);
+
+ /* void g_source_connect_closure (GSource        *source,
+                                  GClosure       *closure);
+ */
+
+/* Specific source types
+ */
+GSource *g_idle_source_new    (void);
+GSource *g_timeout_source_new (guint         interval);
+
+/* Miscellaneous functions
+ */
+void g_get_current_time                        (GTimeVal       *result);
+
+/* ============== Compat main loop stuff ================== */
+
+/* Legacy names for GMainLoop functions
+ */
+#define        g_main_new(is_running)  g_main_loop_new (NULL, is_running);
+#define         g_main_run(loop)        g_main_loop_run(loop)
+#define         g_main_quit(loop)       g_main_loop_quit(loop)
+#define         g_main_destroy(loop)    g_main_loop_destroy(loop)
+#define         g_main_is_running(loop) g_main_loop_is_running(loop)
+
+/* Source manipulation by ID */
+gboolean g_source_remove                     (guint          tag);
+gboolean g_source_remove_by_user_data        (gpointer       user_data);
+gboolean g_source_remove_by_funcs_user_data  (GSourceFuncs  *funcs,
+                                             gpointer       user_data);
+
+/* Functions to manipulate the default main loop
+ */
+
+#define        g_main_iteration(may_block) g_main_context_iteration      (NULL, may_block)
+#define g_main_pending()            g_main_context_pending        (NULL)
+
+#define g_main_set_poll_func(func)   g_main_context_set_poll_func (NULL, func)
+
+/* Idles and timeouts */
+guint          g_timeout_add_full      (gint           priority,
+                                        guint          interval, 
+                                        GSourceFunc    function,
+                                        gpointer       data,
+                                        GDestroyNotify notify);
+guint          g_timeout_add           (guint          interval,
+                                        GSourceFunc    function,
+                                        gpointer       data);
+guint          g_idle_add              (GSourceFunc    function,
+                                        gpointer       data);
+guint          g_idle_add_full         (gint           priority,
+                                        GSourceFunc    function,
+                                        gpointer       data,
+                                        GDestroyNotify notify);
+gboolean       g_idle_remove_by_data   (gpointer       data);
+
+#ifdef G_OS_WIN32
+
+/* This is used to add polling for Windows messages. GDK (GTK+) programs
+ * should *not* use this.
+ */
+void        g_main_poll_win32_msg_add (gint        priority,
+                                      GPollFD    *fd,
+                                      guint       hwnd);
+#endif G_OS_WIN32
 
 G_END_DECLS
 
 #endif /* __G_MAIN_H__ */
-
index e99c78d5e4b2cab78572b0bcf480423ab807bcce..12681784be2be1807ccf7bed16254931828d8eb3 100644 (file)
@@ -28,7 +28,6 @@
 #define __G_THREAD_H__
 
 #include <gerror.h>
-#include <gmain.h>
 
 G_BEGIN_DECLS
 
index c43d322a70c6806632176d830d68feee5686b5d1..9614c73db4072e12a89e622f227e91d85b01a736 100644 (file)
@@ -314,6 +314,14 @@ union _GDoubleIEEE754
 #error unknown ENDIAN type
 #endif /* !G_LITTLE_ENDIAN && !G_BIG_ENDIAN */
 
+typedef struct _GTimeVal                GTimeVal;
+
+struct _GTimeVal
+{
+  glong tv_sec;
+  glong tv_usec;
+};
+
 G_END_DECLS
 
 #endif /* __G_TYPES_H__ */
diff --git a/gmain.c b/gmain.c
index ad03a35c0457d47549df15fd45fe75cc9a6505f0..d87022c30c62c90c60e0be4331d8a78dbf5709e2 100644 (file)
--- a/gmain.c
+++ b/gmain.c
@@ -63,9 +63,9 @@
 
 /* Types */
 
-typedef struct _GTimeoutData GTimeoutData;
-typedef struct _GSource GSource;
+typedef struct _GTimeoutSource GTimeoutSource;
 typedef struct _GPollRec GPollRec;
+typedef struct _GSourceCallback GSourceCallback;
 
 typedef enum
 {
@@ -73,23 +73,76 @@ typedef enum
   G_SOURCE_CAN_RECURSE = 1 << (G_HOOK_FLAG_USER_SHIFT + 1)
 } GSourceFlags;
 
-struct _GSource
+struct _GMainContext
 {
-  GHook hook;
-  gint priority;
-  gpointer source_data;
+#ifdef G_THREADS_ENABLED
+  /* The following lock is used for both the list of sources
+   * and the list of poll records
+   */
+  GMutex *mutex;
+  GThread *thread;
+#endif  
+
+  GPtrArray *pending_dispatches;
+  gint timeout;                        /* Timeout for current iteration */
+
+  guint next_id;
+  GSource *source_list;
+  gint in_check_or_prepare;
+
+  GPollRec *poll_records;
+  GPollRec *poll_free_list;
+  GMemChunk *poll_chunk;
+  guint n_poll_records;
+  GPollFD *cached_poll_array;
+  gint cached_poll_array_size;
+
+#ifdef G_THREADS_ENABLED  
+#ifndef G_OS_WIN32
+/* this pipe is used to wake up the main loop when a source is added.
+ */
+  gint wake_up_pipe[2];
+#else /* G_OS_WIN32 */
+  HANDLE wake_up_semaphore;
+#endif /* G_OS_WIN32 */
+
+  GPollFD wake_up_rec;
+  gboolean poll_waiting;
+
+/* Flag indicating whether the set of fd's changed during a poll */
+  gboolean poll_changed;
+#endif /* G_THREADS_ENABLED */
+
+  GPollFunc poll_func;
+
+  GTimeVal current_time;
+  gboolean time_is_current;
+};
+
+struct _GSourceCallback
+{
+  guint ref_count;
+  GSourceFunc func;
+  gpointer    data;
+  GDestroyNotify notify;
 };
 
 struct _GMainLoop
 {
+  GMainContext *context;
   gboolean is_running;
+
+#ifdef G_THREADS_ENABLED
+  GMutex *mutex;
+  GCond *sem_cond;
+#endif G_THREADS_ENABLED  
 };
 
-struct _GTimeoutData
+struct _GTimeoutSource
 {
+  GSource     source;
   GTimeVal    expiration;
   gint        interval;
-  GSourceFunc callback;
 };
 
 struct _GPollRec
@@ -99,57 +152,71 @@ struct _GPollRec
   GPollRec *next;
 };
 
+#ifdef G_THREADS_ENABLED
+#define LOCK_CONTEXT(context) g_mutex_lock(context->mutex)
+#define UNLOCK_CONTEXT(context) g_mutex_unlock(context->mutex)
+#define LOCK_LOOP(loop) g_mutex_lock(loop->mutex)
+#define UNLOCK_LOOP(loop) g_mutex_unlock(loop->mutex)
+#else
+#define LOCK_CONTEXT(context) (void)0
+#define UNLOCK_CONTEXT(context) (void)0
+#define LOCK_LOOP(context) (void)0
+#define UNLOCK_LOOP(context) (void)0
+#endif
+
+#define SOURCE_DESTROYED(source) (((source)->flags & G_HOOK_FLAG_ACTIVE) == 0)
+
+#define SOURCE_UNREF(source, context)                       \
+   G_STMT_START {                                           \
+    if ((source)->ref_count > 1)                            \
+      (source)->ref_count--;                                \
+    else                                                    \
+      g_source_unref_internal ((source), (context), TRUE);  \
+   } G_STMT_END
+
+
 /* Forward declarations */
 
-static gint     g_source_compare          (GHook      *a,
-                                          GHook      *b);
-static void     g_source_destroy_func     (GHookList  *hook_list,
-                                          GHook      *hook);
-static void     g_main_poll               (gint      timeout,
-                                          gboolean  use_priority, 
-                                          gint      priority);
-static void     g_main_add_poll_unlocked  (gint      priority,
-                                          GPollFD  *fd);
-static void     g_main_wakeup             (void);
-
-static gboolean g_timeout_prepare      (gpointer  source_data, 
-                                       GTimeVal *current_time,
-                                       gint     *timeout,
-                                       gpointer  user_data);
-static gboolean g_timeout_check        (gpointer  source_data,
-                                       GTimeVal *current_time,
-                                       gpointer  user_data);
-static gboolean g_timeout_dispatch     (gpointer  source_data,
-                                       GTimeVal *dispatch_time,
-                                       gpointer  user_data);
-static gboolean g_idle_prepare         (gpointer  source_data, 
-                                       GTimeVal *current_time,
-                                       gint     *timeout,
-                                       gpointer  user_data);
-static gboolean g_idle_check           (gpointer  source_data,
-                                       GTimeVal *current_time,
-                                       gpointer  user_data);
-static gboolean g_idle_dispatch        (gpointer  source_data,
-                                       GTimeVal *dispatch_time,
-                                       gpointer  user_data);
-
-/* Data */
-
-static GSList *pending_dispatches = NULL;
-static GHookList source_list = { 0 };
-static gint in_check_or_prepare = 0;
-
-/* The following lock is used for both the list of sources
- * and the list of poll records
- */
+static void g_source_unref_internal             (GSource      *source,
+                                                GMainContext *context,
+                                                gboolean      have_lock);
+static void g_source_destroy_internal           (GSource      *source,
+                                                GMainContext *context,
+                                                gboolean      have_lock);
+static void g_main_context_poll                 (GMainContext *context,
+                                                gint          timeout,
+                                                gint          priority,
+                                                GPollFD      *fds,
+                                                gint          n_fds);
+static void g_main_context_add_poll_unlocked    (GMainContext *context,
+                                                gint          priority,
+                                                GPollFD      *fd);
+static void g_main_context_remove_poll_unlocked (GMainContext *context,
+                                                GPollFD      *fd);
+static void g_main_context_wakeup               (GMainContext *context);
+
+static gboolean g_timeout_prepare  (GSource     *source,
+                                   gint        *timeout);
+static gboolean g_timeout_check    (GSource     *source);
+static gboolean g_timeout_dispatch (GSource     *source,
+                                   GSourceFunc  callback,
+                                   gpointer     user_data);
+static gboolean g_idle_prepare     (GSource     *source,
+                                   gint        *timeout);
+static gboolean g_idle_check       (GSource     *source);
+static gboolean g_idle_dispatch    (GSource     *source,
+                                   GSourceFunc  callback,
+                                   gpointer     user_data);
+
 G_LOCK_DEFINE_STATIC (main_loop);
+static GMainContext *default_main_context;
 
 static GSourceFuncs timeout_funcs =
 {
   g_timeout_prepare,
   g_timeout_check,
   g_timeout_dispatch,
-  g_free,
+  NULL
 };
 
 static GSourceFuncs idle_funcs =
@@ -157,36 +224,16 @@ static GSourceFuncs idle_funcs =
   g_idle_prepare,
   g_idle_check,
   g_idle_dispatch,
-  NULL,
+  NULL
 };
 
-static GPollRec *poll_records = NULL;
-static GPollRec *poll_free_list = NULL;
-static GMemChunk *poll_chunk;
-static guint n_poll_records = 0;
-
-#ifdef G_THREADS_ENABLED
-#ifndef G_OS_WIN32
-/* this pipe is used to wake up the main loop when a source is added.
- */
-static gint wake_up_pipe[2] = { -1, -1 };
-#else /* G_OS_WIN32 */
-static HANDLE wake_up_semaphore = NULL;
-#endif /* G_OS_WIN32 */
-static GPollFD wake_up_rec;
-static gboolean poll_waiting = FALSE;
-
-/* Flag indicating whether the set of fd's changed during a poll */
-static gboolean poll_changed = FALSE;
-#endif /* G_THREADS_ENABLED */
-
 #ifdef HAVE_POLL
 /* SunOS has poll, but doesn't provide a prototype. */
 #  if defined (sun) && !defined (__SVR4)
 extern gint poll (GPollFD *ufds, guint nfsd, gint timeout);
 #  endif  /* !sun */
-static GPollFunc poll_func = (GPollFunc) poll;
 #else  /* !HAVE_POLL */
+
 #ifdef G_OS_WIN32
 
 static gint
@@ -436,656 +483,1723 @@ g_poll (GPollFD *fds,
 
 #endif /* !G_OS_WIN32 */
 
-static GPollFunc poll_func = g_poll;
 #endif /* !HAVE_POLL */
 
-/* Hooks for adding to the main loop */
-
-/* Use knowledge of insert_sorted algorithm here to make
- * sure we insert at the end of equal priority items
+/* Called to clean up when a thread terminates
  */
-static gint
-g_source_compare (GHook *a,
-                 GHook *b)
-{
-  GSource *source_a = (GSource *)a;
-  GSource *source_b = (GSource *)b;
-
-  return (source_a->priority < source_b->priority) ? -1 : 1;
-}
-
-/* HOLDS: main_loop lock */
 static void
-g_source_destroy_func (GHookList *hook_list,
-                      GHook     *hook)
+g_main_context_destroy (GMainContext *context)
 {
-  GSource *source = (GSource*) hook;
-  GDestroyNotify destroy;
+  GSource *source;
 
-  G_UNLOCK (main_loop);
+  /* We need the lock here only because g_source_destroy expects
+   * to be able to unlock when destroying the source's data
+   */
+  LOCK_CONTEXT (context);
+  source = context->source_list;
+  while (source)
+    {
+      GSource *next = source->next;
+      g_source_destroy_internal (source, context, TRUE);
+      source = next;
+    }
+  UNLOCK_CONTEXT (context);
 
-  destroy = hook->destroy;
-  if (destroy)
-    destroy (hook->data);
+#ifdef G_THREADS_ENABLED  
+  g_mutex_free (context->mutex);
+#endif
 
-  destroy = ((GSourceFuncs*) hook->func)->destroy;
-  if (destroy)
-    destroy (source->source_data);
+  g_ptr_array_free (context->pending_dispatches, TRUE);
+  g_free (context->cached_poll_array);
+  
+  g_mem_chunk_destroy (context->poll_chunk);
 
-  G_LOCK (main_loop);
+  if (g_thread_supported())
+    {
+#ifndef G_OS_WIN32
+      close (context->wake_up_pipe[0]);
+      close (context->wake_up_pipe[1]);
+#else
+      CloseHandle (context->wake_up_semaphore);
+#endif
+    }
+  
+  g_free (context);
 }
 
-guint 
-g_source_add (gint           priority,
-             gboolean       can_recurse,
-             GSourceFuncs  *funcs,
-             gpointer       source_data, 
-             gpointer       user_data,
-             GDestroyNotify notify)
-{
-  guint return_val;
-  GSource *source;
+/**
+ * g_main_context_get:
+ * @thread: a #GThread
+ * 
+ * Retrieves the main loop context for a particular thread. This
+ * will create the main context for the thread if none previously
+ * existed. The context will exist until the thread terminates.
+ * 
+ * Return value: the main loop context for @thread.
+ **/
+GMainContext *
+g_main_context_get (GThread *thread)
+{
+  static GStaticPrivate private_key = G_STATIC_PRIVATE_INIT;
+  GMainContext *context;
 
-  G_LOCK (main_loop);
+  g_return_val_if_fail (thread != NULL, NULL);
+
+  if (g_thread_supported ())
+    context = g_static_private_get_for_thread (&private_key, thread);
+  else
+    context = default_main_context;
 
-  if (!source_list.is_setup)
+  if (!context)
     {
-      g_hook_list_init (&source_list, sizeof (GSource));
+      context = g_new0 (GMainContext, 1);
 
-      source_list.hook_destroy = G_HOOK_DEFERRED_DESTROY;
-      source_list.hook_free = g_source_destroy_func;
-    }
+#ifdef G_THREADS_ENABLED
+      if (g_thread_supported ())
+       context->mutex = g_mutex_new();
 
-  source = (GSource*) g_hook_alloc (&source_list);
-  source->priority = priority;
-  source->source_data = source_data;
-  source->hook.func = funcs;
-  source->hook.data = user_data;
-  source->hook.destroy = notify;
-  
-  g_hook_insert_sorted (&source_list, 
-                       (GHook *)source, 
-                       g_source_compare);
+      context->thread = thread;
+#endif
+      
+      context->next_id = 1;
+      
+      context->source_list = NULL;
 
-  if (can_recurse)
-    source->hook.flags |= G_SOURCE_CAN_RECURSE;
+#if HAVE_POLL
+      context->poll_func = (GPollFunc)poll;
+#else
+      context->poll_func = g_poll;
+#endif
 
-  return_val = source->hook.hook_id;
+      context->cached_poll_array = NULL;
+      context->cached_poll_array_size = 0;
+      
+      context->pending_dispatches = g_ptr_array_new ();
+      
+      context->time_is_current = FALSE;
 
 #ifdef G_THREADS_ENABLED
-  /* Now wake up the main loop if it is waiting in the poll() */
-  g_main_wakeup ();
+      if (g_thread_supported ())
+       {
+#ifndef G_OS_WIN32
+         if (pipe (context->wake_up_pipe) < 0)
+           g_error ("Cannot create pipe main loop wake-up: %s\n",
+                    g_strerror (errno));
+         
+         context->wake_up_rec.fd = context->wake_up_pipe[0];
+         context->wake_up_rec.events = G_IO_IN;
+         g_main_context_add_poll_unlocked (context, 0, &context->wake_up_rec);
+#else
+         if ((context->wake_up_semaphore = CreateSemaphore (NULL, 0, 100, NULL)) == NULL)
+           g_error ("Cannot create wake-up semaphore: %s", g_win32_error_message (GetLastError ()));
+         context->wake_up_rec.fd = (gint) context->wake_up_semaphore;
+         context->wake_up_rec.events = G_IO_IN;
+#ifdef G_MAIN_POLL_DEBUG
+         g_print ("wake-up semaphore: %#x\n", (guint) context->wake_up_semaphore);
 #endif
-  
-  G_UNLOCK (main_loop);
+         g_main_context_add_poll_unlocked (context, 0, &context->wake_up_rec);
+#endif
+       }
+#endif
+
+      if (g_thread_supported ())
+       g_static_private_set_for_thread (&private_key, thread,
+                                        context,
+                                        (GDestroyNotify)g_main_context_destroy);
+      else
+       default_main_context = context;
+    }
 
-  return return_val;
+  return context;
 }
 
-gboolean
-g_source_remove (guint tag)
+/**
+ * g_main_context_default:
+ * 
+ * Return the default main context. This is the main context used
+ * for main loop functions when a main loop is not explicitly
+ * specified.
+ * 
+ * Return value: the default main context.
+ **/
+GMainContext *
+g_main_context_default (void)
 {
-  GHook *hook;
-
-  g_return_val_if_fail (tag > 0, FALSE);
-
+  /* Slow, but safe */
+  
   G_LOCK (main_loop);
 
-  hook = g_hook_get (&source_list, tag);
-  if (hook)
-    g_hook_destroy_link (&source_list, hook);
+  if (!default_main_context)
+    default_main_context = g_main_context_get (g_thread_self ());
 
   G_UNLOCK (main_loop);
 
-  return hook != NULL;
+  return default_main_context;
 }
 
-gboolean
-g_source_remove_by_user_data (gpointer user_data)
+/* Hooks for adding to the main loop */
+
+/**
+ * g_source_new:
+ * @source_funcs: structure containing functions that implement
+ *                the sources behavior.
+ * @struct_size: size of the #GSource structure to create
+ * 
+ * Create a new GSource structure. The size is specified to
+ * allow creating structures derived from GSource that contain
+ * additional data. The size passed in must be at least
+ * sizeof(GSource).
+ * 
+ * The source will not initially be associated with any #GMainContext
+ * and must be added to one with g_source_add() before it will be
+ * executed.
+ * 
+ * Return value: the newly create #GSource
+ **/
+GSource *
+g_source_new (GSourceFuncs *source_funcs,
+             guint         struct_size)
 {
-  GHook *hook;
+  GSource *source;
+
+  g_return_val_if_fail (source_funcs != NULL, NULL);
+  g_return_val_if_fail (struct_size >= sizeof (GSource), NULL);
   
-  G_LOCK (main_loop);
+  source = (GSource*) g_malloc0 (struct_size);
+
+  source->source_funcs = source_funcs;
+  source->ref_count = 1;
   
-  hook = g_hook_find_data (&source_list, TRUE, user_data);
-  if (hook)
-    g_hook_destroy_link (&source_list, hook);
+  source->priority = G_PRIORITY_DEFAULT;
 
-  G_UNLOCK (main_loop);
+  source->flags = G_HOOK_FLAG_ACTIVE;
 
-  return hook != NULL;
+  /* NULL/0 initialization for all other fields */
+  
+  return source;
 }
 
-static gboolean
-g_source_find_source_data (GHook       *hook,
-                          gpointer      data)
+/* Holds context's lock
+ */
+static void
+g_source_list_add (GSource      *source,
+                  GMainContext *context)
 {
-  GSource *source = (GSource *)hook;
+  GSource *tmp_source, *last_source;
+  
+  last_source = NULL;
+  tmp_source = context->source_list;
+  while (tmp_source && tmp_source->priority <= source->priority)
+    {
+      last_source = tmp_source;
+      tmp_source = tmp_source->next;
+    }
 
-  return (source->source_data == data);
+  source->next = tmp_source;
+  if (tmp_source)
+    tmp_source->prev = source;
+  
+  source->prev = last_source;
+  if (last_source)
+    last_source->next = source;
+  else
+    context->source_list = source;
 }
 
-gboolean
-g_source_remove_by_source_data (gpointer source_data)
+/* Holds context's lock
+ */
+static void
+g_source_list_remove (GSource      *source,
+                     GMainContext *context)
 {
-  GHook *hook;
-
-  G_LOCK (main_loop);
-
-  hook = g_hook_find (&source_list, TRUE, 
-                     g_source_find_source_data, source_data);
-  if (hook)
-    g_hook_destroy_link (&source_list, hook);
+  if (source->prev)
+    source->prev->next = source->next;
+  else
+    context->source_list = source->next;
 
-  G_UNLOCK (main_loop);
+  if (source->next)
+    source->next->prev = source->prev;
 
-  return hook != NULL;
+  source->prev = NULL;
+  source->next = NULL;
 }
 
-static gboolean
-g_source_find_funcs_user_data (GHook   *hook,
-                              gpointer data)
+/**
+ * g_source_attach:
+ * @source: a #GSource
+ * @context: a #GMainContext (if %NULL, the default context will be used)
+ * 
+ * Adds a #GSource to a @context so that it will be executed within
+ * that context.
+ *
+ * Return value: the ID for the source within the #GMainContext
+ **/
+guint
+g_source_attach (GSource      *source,
+                GMainContext *context)
 {
-  gpointer *d = data;
+  guint result = 0;
+  GSList *tmp_list;
 
-  return hook->func == d[0] && hook->data == d[1];
-}
+  g_return_val_if_fail (source->context == NULL, 0);
+  g_return_val_if_fail (!SOURCE_DESTROYED (source), 0);
+  
+  if (!context)
+    context = g_main_context_default ();
 
-gboolean
-g_source_remove_by_funcs_user_data (GSourceFuncs *funcs,
-                                   gpointer      user_data)
-{
-  gpointer d[2];
-  GHook *hook;
+  LOCK_CONTEXT (context);
 
-  g_return_val_if_fail (funcs != NULL, FALSE);
+  source->context = context;
+  result = source->id = context->next_id++;
 
-  G_LOCK (main_loop);
+  g_source_list_add (source, context);
 
-  d[0] = funcs;
-  d[1] = user_data;
+  tmp_list = source->poll_fds;
+  while (tmp_list)
+    {
+      g_main_context_add_poll_unlocked (context, source->priority, tmp_list->data);
+      tmp_list = tmp_list->next;
+    }
 
-  hook = g_hook_find (&source_list, TRUE,
-                     g_source_find_funcs_user_data, d);
-  if (hook)
-    g_hook_destroy_link (&source_list, hook);
+#ifdef G_THREADS_ENABLED
+  /* Now wake up the main loop if it is waiting in the poll() */
+  g_main_context_wakeup (context);
+#endif
 
-  G_UNLOCK (main_loop);
+  UNLOCK_CONTEXT (context);
 
-  return hook != NULL;
+  return result;
 }
 
-void
-g_get_current_time (GTimeVal *result)
+static void
+g_source_destroy_internal (GSource      *source,
+                          GMainContext *context,
+                          gboolean      have_lock)
 {
-#ifndef G_OS_WIN32
-  struct timeval r;
-
-  g_return_if_fail (result != NULL);
+  if (!have_lock)
+    LOCK_CONTEXT (context);
+  
+  if (!SOURCE_DESTROYED (source))
+    {
+      gpointer old_cb_data;
+      GSourceCallbackFuncs *old_cb_funcs;
+      
+      source->flags &= ~G_HOOK_FLAG_ACTIVE;
 
-  /*this is required on alpha, there the timeval structs are int's
-    not longs and a cast only would fail horribly*/
-  gettimeofday (&r, NULL);
-  result->tv_sec = r.tv_sec;
-  result->tv_usec = r.tv_usec;
-#else
-  FILETIME filetime;
-  gint64 t;
+      old_cb_data = source->callback_data;
+      old_cb_funcs = source->callback_funcs;
 
-  g_return_if_fail (result != NULL);
+      source->callback_data = NULL;
+      source->callback_funcs = NULL;
 
-  GetSystemTimeAsFileTime (&filetime);
-  t = ((gint64) filetime.dwLowDateTime) +
-    ((gint64) filetime.dwHighDateTime) * G_GINT64_CONSTANT (0x100000000);
+      if (old_cb_funcs)
+       {
+         UNLOCK_CONTEXT (context);
+         old_cb_funcs->unref (old_cb_data);
+         LOCK_CONTEXT (context);
+       }
+      
+      g_source_unref_internal (source, context, TRUE);
+    }
 
-  result->tv_sec = (t - G_GINT64_CONSTANT (0x19db1ded53e8000)) / 10000000;
-  result->tv_usec = (t % 10000000) / 10;
-#endif
+  if (!have_lock)
+    UNLOCK_CONTEXT (context);
 }
 
-/* Running the main loop */
+/**
+ * g_source_destroy:
+ * @source: a #GSource
+ * 
+ * Remove a source from its #GMainContext, if any, and mark it as
+ * destroyed.  The source cannot be subsequently added to another
+ * context.
+ **/
+void
+g_source_destroy (GSource *source)
+{
+  GMainContext *context;
+  
+  g_return_if_fail (source != NULL);
+  
+  context = source->context;
+  
+  if (context)
+    g_source_destroy_internal (source, context, FALSE);
+  else
+    source->flags &= ~G_HOOK_FLAG_ACTIVE;
+}
 
-/* HOLDS: main_loop_lock */
-static void
-g_main_dispatch (GTimeVal *dispatch_time)
+/**
+ * g_source_get_id:
+ * @source: a #GSource
+ * 
+ * Return the numeric ID for a particular source. The ID of a source
+ * is unique within a particular main loop context. The reverse
+ * mapping from ID to source is done by g_main_context_find_source_by_id().
+ *
+ * Return value: the ID for the source
+ **/
+guint
+g_source_get_id (GSource *source)
 {
-  while (pending_dispatches != NULL)
-    {
-      gboolean need_destroy;
-      GSource *source = pending_dispatches->data;
-      GSList *tmp_list;
+  guint result;
+  
+  g_return_val_if_fail (source != NULL, 0);
+  g_return_val_if_fail (source->context != NULL, 0);
+
+  LOCK_CONTEXT (source->context);
+  result = source->id;
+  UNLOCK_CONTEXT (source->context);
+  
+  return result;
+}
 
-      tmp_list = pending_dispatches;
-      pending_dispatches = g_slist_remove_link (pending_dispatches, pending_dispatches);
-      g_slist_free_1 (tmp_list);
+/**
+ * g_source_get_context:
+ * @source: a #GSource
+ * 
+ * Get the #GMainContext with which the source is associated.
+ * Calling this function on a destroyed source is an error.
+ * 
+ * Return value: the #GMainContext with which the source is associated,
+ *               or %NULL if the context has not yet been added
+ *               to a source.
+ **/
+GMainContext *
+g_source_get_context (GSource *source)
+{
+  g_return_val_if_fail (!SOURCE_DESTROYED (source), NULL);
 
-      if (G_HOOK_IS_VALID (source))
-       {
-         gboolean was_in_call;
-         gpointer hook_data = source->hook.data;
-         gpointer source_data = source->source_data;
-         gboolean (*dispatch) (gpointer,
-                               GTimeVal *,
-                               gpointer);
+  return source->context;
+}
 
-         dispatch = ((GSourceFuncs *) source->hook.func)->dispatch;
-         
-         was_in_call = G_HOOK_IN_CALL (source);
-         source->hook.flags |= G_HOOK_FLAG_IN_CALL;
+/**
+ * g_main_source_add_poll:
+ * @source:a #GSource 
+ * @fd: a #GPollFD structure holding information about a file
+ *      descriptor to watch.
+ * 
+ * Add a file descriptor to the set of file descriptors polled * for
+ * this source. This is usually combined with g_source_new() to add an
+ * event source. The event source's check function will typically test
+ * the revents field in the #GPollFD struct and return %TRUE if events need
+ * to be processed.
+ **/
+void
+g_source_add_poll (GSource *source,
+                  GPollFD *fd)
+{
+  GMainContext *context;
+  
+  g_return_if_fail (source != NULL);
+  g_return_if_fail (fd != NULL);
 
-         G_UNLOCK (main_loop);
-         need_destroy = ! dispatch (source_data,
-                                    dispatch_time,
-                                    hook_data);
-         G_LOCK (main_loop);
+  context = source->context;
 
-         if (!was_in_call)
-           source->hook.flags &= ~G_HOOK_FLAG_IN_CALL;
-         
-         if (need_destroy && G_HOOK_IS_VALID (source))
-           g_hook_destroy_link (&source_list, (GHook *) source);
-       }
+  if (context)
+    LOCK_CONTEXT (context);
+  
+  source->poll_fds = g_slist_prepend (source->poll_fds, fd);
 
-      g_hook_unref (&source_list, (GHook*) source);
+  if (context)
+    {
+      g_main_context_add_poll_unlocked (context, source->priority, fd);
+      UNLOCK_CONTEXT (context);
     }
 }
 
-/* g_main_iterate () runs a single iteration of the mainloop, or,
- * if !dispatch checks to see if any sources need dispatching.
- * basic algorithm for dispatch=TRUE:
- *
- * 1) while the list of currently pending sources is non-empty,
- *    we call (*dispatch) on those that are !IN_CALL or can_recurse,
- *    removing sources from the list after each returns.
- *    the return value of (*dispatch) determines whether the source
- *    itself is kept alive.
- *
- * 2) call (*prepare) for sources that are not yet SOURCE_READY and
- *    are !IN_CALL or can_recurse. a return value of TRUE determines
- *    that the source would like to be dispatched immediatedly, it
- *    is then flagged as SOURCE_READY.
- *
- * 3) poll with the pollfds from all sources at the priority of the
- *    first source flagged as SOURCE_READY. if there are any sources
- *    flagged as SOURCE_READY, we use a timeout of 0 or the minimum
- *    of all timouts otherwise.
- *
- * 4) for each source !IN_CALL or can_recurse, if SOURCE_READY or
- *    (*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.
- *
- */
-static gboolean
-g_main_iterate (gboolean block,
-               gboolean dispatch)
+/**
+ * g_source_set_callback_indirect:
+ * @source: the source
+ * @callback_data: pointer to callback data "object"
+ * @callback_funcs: functions for reference counting callback_data
+ *                  and getting the callback and data
+ * 
+ * Set the callback function storing the data as a refcounted callback
+ * "object". This is used to implement g_source_set_callback_closure()
+ * and internally. Note that calling g_source_set_callback_indirect() assumes
+ * an initial reference count on @callback_data, and thus
+ * @callback_funcs->unref will eventually be called once more
+ * than @callback_funcs->ref.
+ **/
+void
+g_source_set_callback_indirect (GSource              *source,
+                               gpointer              callback_data,
+                               GSourceCallbackFuncs *callback_funcs)
 {
-  GHook *hook;
-  GTimeVal current_time  = { 0, 0 };
-  gint n_ready = 0;
-  gint current_priority = 0;
-  gint timeout;
-  gboolean retval = FALSE;
+  GMainContext *context;
+  gpointer old_cb_data;
+  GSourceCallbackFuncs *old_cb_funcs;
+  
+  g_return_if_fail (source != NULL);
+  g_return_if_fail (callback_funcs != NULL || callback_data == NULL);
 
-  g_return_val_if_fail (!block || dispatch, FALSE);
+  context = source->context;
 
-  g_get_current_time (&current_time);
+  if (context)
+    LOCK_CONTEXT (context);
 
-  G_LOCK (main_loop);
+  old_cb_data = source->callback_data;
+  old_cb_funcs = source->callback_funcs;
 
-#ifdef G_THREADS_ENABLED
-  if (poll_waiting)
-    {
-      g_warning("g_main_iterate(): main loop already active in another thread");
-      G_UNLOCK (main_loop);
-      return FALSE;
-    }
-#endif /* G_THREADS_ENABLED */
+  source->callback_data = callback_data;
+  source->callback_funcs = callback_funcs;
   
-  /* If recursing, finish up current dispatch, before starting over */
-  if (pending_dispatches)
-    {
-      if (dispatch)
-       g_main_dispatch (&current_time);
-      
-      G_UNLOCK (main_loop);
+  if (context)
+    UNLOCK_CONTEXT (context);
+  
+  if (old_cb_funcs)
+    old_cb_funcs->unref (old_cb_data);
+}
 
-      return TRUE;
-    }
+static void
+g_source_callback_ref (gpointer cb_data)
+{
+  GSourceCallback *callback = cb_data;
 
-  /* Prepare all sources */
+  callback->ref_count++;
+}
 
-  timeout = block ? -1 : 0;
-  
-  hook = g_hook_first_valid (&source_list, TRUE);
-  while (hook)
+
+static void
+g_source_callback_unref (gpointer cb_data)
+{
+  GSourceCallback *callback = cb_data;
+
+  callback->ref_count--;
+  if (callback->ref_count == 0)
     {
-      GSource *source = (GSource*) hook;
-      gint source_timeout = -1;
+      if (callback->notify)
+       callback->notify (callback->data);
+    }
+}
 
-      if ((n_ready > 0) && (source->priority > current_priority))
-       {
-         g_hook_unref (&source_list, hook);
-         break;
-       }
-      if (G_HOOK_IN_CALL (hook) && !(hook->flags & G_SOURCE_CAN_RECURSE))
-       {
-         hook = g_hook_next_valid (&source_list, hook, TRUE);
-         continue;
-       }
+static void
+g_source_callback_get (gpointer     cb_data,
+                      GSourceFunc *func,
+                      gpointer    *data)
+{
+  GSourceCallback *callback = cb_data;
 
-      if (!(hook->flags & G_SOURCE_READY))
-       {
-         gboolean (*prepare)  (gpointer  source_data, 
-                               GTimeVal *current_time,
-                               gint     *timeout,
-                               gpointer  user_data);
+  *func = callback->func;
+  *data = callback->data;
+}
 
-         prepare = ((GSourceFuncs *) hook->func)->prepare;
-         in_check_or_prepare++;
-         G_UNLOCK (main_loop);
+static GSourceCallbackFuncs g_source_callback_funcs = {
+  g_source_callback_ref,
+  g_source_callback_unref,
+  g_source_callback_get,
+};
 
-         if ((*prepare) (source->source_data, &current_time, &source_timeout, source->hook.data))
-           hook->flags |= G_SOURCE_READY;
-         
-         G_LOCK (main_loop);
-         in_check_or_prepare--;
-       }
+/**
+ * g_source_set_callback:
+ * @source: the source
+ * @func: a callback function
+ * @data: the data to pass to callback function
+ * @notify: a function to call when @data is no longer in use, or %NULL.
+ * 
+ * Set the callback function for a source.
+ **/
+void
+g_source_set_callback (GSource        *source,
+                      GSourceFunc     func,
+                      gpointer        data,
+                      GDestroyNotify  notify)
+{
+  GSourceCallback *new_callback;
 
-      if (hook->flags & G_SOURCE_READY)
-       {
-         if (!dispatch)
-           {
-             g_hook_unref (&source_list, hook);
-             G_UNLOCK (main_loop);
+  g_return_if_fail (source != NULL);
 
-             return TRUE;
-           }
-         else
+  new_callback = g_new (GSourceCallback, 1);
+  
+  new_callback->func = func;
+  new_callback->data = data;
+  new_callback->notify = notify;
+
+  g_source_set_callback_indirect (source, new_callback, &g_source_callback_funcs);
+}
+
+/**
+ * g_source_set_priority:
+ * @source: a #GSource
+ * @priority: the new priority.
+ * 
+ * Set the priority of a source. While the main loop is being
+ * run, a source will 
+ **/
+void
+g_source_set_priority (GSource  *source,
+                      gint      priority)
+{
+  GSList *tmp_list;
+  GMainContext *context;
+  
+  g_return_if_fail (source != NULL);
+
+  context = source->context;
+
+  if (context)
+    LOCK_CONTEXT (context);
+  
+  source->priority = priority;
+
+  if (context)
+    {
+      source->next = NULL;
+      source->prev = NULL;
+      
+      tmp_list = source->poll_fds;
+      while (tmp_list)
+       {
+         g_main_context_remove_poll_unlocked (context, tmp_list->data);
+         g_main_context_add_poll_unlocked (context, priority, tmp_list->data);
+      
+         tmp_list = tmp_list->next;
+       }
+      
+      UNLOCK_CONTEXT (source->context);
+    }
+}
+
+/**
+ * g_source_get_priority:
+ * @source: a #GSource
+ * 
+ * Gets the priority of a surce
+ * 
+ * Return value: the priority of the source
+ **/
+gint
+g_source_get_priority (GSource *source)
+{
+  g_return_val_if_fail (source != NULL, 0);
+
+  return source->priority;
+}
+
+/**
+ * g_source_set_can_recurse:
+ * @source: a #GSource
+ * @can_recurse: whether recursion is allowed for this source
+ * 
+ * Sets whether a source can be called recursively. If @can_recurse is
+ * %TRUE, then while the source is being dispatched then this source
+ * will be processed normally. Otherwise, all processing of this
+ * source is blocked until the dispatch function returns.
+ **/
+void
+g_source_set_can_recurse (GSource  *source,
+                         gboolean  can_recurse)
+{
+  GMainContext *context;
+  
+  g_return_if_fail (source != NULL);
+
+  context = source->context;
+
+  if (context)
+    LOCK_CONTEXT (context);
+  
+  if (can_recurse)
+    source->flags |= G_SOURCE_CAN_RECURSE;
+  else
+    source->flags &= ~G_SOURCE_CAN_RECURSE;
+
+  if (context)
+    UNLOCK_CONTEXT (context);
+}
+
+/**
+ * g_source_get_can_recurse:
+ * @source: a #GSource
+ * 
+ * Checks whether a source is allowed to be called recursively.
+ * see g_source_set_can_recurse.
+ * 
+ * Return value: whether recursion is allowed.
+ **/
+gboolean
+g_source_get_can_recurse (GSource  *source)
+{
+  g_return_val_if_fail (source != NULL, FALSE);
+  
+  return (source->flags & G_SOURCE_CAN_RECURSE) != 0;
+}
+
+/**
+ * g_source_ref:
+ * @source: a #GSource
+ * 
+ * Increases the reference count on a source by one.
+ * 
+ * Return value: @source
+ **/
+GSource *
+g_source_ref (GSource *source)
+{
+  GMainContext *context;
+  
+  g_return_val_if_fail (source != NULL, NULL);
+
+  context = source->context;
+
+  if (context)
+    LOCK_CONTEXT (context);
+
+  source->ref_count++;
+
+  if (context)
+    UNLOCK_CONTEXT (context);
+
+  return source;
+}
+
+/* g_source_unref() but possible to call within context lock
+ */
+static void
+g_source_unref_internal (GSource      *source,
+                        GMainContext *context,
+                        gboolean      have_lock)
+{
+  gpointer cb_data = NULL;
+  GSourceCallbackFuncs *cb_funcs = NULL;
+  GSList *tmp_list;
+
+  g_return_if_fail (source != NULL);
+  
+  if (!have_lock && context)
+    LOCK_CONTEXT (context);
+
+  source->ref_count--;
+  if (source->ref_count == 0)
+    {
+      if (context && !SOURCE_DESTROYED (source))
+       {
+         g_warning (G_STRLOC ": ref_count == 0, but source is still attached to a context!");
+         source->ref_count++;
+       }
+      else
+       {
+         g_source_list_remove (source, context);
+         
+         tmp_list = source->poll_fds;
+         while (tmp_list)
            {
-             n_ready++;
-             current_priority = source->priority;
-             timeout = 0;
+             g_main_context_remove_poll_unlocked (context, tmp_list->data);
+             tmp_list = tmp_list->next;
            }
        }
+    }
+  
+  if (!have_lock && context)
+    UNLOCK_CONTEXT (context);
+
+  if (cb_data)
+    {
+      if (have_lock)
+       UNLOCK_CONTEXT (context);
+      
+      cb_funcs->unref (cb_data);
+
+      if (have_lock)
+       LOCK_CONTEXT (context);
+    }
+}
+
+/**
+ * g_source_unref:
+ * @source: a #GSource
+ * 
+ * Decreases the reference count of a source by one. If the
+ * resulting reference count is zero the source and associated
+ * memory will be destroyed. 
+ **/
+void
+g_source_unref (GSource *source)
+{
+  g_return_if_fail (source != NULL);
+
+  g_source_unref_internal (source, source->context, FALSE);
+}
+
+/**
+ * g_main_context_find_source_by_id:
+ * @context: a #GMainContext (if %NULL, the default context will be used)
+ * @id: the source ID, as returned by g_source_get_id()
+ * 
+ * Finds a #GSource given a pair of context and ID
+ * 
+ * Return value: the #GSource if found, otherwise, %NULL
+ **/
+GSource *
+g_main_context_find_source_by_id (GMainContext *context,
+                                 guint         id)
+{
+  GSource *source;
+  
+  g_return_val_if_fail (id > 0, FALSE);
+
+  if (context == NULL)
+    context = g_main_context_default ();
+  
+  LOCK_CONTEXT (context);
+  
+  source = context->source_list;
+  while (source)
+    {
+      if (!SOURCE_DESTROYED (source) &&
+         source->id == id)
+       break;
+      source = source->next;
+    }
+
+  UNLOCK_CONTEXT (context);
+
+  return source;
+}
+
+/**
+ * g_main_context_find_source_by_funcs_user_data:
+ * @context: a #GMainContext (if %NULL, the default context will be used).
+ * @funcs: the @source_funcs passed to g_source_new().
+ * @user_data: the user data from the callback.
+ * 
+ * Finds a source with the given source functions and user data.  If
+ * multiple sources exist with the same source function and user data,
+ * the first one found will be returned.
+ * 
+ * Return value: the source, if one was found, otherwise %NULL
+ **/
+GSource *
+g_main_context_find_source_by_funcs_user_data (GMainContext *context,
+                                              GSourceFuncs *funcs,
+                                              gpointer      user_data)
+{
+  GSource *source;
+  
+  g_return_val_if_fail (funcs != NULL, FALSE);
+
+  if (context == NULL)
+    context = g_main_context_default ();
+  
+  LOCK_CONTEXT (context);
+
+  source = context->source_list;
+  while (source)
+    {
+      if (!SOURCE_DESTROYED (source) &&
+         source->source_funcs == funcs &&
+         source->callback_data == user_data)
+       break;
+      source = source->next;
+    }
+
+  UNLOCK_CONTEXT (context);
+
+  return source;
+}
+
+/**
+ * g_main_context_find_source_by_user_data:
+ * @context: a #GMainContext
+ * @user_data: the user_data for the callback.
+ * 
+ * Finds a source with the given user data for the callback.  If
+ * multiple sources exist with the same user data, the first
+ * one found will be returned.
+ * 
+ * Return value: the source, if one was found, otherwise %NULL
+ **/
+GSource *
+g_main_context_find_source_by_user_data (GMainContext *context,
+                                        gpointer      user_data)
+{
+  GSource *source;
+  
+  if (context == NULL)
+    context = g_main_context_default ();
+  
+  LOCK_CONTEXT (context);
+
+  source = context->source_list;
+  while (source)
+    {
+      if (!SOURCE_DESTROYED (source) &&
+         source->callback_data == user_data)
+       break;
+      source = source->next;
+    }
+
+  UNLOCK_CONTEXT (context);
+
+  return source;
+}
+
+/**
+ * g_source_remove:
+ * @tag: the id of the source to remove.
+ * 
+ * Removes the source with the given id from the default main
+ * context. The id of a #GSource is given by g_source_get_id(),
+ * or will be returned by the functions g_source_attach(),
+ * g_idle_add(), g_idle_add_full(), g_timeout_add(),
+ * g_timeout_add_full(), g_io_add_watch, and g_io_add_watch_full().
+ *
+ * See also g_source_destroy().
+ *
+ * Return value: %TRUE if the source was found and removed.
+ **/
+gboolean
+g_source_remove (guint tag)
+{
+  GSource *source;
+  
+  g_return_val_if_fail (tag > 0, FALSE);
+
+  source = g_main_context_find_source_by_id (NULL, tag);
+  if (source)
+    g_source_destroy (source);
+
+  return source != NULL;
+}
+
+/**
+ * g_source_remove_by_user_data:
+ * @user_data: the user_data for the callback.
+ * 
+ * Removes a source from the default main loop context given the user
+ * data for the callback. If multiple sources exist with the same user
+ * data, only one will be destroyed.
+ * 
+ * Return value: %TRUE if a source was found and removed. 
+ **/
+gboolean
+g_source_remove_by_user_data (gpointer user_data)
+{
+  GSource *source;
+  
+  source = g_main_context_find_source_by_user_data (NULL, user_data);
+  if (source)
+    {
+      g_source_destroy (source);
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+/**
+ * g_source_remove_by_funcs_user_data:
+ * @funcs: The @source_funcs passed to g_source_new()
+ * @user_data: the user data for the callback
+ * 
+ * Removes a source from the default main loop context given the
+ * source functions and user data. If multiple sources exist with the
+ * same source functions and user data, only one will be destroyed.
+ * 
+ * Return value: %TRUE if a source was found and removed. 
+ **/
+gboolean
+g_source_remove_by_funcs_user_data (GSourceFuncs *funcs,
+                                   gpointer      user_data)
+{
+  GSource *source;
+
+  g_return_val_if_fail (funcs != NULL, FALSE);
+
+  source = g_main_context_find_source_by_funcs_user_data (NULL, funcs, user_data);
+  if (source)
+    {
+      g_source_destroy (source);
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+/**
+ * g_get_current_time:
+ * @result: #GTimeVal structure in which to store current time.
+ * 
+ * Equivalent to Unix's <function>gettimeofday()</function>, but portable
+ **/
+void
+g_get_current_time (GTimeVal *result)
+{
+#ifndef G_OS_WIN32
+  struct timeval r;
+
+  g_return_if_fail (result != NULL);
+
+  /*this is required on alpha, there the timeval structs are int's
+    not longs and a cast only would fail horribly*/
+  gettimeofday (&r, NULL);
+  result->tv_sec = r.tv_sec;
+  result->tv_usec = r.tv_usec;
+#else
+  /* Avoid calling time() except for the first time.
+   * GetTickCount() should be pretty fast and low-level?
+   * I could also use ftime() but it seems unnecessarily overheady.
+   */
+  static DWORD start_tick = 0;
+  static time_t start_time;
+  DWORD tick;
+
+  g_return_if_fail (result != NULL);
+  if (start_tick == 0)
+    {
+      start_tick = GetTickCount ();
+      time (&start_time);
+    }
+
+  tick = GetTickCount ();
+
+  result->tv_sec = (tick - start_tick) / 1000 + start_time;
+  result->tv_usec = ((tick - start_tick) % 1000) * 1000;
+#endif
+}
+
+/* Running the main loop */
+
+/* HOLDS: context's lock */
+static void
+g_main_dispatch (GMainContext *context)
+{
+  gint i;
+
+  for (i = 0; i < context->pending_dispatches->len; i++)
+    {
+      GSource *source = context->pending_dispatches->pdata[i];
+
+      context->pending_dispatches->pdata[i] = NULL;
+      g_assert (source);
+
+      source->flags &= ~G_SOURCE_READY;
+
+      if (!SOURCE_DESTROYED (source))
+       {
+         gboolean was_in_call;
+         gpointer user_data = NULL;
+         GSourceFunc callback = NULL;
+         GSourceCallbackFuncs *cb_funcs;
+         gpointer cb_data;
+         gboolean need_destroy;
+
+         gboolean (*dispatch) (GSource *,
+                               GSourceFunc,
+                               gpointer);
+
+         dispatch = source->source_funcs->dispatch;
+         cb_funcs = source->callback_funcs;
+         cb_data = source->callback_data;
+
+         if (cb_funcs)
+           cb_funcs->ref (cb_data);
+         
+         was_in_call = source->flags & G_HOOK_FLAG_IN_CALL;
+         source->flags |= G_HOOK_FLAG_IN_CALL;
+
+         UNLOCK_CONTEXT (context);
+
+         if (cb_funcs)
+           cb_funcs->get (cb_data, &callback, &user_data);
+
+         need_destroy = ! dispatch (source,
+                                    callback,
+                                    user_data);
+         LOCK_CONTEXT (context);
+
+         if (cb_funcs)
+           cb_funcs->unref (cb_data);
+
+        if (!was_in_call)
+           source->flags &= ~G_HOOK_FLAG_IN_CALL;
+
+         /* Note: this depends on the fact that we can't switch
+          * sources from one main context to another
+          */
+         if (need_destroy && !SOURCE_DESTROYED (source))
+           {
+             g_assert (source->context == context);
+             g_source_destroy_internal (source, context, TRUE);
+           }
+       }
+      
+      SOURCE_UNREF (source, context);
+    }
+
+  g_ptr_array_set_size (context->pending_dispatches, 0);
+}
+
+/* Holds context's lock */
+static inline GSource *
+next_valid_source (GMainContext *context,
+                  GSource      *source)
+{
+  GSource *new_source = source ? source->next : context->source_list;
+
+  while (new_source)
+    {
+      if (!SOURCE_DESTROYED (new_source))
+       {
+         new_source->ref_count++;
+         break;
+       }
+      
+      new_source = new_source->next;
+    }
+
+  if (source)
+    SOURCE_UNREF (source, context);
+         
+  return new_source;
+}
+
+
+/**
+ * g_main_context_prepare:
+ * @context: a #GMainContext
+ * @priority: location to store priority of highest priority
+ *            source already ready.
+ * 
+ * Prepares to poll sources within a main loop. The resulting information
+ * for polling is determined by calling g_main_context_query ().
+ * 
+ * Return value: %TRUE if some source is ready to be dispatched
+ *               prior to polling.
+ **/
+gboolean
+g_main_context_prepare (GMainContext *context,
+                       gint         *priority)
+{
+  gint n_ready = 0;
+  gint current_priority = G_MAXINT;
+  GSource *source;
+
+  if (context == NULL)
+    context = g_main_context_default ();
+  
+  LOCK_CONTEXT (context);
+
+  context->time_is_current = FALSE;
+
+  if (context->in_check_or_prepare)
+    {
+      g_warning ("g_main_context_prepare() called recursively from within a source's check() or "
+                "prepare() member.");
+      return FALSE;
+    }
+
+#ifdef G_THREADS_ENABLED
+  if (context->poll_waiting)
+    {
+      g_warning("g_main_context_prepare(): main loop already active in another thread");
+      UNLOCK_CONTEXT (context);
+      return FALSE;
+    }
+  
+  context->poll_waiting = TRUE;
+#endif G_THREADS_ENABLED  
+
+#if 0
+  /* If recursing, finish up current dispatch, before starting over */
+  if (context->pending_dispatches)
+    {
+      if (dispatch)
+       g_main_dispatch (context, &current_time);
+      
+      UNLOCK_CONTEXT (context);
+      return TRUE;
+    }
+#endif
+
+  /* If recursing, clear list of pending dispatches */
+  g_ptr_array_set_size (context->pending_dispatches, 0);
+  
+  /* Prepare all sources */
+
+  context->timeout = -1;
+  
+  source = next_valid_source (context, NULL);
+  while (source)
+    {
+      gint source_timeout = -1;
+
+      if ((n_ready > 0) && (source->priority > current_priority))
+       {
+         SOURCE_UNREF (source, context);
+         break;
+       }
+      if ((source->flags & G_HOOK_FLAG_IN_CALL) && !(source->flags & G_SOURCE_CAN_RECURSE))
+       goto next;
+
+      if (!(source->flags & G_SOURCE_READY))
+       {
+         gboolean result;
+         gboolean (*prepare)  (GSource  *source, 
+                               gint     *timeout);
+
+         prepare = source->source_funcs->prepare;
+         context->in_check_or_prepare++;
+         UNLOCK_CONTEXT (context);
+
+         result = (*prepare) (source, &source_timeout);
+
+         LOCK_CONTEXT (context);
+         context->in_check_or_prepare--;
+
+         if (result)
+           source->flags |= G_SOURCE_READY;
+       }
+
+      if (source->flags & G_SOURCE_READY)
+       {
+         n_ready++;
+         current_priority = source->priority;
+         context->timeout = 0;
+       }
       
       if (source_timeout >= 0)
        {
-         if (timeout < 0)
-           timeout = source_timeout;
+         if (context->timeout < 0)
+           context->timeout = source_timeout;
          else
-           timeout = MIN (timeout, source_timeout);
+           context->timeout = MIN (context->timeout, source_timeout);
        }
 
-      hook = g_hook_next_valid (&source_list, hook, TRUE);
+    next:
+      source = next_valid_source (context, source);
+    }
+
+  UNLOCK_CONTEXT (context);
+  
+  if (priority)
+    *priority = current_priority;
+  
+  return (n_ready > 0);
+}
+
+/**
+ * g_main_context_query:
+ * @context: a #GMainContext
+ * @max_priority: maximum priority source to check
+ * @timeout: location to store timeout to be used in polling
+ * @fds: location to store #GPollFD records that need to be polled.
+ * @n_fds: length of @fds.
+ * 
+ * Determines information necessary to poll this main loop.
+ * 
+ * Return value: 
+ **/
+gint
+g_main_context_query (GMainContext *context,
+                     gint          max_priority,
+                     gint         *timeout,
+                     GPollFD      *fds,
+                     gint          n_fds)
+{
+  gint n_poll;
+  GPollRec *pollrec;
+  
+  LOCK_CONTEXT (context);
+
+  pollrec = context->poll_records;
+  n_poll = 0;
+  while (pollrec && max_priority >= pollrec->priority)
+    {
+      if (pollrec->fd->events)
+       {
+         if (n_poll < n_fds)
+           {
+             fds[n_poll].fd = pollrec->fd->fd;
+             /* In direct contradiction to the Unix98 spec, IRIX runs into
+              * difficulty if you pass in POLLERR, POLLHUP or POLLNVAL
+              * flags in the events field of the pollfd while it should
+              * just ignoring them. So we mask them out here.
+              */
+             fds[n_poll].events = pollrec->fd->events & ~(G_IO_ERR|G_IO_HUP|G_IO_NVAL);
+             fds[n_poll].revents = 0;
+           }
+         n_poll++;
+       }
+      
+      pollrec = pollrec->next;
     }
 
-  /* poll(), if necessary */
+  context->poll_changed = FALSE;
+
+  if (timeout)
+    {
+      *timeout = context->timeout;
+      if (timeout != 0)
+       context->time_is_current = FALSE;
+    }
+  
+  UNLOCK_CONTEXT (context);
 
-  g_main_poll (timeout, n_ready > 0, current_priority);
+  return n_poll;
+}
 
-  if (timeout != 0)
-    g_get_current_time (&current_time);
+/**
+ * g_main_context_check:
+ * @context: a #GMainContext
+ * @max_priority: the maximum numerical priority of sources to check
+ * @fds: array of #GPollFD's that was passed to the last call to
+ *       g_main_context_query()
+ * @n_fds: return value of g_main_context_query()
+ * 
+ * Pass the results of polling back to the main loop.
+ * 
+ * Return value: %TRUE if some sources are ready to be dispatched.
+ **/
+gboolean
+g_main_context_check (GMainContext *context,
+                     gint          max_priority,
+                     GPollFD      *fds,
+                     gint          n_fds)
+{
+  GSource *source;
+  GPollRec *pollrec;
+  gint n_ready = 0;
+  gint i;
   
-  /* Check to see what sources need to be dispatched */
+  LOCK_CONTEXT (context);
 
-  n_ready = 0;
+  if (context->in_check_or_prepare)
+    {
+      g_warning ("g_main_context_check() called recursively from within a source's check() or "
+                "prepare() member.");
+      return FALSE;
+    }
   
-  hook = g_hook_first_valid (&source_list, TRUE);
-  while (hook)
+#ifdef G_THREADS_ENABLED
+  if (!context->poll_waiting)
     {
-      GSource *source = (GSource *)hook;
+#ifndef G_OS_WIN32
+      gchar c;
+      read (context->wake_up_pipe[0], &c, 1);
+#endif
+    }
+  else
+    context->poll_waiting = FALSE;
+#endif /* G_THREADS_ENABLED */
 
-      if ((n_ready > 0) && (source->priority > current_priority))
+  /* If the set of poll file descriptors changed, bail out
+   * and let the main loop rerun
+   */
+  if (context->poll_changed)
+    return 0;
+
+  pollrec = context->poll_records;
+  i = 0;
+  while (i < n_fds)
+    {
+      if (pollrec->fd->events)
        {
-         g_hook_unref (&source_list, hook);
-         break;
+         pollrec->fd->revents = fds[i].revents;
+         i++;
        }
-      if (G_HOOK_IN_CALL (hook) && !(hook->flags & G_SOURCE_CAN_RECURSE))
+      pollrec = pollrec->next;
+    }
+
+  source = next_valid_source (context, NULL);
+  while (source)
+    {
+      if ((n_ready > 0) && (source->priority > max_priority))
        {
-         hook = g_hook_next_valid (&source_list, hook, TRUE);
-         continue;
+         SOURCE_UNREF (source, context);
+         break;
        }
+      if ((source->flags & G_HOOK_FLAG_IN_CALL) && !(source->flags & G_SOURCE_CAN_RECURSE))
+       goto next;
 
-      if (!(hook->flags & G_SOURCE_READY))
+      if (!(source->flags & G_SOURCE_READY))
        {
-         gboolean (*check) (gpointer  source_data,
-                            GTimeVal *current_time,
-                            gpointer  user_data);
+         gboolean result;
+         gboolean (*check) (GSource  *source);
 
-         check = ((GSourceFuncs *) hook->func)->check;
-         in_check_or_prepare++;
-         G_UNLOCK (main_loop);
+         check = source->source_funcs->check;
          
-         if ((*check) (source->source_data, &current_time, source->hook.data))
-           hook->flags |= G_SOURCE_READY;
-
-         G_LOCK (main_loop);
-         in_check_or_prepare--;
+         context->in_check_or_prepare++;
+         UNLOCK_CONTEXT (context);
+         
+         result = (*check) (source);
+         
+         LOCK_CONTEXT (context);
+         context->in_check_or_prepare--;
+         
+         if (result)
+           source->flags |= G_SOURCE_READY;
        }
 
-      if (hook->flags & G_SOURCE_READY)
+      if (source->flags & G_SOURCE_READY)
        {
-         if (dispatch)
-           {
-             hook->flags &= ~G_SOURCE_READY;
-             g_hook_ref (&source_list, hook);
-             pending_dispatches = g_slist_prepend (pending_dispatches, source);
-             current_priority = source->priority;
-             n_ready++;
-           }
-         else
-           {
-             g_hook_unref (&source_list, hook);
-             G_UNLOCK (main_loop);
+         source->ref_count++;
+         g_ptr_array_add (context->pending_dispatches, source);
 
-             return TRUE;
-           }
+         n_ready++;
        }
-      
-      hook = g_hook_next_valid (&source_list, hook, TRUE);
+
+    next:
+      source = next_valid_source (context, source);
     }
-  /* Now invoke the callbacks */
 
-  if (pending_dispatches)
+  UNLOCK_CONTEXT (context);
+
+  return n_ready > 0;
+}
+
+/**
+ * g_main_context_dispatch:
+ * @context: a #GMainContext
+ * 
+ * Dispatch all pending sources()
+ **/
+void
+g_main_context_dispatch (GMainContext *context)
+{
+  LOCK_CONTEXT (context);
+
+  if (context->pending_dispatches->len > 0)
     {
-      pending_dispatches = g_slist_reverse (pending_dispatches);
-      g_main_dispatch (&current_time);
-      retval = TRUE;
+      g_main_dispatch (context);
     }
 
-  G_UNLOCK (main_loop);
+  UNLOCK_CONTEXT (context);
+}
+
+static gboolean
+g_main_context_iterate (GMainContext *context,
+                       gboolean      block,
+                       gboolean      dispatch)
+{
+  gint max_priority;
+  gint timeout;
+  gboolean some_ready;
+  gint nfds, new_nfds;
+  GPollFD *fds;
+  
+  some_ready = g_main_context_prepare (context, &max_priority);
+
+  do
+    {
+      LOCK_CONTEXT (context);
+
+      if (context->cached_poll_array)
+       {
+         nfds = context->cached_poll_array_size;
+         fds = context->cached_poll_array;
+         context->cached_poll_array = NULL;
+       }
+      else
+       {
+         nfds = context->cached_poll_array_size = context->n_poll_records;
+         fds = g_new (GPollFD, nfds);
+       }
+
+      UNLOCK_CONTEXT (context);
+  
+      new_nfds = g_main_context_query (context, max_priority,
+                                      &timeout, fds, nfds);
+    }
+  while (new_nfds > nfds);
+
+  if (!block)
+    timeout = 0;
+  
+  g_main_context_poll (context, timeout, max_priority,
+                      fds, new_nfds);
 
-  return retval;
+  g_main_context_check (context,
+                       max_priority,
+                       fds, new_nfds);
+
+  LOCK_CONTEXT (context);
+
+  g_assert (!context->cached_poll_array);
+  
+  context->cached_poll_array = fds;
+  context->cached_poll_array_size = nfds;
+
+  UNLOCK_CONTEXT (context);
+  
+  if (dispatch)
+    g_main_context_dispatch (context);
+
+  return some_ready;
 }
 
-/* See if any events are pending
- */
+/**
+ * g_main_context_pending:
+ * @context: a #GMainContext (if %NULL, the default context will be used)
+ *
+ * Check if any sources have pending events for the given context.
+ * 
+ * Return value: %TRUE if events are pending.
+ **/
 gboolean 
-g_main_pending (void)
+g_main_context_pending (GMainContext *context)
 {
-  return in_check_or_prepare ? FALSE : g_main_iterate (FALSE, FALSE);
+  if (!context)
+    context = g_main_context_default();
+  
+  return g_main_context_iterate (context, FALSE, FALSE);
 }
 
-/* Run a single iteration of the mainloop. If block is FALSE,
- * will never block
- */
+/**
+ * g_main_context_iteration:
+ * @context: a #GMainContext (if %NULL, the default context will be used) 
+ * @may_block: whether the call may block.
+ * 
+ * Run a single iteration for the given main loop. This involves
+ * checking to see if any event sources are ready to be processed,
+ * then if no events sources are ready and @may_block is %TRUE, waiting
+ * for a source to become ready, then dispatching the highest priority
+ * events sources that are ready. Note that even when @may_block is %TRUE,
+ * it is still possible for g_main_context_iteration() to return
+ * %FALSE, since the the wait may be interrupted for other
+ * reasons than an event source becoming ready.
+ * 
+ * Return value: %TRUE if events were dispatched.
+ **/
 gboolean
-g_main_iteration (gboolean block)
+g_main_context_iteration (GMainContext *context, gboolean may_block)
 {
-  if (in_check_or_prepare)
-    {
-      g_warning ("g_main_iteration(): called recursively from within a source's check() or "
-                "prepare() member or from a second thread, iteration not possible");
-      return FALSE;
-    }
-  else
-    return g_main_iterate (block, TRUE);
+  if (!context)
+    context = g_main_context_default();
+  
+  return g_main_context_iterate (context, may_block, TRUE);
 }
 
-GMainLoop*
-g_main_new (gboolean is_running)
+/**
+ * g_main_loop_new:
+ * @context: a #GMainContext  (if %NULL, the default context will be used).
+ * @is_running: set to TRUE to indicate that the loop is running. This
+ * is not very important since calling g_main_run() will set this to
+ * TRUE anyway.
+ * 
+ * Create a new #GMainLoop structure
+ * 
+ * Return value: 
+ **/
+GMainLoop *
+g_main_loop_new (GMainContext *context,
+                gboolean      is_running)
 {
   GMainLoop *loop;
-
+  
+  if (!context)
+    context = g_main_context_default();
+  
   loop = g_new0 (GMainLoop, 1);
+  loop->context = context;
   loop->is_running = is_running != FALSE;
 
+#ifdef G_THREADS_ENABLED
+  if (g_thread_supported ())
+    loop->mutex = g_mutex_new ();
+  else
+    loop->mutex = NULL;
+  loop->sem_cond = NULL;
+#endif G_THREADS_ENABLED  
+
   return loop;
 }
 
+/**
+ * g_main_loop_run:
+ * @loop: a #GMainLoop
+ * 
+ * Run a main loop until g_main_quit() is called on the loop.
+ * If this is called for the thread of the loop's #GMainContext,
+ * it will process events from the loop, otherwise it will
+ * simply wait.
+ **/
 void 
-g_main_run (GMainLoop *loop)
+g_main_loop_run (GMainLoop *loop)
 {
   g_return_if_fail (loop != NULL);
 
-  if (in_check_or_prepare)
+#ifdef G_THREADS_ENABLED
+  if (loop->context->thread != g_thread_self ())
+    {
+      LOCK_LOOP (loop);
+
+      if (!g_thread_supported ())
+       {
+         g_warning ("g_main_loop_run() was called from second thread but"
+                    "g_thread_init() was never called.");
+       }
+      else
+       {
+         if (!loop->sem_cond)
+           loop->sem_cond = g_cond_new ();
+         
+         if (!loop->is_running)
+           loop->is_running = TRUE;
+         
+         while (loop->is_running)
+           g_cond_wait (loop->sem_cond, loop->mutex);
+       }
+       
+      UNLOCK_LOOP (loop);
+    }
+  else
+#endif /* G_THREADS_ENABLED */    
     {
-      g_warning ("g_main_run(): called recursively from within a source's check() or "
-                "prepare() member or from a second thread, iteration not possible");
-      return;
+      LOCK_CONTEXT (loop->context);
+      if (loop->context->in_check_or_prepare)
+       {
+         g_warning ("g_main_run(): called recursively from within a source's check() or "
+                    "prepare() member, iteration not possible.");
+         return;
+       }
+      UNLOCK_CONTEXT (loop->context);
+      
+      LOCK_LOOP (loop);
+
+      loop->is_running = TRUE;
+      while (loop->is_running)
+       {
+         UNLOCK_LOOP (loop);
+         g_main_context_iterate (loop->context, TRUE, TRUE);
+         LOCK_LOOP (loop);
+       }
+      UNLOCK_LOOP (loop);
     }
-  
-  loop->is_running = TRUE;
-  while (loop->is_running)
-    g_main_iterate (TRUE, TRUE);
 }
 
+/**
+ * g_main_loop_quit:
+ * @loop: a #GMainLoop
+ * 
+ * Stops a #GMainLoop from running. Any calls to g_main_loop_run()
+ * for the loop will return.
+ **/
 void 
-g_main_quit (GMainLoop *loop)
+g_main_loop_quit (GMainLoop *loop)
 {
   g_return_if_fail (loop != NULL);
 
+  LOCK_LOOP (loop);
   loop->is_running = FALSE;
+
+  if (loop->sem_cond)
+    g_cond_broadcast (loop->sem_cond);
+  
+  UNLOCK_LOOP (loop);
+
+  LOCK_CONTEXT (loop->context);
+  
+  g_main_context_wakeup (loop->context);
+  UNLOCK_CONTEXT (loop->context);
 }
 
+/**
+ * g_main_loop_destroy:
+ * @loop: a #GMainLoop
+ * 
+ * Destroy a #GMainLoop object and free all associated memory.
+ * The loop must not currently be running via g_main_run().
+ **/
 void 
-g_main_destroy (GMainLoop *loop)
+g_main_loop_destroy (GMainLoop *loop)
 {
   g_return_if_fail (loop != NULL);
+  g_return_if_fail (!loop->is_running);
+
+#ifdef G_THREADS_ENABLED
+  g_mutex_free (loop->mutex);
+  if (loop->sem_cond)
+    g_cond_free (loop->sem_cond);
+#endif /* G_THREADS_ENABLED */  
 
   g_free (loop);
 }
 
+/**
+ * g_main_loop_is_running:
+ * @loop: a #GMainLoop.
+ * 
+ * Check to see if the main loop is currently being run via g_main_run()
+ * 
+ * Return value: %TRUE if the mainloop is currently being run.
+ **/
 gboolean
-g_main_is_running (GMainLoop *loop)
+g_main_loop_is_running (GMainLoop *loop)
 {
+  gboolean result;
+  
   g_return_val_if_fail (loop != NULL, FALSE);
 
-  return loop->is_running;
+  LOCK_LOOP (loop);
+  result = loop->is_running;
+  UNLOCK_LOOP (loop);
+
+  return result;
 }
 
-/* HOLDS: main_loop_lock */
+/* HOLDS: context's lock */
 static void
-g_main_poll (gint     timeout,
-            gboolean use_priority,
-            gint     priority)
+g_main_context_poll (GMainContext *context,
+                    gint          timeout,
+                    gint          priority,
+                    GPollFD      *fds,
+                    gint          n_fds)
 {
 #ifdef  G_MAIN_POLL_DEBUG
   GTimer *poll_timer;
-#endif
-  GPollFD *fd_array;
   GPollRec *pollrec;
   gint i;
-  gint npoll;
+#endif
 
-#ifdef G_THREADS_ENABLED
-#ifndef G_OS_WIN32
-  if (wake_up_pipe[0] < 0)
-    {
-      if (pipe (wake_up_pipe) < 0)
-       g_error ("Cannot create pipe main loop wake-up: %s\n",
-                g_strerror (errno));
+  GPollFunc poll_func;
 
-      wake_up_rec.fd = wake_up_pipe[0];
-      wake_up_rec.events = G_IO_IN;
-      g_main_add_poll_unlocked (0, &wake_up_rec);
-    }
-#else
-  if (wake_up_semaphore == NULL)
-    {
-      if ((wake_up_semaphore = CreateSemaphore (NULL, 0, 100, NULL)) == NULL)
-       g_error ("Cannot create wake-up semaphore: %s", g_win32_error_message (GetLastError ()));
-      wake_up_rec.fd = (gint) wake_up_semaphore;
-      wake_up_rec.events = G_IO_IN;
-#ifdef G_MAIN_POLL_DEBUG
-      g_print ("wake-up semaphore: %#x\n", (guint) wake_up_semaphore);
-#endif
-      g_main_add_poll_unlocked (0, &wake_up_rec);
-    }
-#endif
-#endif
-  fd_array = g_new (GPollFD, n_poll_records);
-  pollrec = poll_records;
-  i = 0;
-  while (pollrec && (!use_priority || priority >= pollrec->priority))
-    {
-      if (pollrec->fd->events)
-       {
-         fd_array[i].fd = pollrec->fd->fd;
-         /* In direct contradiction to the Unix98 spec, IRIX runs into
-          * difficulty if you pass in POLLERR, POLLHUP or POLLNVAL
-          * flags in the events field of the pollfd while it should
-          * just ignoring them. So we mask them out here.
-          */
-         fd_array[i].events = pollrec->fd->events & ~(G_IO_ERR|G_IO_HUP|G_IO_NVAL);
-         fd_array[i].revents = 0;
-         i++;
-       }
-      
-      pollrec = pollrec->next;
-    }
-#ifdef G_THREADS_ENABLED
-  poll_waiting = TRUE;
-  poll_changed = FALSE;
-#endif
-  
-  npoll = i;
-  if (npoll || timeout != 0)
+  if (n_fds || timeout != 0)
     {
 #ifdef G_MAIN_POLL_DEBUG
-      g_print ("g_main_poll(%d) timeout: %d\n", npoll, timeout);
+      g_print ("g_main_poll(%d) timeout: %d\n", n_fds, timeout);
       poll_timer = g_timer_new ();
 #endif
+
+      LOCK_CONTEXT (context);
+
+      poll_func = context->poll_func;
       
-      G_UNLOCK (main_loop);
-      if ((*poll_func) (fd_array, npoll, timeout) < 0)
+      UNLOCK_CONTEXT (context);
+      if ((*poll_func) (fds, n_fds, timeout) < 0 && errno != EINTR)
        g_warning ("poll(2) failed due to: %s.",
                   g_strerror (errno));
-      G_LOCK (main_loop);
       
 #ifdef G_MAIN_POLL_DEBUG
+      LOCK_CONTEXT (context);
+
       g_print ("g_main_poll(%d) timeout: %d - elapsed %12.10f seconds",
-              npoll,
+              n_fds,
               timeout,
               g_timer_elapsed (poll_timer, NULL));
       g_timer_destroy (poll_timer);
-      pollrec = poll_records;
+      pollrec = context->poll_records;
       i = 0;
-      while (i < npoll)
+      while (i < n_fds)
        {
          if (pollrec->fd->events)
            {
-             if (fd_array[i].revents)
+             if (fds[i].revents)
                {
-                 g_print (" [%d:", fd_array[i].fd);
-                 if (fd_array[i].revents & G_IO_IN)
+                 g_print (" [%d:", fds[i].fd);
+                 if (fds[i].revents & G_IO_IN)
                    g_print ("i");
-                 if (fd_array[i].revents & G_IO_OUT)
+                 if (fds[i].revents & G_IO_OUT)
                    g_print ("o");
-                 if (fd_array[i].revents & G_IO_PRI)
+                 if (fds[i].revents & G_IO_PRI)
                    g_print ("p");
-                 if (fd_array[i].revents & G_IO_ERR)
+                 if (fds[i].revents & G_IO_ERR)
                    g_print ("e");
-                 if (fd_array[i].revents & G_IO_HUP)
+                 if (fds[i].revents & G_IO_HUP)
                    g_print ("h");
-                 if (fd_array[i].revents & G_IO_NVAL)
+                 if (fds[i].revents & G_IO_NVAL)
                    g_print ("n");
                  g_print ("]");
                }
@@ -1094,77 +2208,64 @@ g_main_poll (gint     timeout,
          pollrec = pollrec->next;
        }
       g_print ("\n");
+      
+      UNLOCK_CONTEXT (context);
 #endif
-    } /* if (npoll || timeout != 0) */
-  
-#ifdef G_THREADS_ENABLED
-  if (!poll_waiting)
-    {
-#ifndef G_OS_WIN32
-      gchar c;
-      read (wake_up_pipe[0], &c, 1);
-#endif
-    }
-  else
-    poll_waiting = FALSE;
-
-  /* If the set of poll file descriptors changed, bail out
-   * and let the main loop rerun
-   */
-  if (poll_changed)
-    {
-      g_free (fd_array);
-      return;
-    }
-#endif
-
-  pollrec = poll_records;
-  i = 0;
-  while (i < npoll)
-    {
-      if (pollrec->fd->events)
-       {
-         pollrec->fd->revents = fd_array[i].revents;
-         i++;
-       }
-      pollrec = pollrec->next;
-    }
-
-  g_free (fd_array);
+    } /* if (n_fds || timeout != 0) */
 }
 
-void 
-g_main_add_poll (GPollFD *fd,
-                gint     priority)
+/**
+ * g_main_context_add_poll:
+ * @context: a #GMainContext (or %NULL for the default context)
+ * @fd: a #GPollFD structure holding information about a file
+ *      descriptor to watch.
+ * @priority: the priority for this file descriptor which should be
+ *      the same as the priority used for g_source_attach() to ensure that the
+ *      file descriptor is polled whenever the results may be needed.
+ * 
+ * Add a file descriptor to the set of file descriptors polled * for
+ * this context. This will very seldom be used directly. Instead
+ * a typical event source will use g_source_add_poll() instead.
+ **/
+void
+g_main_context_add_poll (GMainContext *context,
+                        GPollFD      *fd,
+                        gint          priority)
 {
-  G_LOCK (main_loop);
-  g_main_add_poll_unlocked (priority, fd);
-  G_UNLOCK (main_loop);
+  if (!context)
+    context = g_main_context_default ();
+  
+  LOCK_CONTEXT (context);
+  g_main_context_add_poll_unlocked (context, priority, fd);
+  UNLOCK_CONTEXT (context);
 }
 
 /* HOLDS: main_loop_lock */
 static void 
-g_main_add_poll_unlocked (gint     priority,
-                         GPollFD *fd)
+g_main_context_add_poll_unlocked (GMainContext *context,
+                                 gint          priority,
+                                 GPollFD      *fd)
 {
   GPollRec *lastrec, *pollrec, *newrec;
 
-  if (!poll_chunk)
-    poll_chunk = g_mem_chunk_create (GPollRec, 32, G_ALLOC_ONLY);
+  if (!context->poll_chunk)
+    context->poll_chunk = g_mem_chunk_create (GPollRec, 32, G_ALLOC_ONLY);
 
-  if (poll_free_list)
+  if (context->poll_free_list)
     {
-      newrec = poll_free_list;
-      poll_free_list = newrec->next;
+      newrec = context->poll_free_list;
+      context->poll_free_list = newrec->next;
     }
   else
-    newrec = g_chunk_new (GPollRec, poll_chunk);
+    newrec = g_chunk_new (GPollRec, context->poll_chunk);
 
+  /* This file descriptor may be checked before we ever poll */
+  fd->revents = 0;
   newrec->fd = fd;
   newrec->priority = priority;
 
   lastrec = NULL;
-  pollrec = poll_records;
+  pollrec = context->poll_records;
   while (pollrec && priority >= pollrec->priority)
     {
       lastrec = pollrec;
@@ -1174,29 +2275,56 @@ g_main_add_poll_unlocked (gint     priority,
   if (lastrec)
     lastrec->next = newrec;
   else
-    poll_records = newrec;
+    context->poll_records = newrec;
 
   newrec->next = pollrec;
 
-  n_poll_records++;
+  context->n_poll_records++;
+  if (context->cached_poll_array &&
+      context->cached_poll_array_size < context->n_poll_records)
+    {
+      g_free (context->cached_poll_array);
+      context->cached_poll_array = NULL;
+    }
 
 #ifdef G_THREADS_ENABLED
-  poll_changed = TRUE;
+  context->poll_changed = TRUE;
 
   /* Now wake up the main loop if it is waiting in the poll() */
-  g_main_wakeup ();
+  g_main_context_wakeup (context);
 #endif
 }
 
-void 
-g_main_remove_poll (GPollFD *fd)
+/**
+ * g_main_context_remove_poll:
+ * @context:a #GMainContext 
+ * @fd: a #GPollFD descriptor previously added with g_main_context_add_poll()
+ * 
+ * Remove file descriptor from the set of file descriptors to be
+ * polled for a particular context.
+ **/
+void
+g_main_context_remove_poll (GMainContext *context,
+                           GPollFD      *fd)
 {
-  GPollRec *pollrec, *lastrec;
+  if (!context)
+    context = g_main_context_default ();
+  
+  LOCK_CONTEXT (context);
 
-  G_LOCK (main_loop);
+  g_main_context_remove_poll_unlocked (context, fd);
   
+  UNLOCK_CONTEXT (context);
+}
+
+static void
+g_main_context_remove_poll_unlocked (GMainContext *context,
+                                    GPollFD      *fd)
+{
+  GPollRec *pollrec, *lastrec;
+
   lastrec = NULL;
-  pollrec = poll_records;
+  pollrec = context->poll_records;
 
   while (pollrec)
     {
@@ -1205,16 +2333,16 @@ g_main_remove_poll (GPollFD *fd)
          if (lastrec != NULL)
            lastrec->next = pollrec->next;
          else
-           poll_records = pollrec->next;
+           context->poll_records = pollrec->next;
 
 #ifdef ENABLE_GC_FRIENDLY
          pollrec->fd = NULL;  
 #endif /* ENABLE_GC_FRIENDLY */
 
-         pollrec->next = poll_free_list;
-         poll_free_list = pollrec;
+         pollrec->next = context->poll_free_list;
+         context->poll_free_list = pollrec;
 
-         n_poll_records--;
+         context->n_poll_records--;
          break;
        }
       lastrec = pollrec;
@@ -1222,52 +2350,120 @@ g_main_remove_poll (GPollFD *fd)
     }
 
 #ifdef G_THREADS_ENABLED
-  poll_changed = TRUE;
+  context->poll_changed = TRUE;
   
   /* Now wake up the main loop if it is waiting in the poll() */
-  g_main_wakeup ();
+  g_main_context_wakeup (context);
 #endif
+}
 
-  G_UNLOCK (main_loop);
+/**
+ * g_source_get_current_time:
+ * @source:  a #GSource
+ * @timeval: #GTimeVal structure in which to store current time.
+ * 
+ * Gets the "current time" to be used when checking 
+ * this source. The advantage of calling this function over
+ * calling g_get_current_time() directly is that when 
+ * checking multiple sources, GLib can cache a single value
+ * instead of having to repeatedly get the system time.
+ **/
+void
+g_source_get_current_time (GSource  *source,
+                          GTimeVal *timeval)
+{
+  GMainContext *context;
+  
+  g_return_if_fail (source->context != NULL);
+  context = source->context;
+
+  LOCK_CONTEXT (context);
+
+  if (!context->time_is_current)
+    {
+      g_get_current_time (&context->current_time);
+      context->time_is_current = TRUE;
+    }
+  
+  *timeval = context->current_time;
+  
+  UNLOCK_CONTEXT (context);
 }
 
-void 
-g_main_set_poll_func (GPollFunc func)
+/**
+ * g_main_context_set_poll_func:
+ * @context: a #GMainContext
+ * @func: the function to call to poll all file descriptors
+ * 
+ * Sets the function to use to handle polling of file descriptors. It
+ * will be used instead of the poll() system call (or GLib's
+ * replacement function, which is used where poll() isn't available).
+ *
+ * This function could possibly be used to integrate the GLib event
+ * loop with an external event loop.
+ **/
+void
+g_main_context_set_poll_func (GMainContext *context,
+                             GPollFunc     func)
 {
+  if (!context)
+    context = g_main_context_default ();
+  
+  LOCK_CONTEXT (context);
+  
   if (func)
-    poll_func = func;
+    context->poll_func = func;
   else
+    {
 #ifdef HAVE_POLL
-    poll_func = (GPollFunc) poll;
+      context->poll_func = (GPollFunc) poll;
 #else
-    poll_func = (GPollFunc) g_poll;
+      context->poll_func = (GPollFunc) g_poll;
 #endif
+    }
+
+  UNLOCK_CONTEXT (context);
 }
 
 #ifdef G_OS_WIN32
 
-/* Useful on other platforms, too? */
-
+/**
+ * g_main_context_get_poll_func:
+ * @context: a #GMainContext
+ * 
+ * Gets the poll function set by g_main_context_set_poll_func()
+ * 
+ * Return value: the poll function
+ **/
 GPollFunc
-g_main_win32_get_poll_func (void)
+g_main_context_get_poll_func (GMainContext *context)
 {
-  return poll_func;
+  GPollFunc *result;
+  
+  if (!context)
+    context = g_main_context_default ();
+  
+  LOCK_CONTEXT (context);
+  result = context->poll_func;
+  UNLOCK_CONTEXT (context);
 }
 
 #endif
 
+/* HOLDS: context's lock */
 /* Wake the main loop up from a poll() */
 static void
-g_main_wakeup (void)
+g_main_context_wakeup (GMainContext *context)
 {
 #ifdef G_THREADS_ENABLED
-  if (poll_waiting)
+  if (g_thread_supported() && context->poll_waiting)
     {
-      poll_waiting = FALSE;
+      context->poll_waiting = FALSE;
 #ifndef G_OS_WIN32
-      write (wake_up_pipe[1], "A", 1);
+      write (context->wake_up_pipe[1], "A", 1);
 #else
-      ReleaseSemaphore (wake_up_semaphore, 1, NULL);
+      ReleaseSemaphore (context->context->wake_up_semaphore, 1, NULL);
 #endif
     }
 #endif
@@ -1276,43 +2472,45 @@ g_main_wakeup (void)
 /* Timeouts */
 
 static void
-g_timeout_set_expiration (GTimeoutData *data,
-                         GTimeVal     *current_time)
+g_timeout_set_expiration (GTimeoutSource *timeout_source,
+                         GTimeVal       *current_time)
 {
-  guint seconds = data->interval / 1000;
-  guint msecs = data->interval - seconds * 1000;
+  guint seconds = timeout_source->interval / 1000;
+  guint msecs = timeout_source->interval - seconds * 1000;
 
-  data->expiration.tv_sec = current_time->tv_sec + seconds;
-  data->expiration.tv_usec = current_time->tv_usec + msecs * 1000;
-  if (data->expiration.tv_usec >= 1000000)
+  timeout_source->expiration.tv_sec = current_time->tv_sec + seconds;
+  timeout_source->expiration.tv_usec = current_time->tv_usec + msecs * 1000;
+  if (timeout_source->expiration.tv_usec >= 1000000)
     {
-      data->expiration.tv_usec -= 1000000;
-      data->expiration.tv_sec++;
+      timeout_source->expiration.tv_usec -= 1000000;
+      timeout_source->expiration.tv_sec++;
     }
 }
 
 static gboolean
-g_timeout_prepare  (gpointer  source_data,
-                   GTimeVal *current_time,
-                   gint     *timeout,
-                   gpointer  user_data)
+g_timeout_prepare  (GSource  *source,
+                   gint     *timeout)
 {
   glong msec;
-  GTimeoutData *data = source_data;
+  GTimeVal current_time;
   
-  msec = ((data->expiration.tv_sec  - current_time->tv_sec) * 1000 +
-         (data->expiration.tv_usec - current_time->tv_usec) / 1000);
+  GTimeoutSource *timeout_source = (GTimeoutSource *)source;
+
+  g_source_get_current_time (source, &current_time);
   
+  msec = ((timeout_source->expiration.tv_sec  - current_time.tv_sec) * 1000 +
+         (timeout_source->expiration.tv_usec - current_time.tv_usec) / 1000);
+
   if (msec < 0)
     msec = 0;
-  else if (msec > data->interval)
+  else if (msec > timeout_source->interval)
     {
       /* The system time has been set backwards, so we
-       * reset the expiration time to now + data->interval;
+       * reset the expiration time to now + timeout_source->interval;
        * this at least avoids hanging for long periods of time.
        */
-      g_timeout_set_expiration (data, current_time);
-      msec = data->interval;
+      g_timeout_set_expiration (timeout_source, &current_time);
+      msec = timeout_source->interval;
     }
   
   *timeout = msec;
@@ -1321,27 +2519,38 @@ g_timeout_prepare  (gpointer  source_data,
 }
 
 static gboolean 
-g_timeout_check (gpointer  source_data,
-                GTimeVal *current_time,
-                gpointer  user_data)
+g_timeout_check (GSource  *source)
 {
-  GTimeoutData *data = source_data;
+  GTimeVal current_time;
+  GTimeoutSource *timeout_source = (GTimeoutSource *)source;
+
+  g_source_get_current_time (source, &current_time);
   
-  return ((data->expiration.tv_sec < current_time->tv_sec) ||
-         ((data->expiration.tv_sec == current_time->tv_sec) &&
-          (data->expiration.tv_usec <= current_time->tv_usec)));
+  return ((timeout_source->expiration.tv_sec < current_time.tv_sec) ||
+         ((timeout_source->expiration.tv_sec == current_time.tv_sec) &&
+          (timeout_source->expiration.tv_usec <= current_time.tv_usec)));
 }
 
 static gboolean
-g_timeout_dispatch (gpointer source_data,
-                   GTimeVal *dispatch_time,
-                   gpointer user_data)
+g_timeout_dispatch (GSource    *source,
+                   GSourceFunc callback,
+                   gpointer    user_data)
 {
-  GTimeoutData *data = source_data;
+  GTimeoutSource *timeout_source = (GTimeoutSource *)source;
 
-  if (data->callback (user_data))
+  if (!callback)
+    {
+      g_warning ("Timeout source dispatched without callback\n"
+                "You must call g_source_set_callback().");
+      return FALSE;
+    }
+  if (callback (user_data))
     {
-      g_timeout_set_expiration (data, dispatch_time);
+      GTimeVal current_time;
+
+      g_source_get_current_time (source, &current_time);
+      g_timeout_set_expiration (timeout_source, &current_time);
 
       return TRUE;
     }
@@ -1349,6 +2558,58 @@ g_timeout_dispatch (gpointer source_data,
     return FALSE;
 }
 
+/**
+ * g_timeout_source_new:
+ * @interval: the timeout interval in milliseconds.
+ * 
+ * Create a new timeout source.
+ *
+ * The source will not initially be associated with any #GMainContext
+ * and must be added to one with g_source_attach() before it will be
+ * executed.
+ * 
+ * Return value: the newly create timeout source
+ **/
+GSource *
+g_timeout_source_new (guint interval)
+{
+  GSource *source = g_source_new (&timeout_funcs, sizeof (GTimeoutSource));
+  GTimeoutSource *timeout_source = (GTimeoutSource *)source;
+  GTimeVal current_time;
+
+  timeout_source->interval = interval;
+
+  g_get_current_time (&current_time);
+  g_timeout_set_expiration (timeout_source, &current_time);
+  
+  return source;
+}
+
+/**
+ * g_timeout_add_full:
+ * @priority: the priority of the idle source. Typically this will be in the
+ *            range btweeen #G_PRIORITY_DEFAULT_IDLE and #G_PRIORITY_HIGH_IDLE.
+ * @interval: the time between calls to the function, in milliseconds
+ *             (1/1000ths of a second.)
+ * @function: function to call
+ * @data:     data to pass to @function
+ * @notify:   function to call when the idle is removed, or %NULL
+ * 
+ * Sets a function to be called at regular intervals, with the given
+ * priority.  The function is called repeatedly until it returns
+ * FALSE, at which point the timeout is automatically destroyed and
+ * the function will not be called again.  The @notify function is
+ * called when the timeout is destroyed.  The first call to the
+ * function will be at the end of the first @interval.
+ *
+ * Note that timeout functions may be delayed, due to the processing of other
+ * event sources. Thus they should not be relied on for precise timing.
+ * After each call to the timeout function, the time of the next
+ * timeout is recalculated based on the current time and the given interval
+ * (it does not try to 'catch up' time lost in delays).
+ * 
+ * Return value: the id of event source.
+ **/
 guint
 g_timeout_add_full (gint           priority,
                    guint          interval,
@@ -1356,18 +2617,41 @@ g_timeout_add_full (gint           priority,
                    gpointer       data,
                    GDestroyNotify notify)
 {
-  GTimeoutData *timeout_data = g_new (GTimeoutData, 1);
-  GTimeVal current_time;
+  GSource *source;
+  
+  g_return_val_if_fail (function != NULL, 0);
 
-  timeout_data->interval = interval;
-  timeout_data->callback = function;
-  g_get_current_time (&current_time);
+  source = g_timeout_source_new (interval);
 
-  g_timeout_set_expiration (timeout_data, &current_time);
+  if (priority != G_PRIORITY_DEFAULT)
+    g_source_set_priority (source, priority);
 
-  return g_source_add (priority, FALSE, &timeout_funcs, timeout_data, data, notify);
+  g_source_set_callback (source, function, data, notify);
+  return g_source_attach (source, NULL);
 }
 
+/**
+ * g_timeout_add:
+ * @interval: the time between calls to the function, in milliseconds
+ *             (1/1000ths of a second.)
+ * @function: function to call
+ * @data:     data to pass to @function
+ * 
+ * Sets a function to be called at regular intervals, with the default
+ * priority, #G_PRIORITY_DEFAULT.  The function is called repeatedly
+ * until it returns FALSE, at which point the timeout is automatically
+ * destroyed and the function will not be called again.  The @notify
+ * function is called when the timeout is destroyed.  The first call
+ * to the function will be at the end of the first @interval.
+ *
+ * Note that timeout functions may be delayed, due to the processing of other
+ * event sources. Thus they should not be relied on for precise timing.
+ * After each call to the timeout function, the time of the next
+ * timeout is recalculated based on the current time and the given interval
+ * (it does not try to 'catch up' time lost in delays).
+ * 
+ * Return value: the id of event source.
+ **/
 guint 
 g_timeout_add (guint32        interval,
               GSourceFunc    function,
@@ -1380,10 +2664,8 @@ g_timeout_add (guint32        interval,
 /* Idle functions */
 
 static gboolean 
-g_idle_prepare  (gpointer  source_data, 
-                GTimeVal *current_time,
-                gint     *timeout,
-                gpointer  user_data)
+g_idle_prepare  (GSource  *source,
+                gint     *timeout)
 {
   *timeout = 0;
 
@@ -1391,34 +2673,89 @@ g_idle_prepare  (gpointer  source_data,
 }
 
 static gboolean 
-g_idle_check    (gpointer  source_data,
-                GTimeVal *current_time,
-                gpointer  user_data)
+g_idle_check    (GSource  *source)
 {
   return TRUE;
 }
 
 static gboolean
-g_idle_dispatch (gpointer source_data
-                GTimeVal *dispatch_time,
-                gpointer user_data)
+g_idle_dispatch (GSource    *source
+                GSourceFunc callback,
+                gpointer    user_data)
 {
-  GSourceFunc func = (GSourceFunc) source_data;
+  if (!callback)
+    {
+      g_warning ("Idle source dispatched without callback\n"
+                "You must call g_source_set_callback().");
+      return FALSE;
+    }
+  
+  return callback (user_data);
+}
 
-  return func (user_data);
+/**
+ * g_idle_source_new:
+ * 
+ * Create a new idle source.
+ *
+ * The source will not initially be associated with any #GMainContext
+ * and must be added to one with g_source_attach() before it will be
+ * executed.
+ * 
+ * Return value: the newly created idle source
+ **/
+GSource *
+g_idle_source_new (void)
+{
+  return g_source_new (&idle_funcs, sizeof (GSource));
 }
 
+/**
+ * g_idle_add_full:
+ * @priority: the priority of the idle source. Typically this will be in the
+ *            range btweeen #G_PRIORITY_DEFAULT_IDLE and #G_PRIORITY_HIGH_IDLE.
+ * @function: function to call
+ * @data:     data to pass to @function
+ * @notify:   function to call when the idle is removed, or %NULL
+ * 
+ * Adds a function to be called whenever there are no higher priority
+ * events pending.  If the function returns FALSE it is automatically
+ * removed from the list of event sources and will not be called again.
+ * 
+ * Return value: the id of the event source.
+ **/
 guint 
 g_idle_add_full (gint           priority,
                 GSourceFunc    function,
                 gpointer       data,
                 GDestroyNotify notify)
 {
+  GSource *source;
+  
   g_return_val_if_fail (function != NULL, 0);
 
-  return g_source_add (priority, FALSE, &idle_funcs, (gpointer) function, data, notify);
+  source = g_idle_source_new ();
+
+  if (priority != G_PRIORITY_DEFAULT)
+    g_source_set_priority (source, priority);
+
+  g_source_set_callback (source, function, data, notify);
+  return g_source_attach (source, NULL);
 }
 
+/**
+ * g_idle_add:
+ * @function: function to call 
+ * @data: data to pass to @function.
+ * 
+ * Adds a function to be called whenever there are no higher priority
+ * events pending to the default main loop. The function is given the
+ * default idle priority, #G_PRIORITY_DEFAULT_IDLE.  If the function
+ * returns FALSE it is automatically removed from the list of event
+ * sources and will not be called again.
+ * 
+ * Return value: the id of the event source.
+ **/
 guint 
 g_idle_add (GSourceFunc    function,
            gpointer       data)
@@ -1426,8 +2763,17 @@ g_idle_add (GSourceFunc    function,
   return g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, function, data, NULL);
 }
 
+/**
+ * g_idle_remove_by_data:
+ * @data: the data for the idle source's callback.
+ * 
+ * Removes the idle function with the given data.
+ * 
+ * Return value: %TRUE if an idle source was found and removed.
+ **/
 gboolean
 g_idle_remove_by_data (gpointer data)
 {
   return g_source_remove_by_funcs_user_data (&idle_funcs, data);
 }
+
diff --git a/gmain.h b/gmain.h
index 7f08c3dfbd07b04eca85d2af0b665d859fcf72ba..8e4d7fce7456fccf0e7acfadfca77e81ef71a064 100644 (file)
--- a/gmain.h
+++ b/gmain.h
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
+/* gmain.h - the GLib Main loop
+ * Copyright (C) 1998-2000 Red Hat, Inc.
  *
  * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
+ * modify it under the terms of the GNU Library 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.
+ * Library General Public License for more details.
  *
- * You should have received a copy of the GNU Lesser General Public
+ * You should have received a copy of the GNU Library 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.
  */
 
-/*
- * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
- * file for a list of people on the GLib Team.  See the ChangeLog
- * files for a list of changes.  These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/. 
- */
-
 #ifndef __G_MAIN_H__
 #define __G_MAIN_H__
 
-#include <gtypes.h>
+#include <gslist.h>
+#include <gthread.h>
 
 G_BEGIN_DECLS
 
-/* Main loop
- */
-typedef struct _GTimeVal        GTimeVal;
-typedef struct _GSourceFuncs    GSourceFuncs;
-typedef struct _GMainLoop       GMainLoop;      /* Opaque */
-
-struct _GTimeVal
-{
-  glong tv_sec;
-  glong tv_usec;
-};
-struct _GSourceFuncs
-{
-  gboolean (*prepare)  (gpointer  source_data,
-                        GTimeVal *current_time,
-                        gint     *timeout,
-                        gpointer  user_data);
-  gboolean (*check)    (gpointer  source_data,
-                        GTimeVal *current_time,
-                        gpointer  user_data);
-  gboolean (*dispatch) (gpointer  source_data,
-                        GTimeVal *dispatch_time,
-                        gpointer  user_data);
-  GDestroyNotify destroy;
-};
+typedef struct _GMainContext           GMainContext;   /* Opaque */
+typedef struct _GMainLoop              GMainLoop;      /* Opaque */
+typedef struct _GSource                        GSource;
+typedef struct _GSourceCallbackFuncs   GSourceCallbackFuncs;
+typedef struct _GSourceFuncs           GSourceFuncs;
 
-/* Standard priorities */
+typedef gboolean (*GSourceFunc)       (gpointer data);
 
-#define G_PRIORITY_HIGH            -100
-#define G_PRIORITY_DEFAULT          0
-#define G_PRIORITY_HIGH_IDLE        100
-#define G_PRIORITY_DEFAULT_IDLE     200
-#define G_PRIORITY_LOW              300
+struct _GSource
+{
+  /*< private >*/
+  gpointer callback_data;
+  GSourceCallbackFuncs *callback_funcs;
 
-typedef gboolean (*GSourceFunc) (gpointer data);
+  GSourceFuncs *source_funcs;
+  guint ref_count;
 
-/* Hooks for adding to the main loop */
-guint    g_source_add                        (gint           priority,
-                                              gboolean       can_recurse,
-                                              GSourceFuncs  *funcs,
-                                              gpointer       source_data,
-                                              gpointer       user_data,
-                                              GDestroyNotify notify);
-gboolean g_source_remove                     (guint          tag);
-gboolean g_source_remove_by_user_data        (gpointer       user_data);
-gboolean g_source_remove_by_source_data      (gpointer       source_data);
-gboolean g_source_remove_by_funcs_user_data  (GSourceFuncs  *funcs,
-                                              gpointer       user_data);
+  GMainContext *context;
 
-void g_get_current_time                 (GTimeVal       *result);
+  gint priority;
+  guint flags;
+  guint id;
 
-/* Running the main loop */
-GMainLoop*      g_main_new              (gboolean        is_running);
-void            g_main_run              (GMainLoop      *loop);
-void            g_main_quit             (GMainLoop      *loop);
-void            g_main_destroy          (GMainLoop      *loop);
-gboolean        g_main_is_running       (GMainLoop      *loop);
+  GSList *poll_fds;
+  
+  GSource *prev;
+  GSource *next;
+};
 
-/* Run a single iteration of the mainloop. If block is FALSE,
- * will never block
- */
-gboolean        g_main_iteration        (gboolean       may_block);
+struct _GSourceCallbackFuncs
+{
+  void (*ref)   (gpointer     cb_data);
+  void (*unref) (gpointer     cb_data);
+  void (*get)   (gpointer     cb_data,
+                GSourceFunc *func,
+                gpointer    *data);
+};
 
-/* See if any events are pending */
-gboolean        g_main_pending          (void);
+struct _GSourceFuncs
+{
+  gboolean (*prepare)  (GSource    *source,
+                       gint       *timeout);
+  gboolean (*check)    (GSource    *source);
+  gboolean (*dispatch) (GSource    *source,
+                       GSourceFunc callback,
+                       gpointer    user_data);
+  void     (*destroy)  (GSource    *source);
+};
 
-/* Idles and timeouts */
-guint           g_timeout_add_full      (gint           priority,
-                                         guint          interval,
-                                         GSourceFunc    function,
-                                         gpointer       data,
-                                         GDestroyNotify notify);
-guint           g_timeout_add           (guint          interval,
-                                         GSourceFunc    function,
-                                         gpointer       data);
-guint           g_idle_add              (GSourceFunc    function,
-                                         gpointer       data);
-guint           g_idle_add_full         (gint           priority,
-                                         GSourceFunc    function,
-                                         gpointer       data,
-                                         GDestroyNotify destroy);
-gboolean        g_idle_remove_by_data   (gpointer       data);
-
-/* GPollFD
+/* Any definitions using GPollFD or GPollFunc are primarily
+ * for Unix and not guaranteed to be the compatible on all
+ * operating systems on which GLib runs. Right now, the
+ * GLib does use these functions on Win32 as well, but interprets
+ * them in a fairly different way than on Unix. If you use
+ * these definitions, you are should be prepared to recode
+ * for different operating systems.
  *
- * System-specific IO and main loop calls
  *
  * On Win32, the fd in a GPollFD should be Win32 HANDLE (*not* a file
  * descriptor as provided by the C runtime) that can be used by
@@ -137,32 +102,185 @@ gboolean        g_idle_remove_by_data   (gpointer       data);
  * Win32. It's really only written for the GIMP's needs so
  * far.
  */
-
 typedef struct _GPollFD GPollFD;
-typedef gint    (*GPollFunc)    (GPollFD *ufds,
-                                 guint    nfsd,
-                                 gint     timeout);
+typedef gint   (*GPollFunc)    (GPollFD *ufds,
+                                guint    nfsd,
+                                gint     timeout);
+
 struct _GPollFD
 {
-  gint          fd;
-  gushort       events;
-  gushort       revents;
+  gint         fd;
+  gushort      events;
+  gushort      revents;
 };
 
-void        g_main_add_poll          (GPollFD    *fd,
-                                      gint        priority);
-void        g_main_remove_poll       (GPollFD    *fd);
-void        g_main_set_poll_func     (GPollFunc   func);
+/* Standard priorities */
 
-#ifdef G_OS_WIN32
+#define G_PRIORITY_HIGH            -100
+#define G_PRIORITY_DEFAULT          0
+#define G_PRIORITY_HIGH_IDLE        100
+#define G_PRIORITY_DEFAULT_IDLE     200
+#define G_PRIORITY_LOW             300
+
+/* GMainContext: */
 
-/* Useful on other platforms, too? */
+GMainContext *g_main_context_get       (GThread      *thread);
+GMainContext *g_main_context_default   (void);
 
-GPollFunc   g_main_win32_get_poll_func (void);
+gboolean      g_main_context_iteration (GMainContext *context,
+                                       gboolean      may_block);
+gboolean      g_main_context_pending   (GMainContext *context);
+
+/* For implementation of legacy interfaces
+ */
+GSource      *g_main_context_find_source_by_id              (GMainContext *context,
+                                                            guint         id);
+GSource      *g_main_context_find_source_by_user_data       (GMainContext *context,
+                                                            gpointer      user_data);
+GSource      *g_main_context_find_source_by_funcs_user_data (GMainContext *context,
+                                                            GSourceFuncs *funcs,
+                                                            gpointer      user_data);
+
+/* Low level functions for implementing custom main loops.
+ */
+gboolean g_main_context_prepare  (GMainContext *context,
+                                 gint         *priority);
+gint     g_main_context_query    (GMainContext *context,
+                                 gint          max_priority,
+                                 gint         *timeout,
+                                 GPollFD      *fds,
+                                 gint          n_fds);
+gint     g_main_context_check    (GMainContext *context,
+                                 gint          max_priority,
+                                 GPollFD      *fds,
+                                 gint          n_fds);
+void     g_main_context_dispatch (GMainContext *context);
 
-#endif
+void      g_main_context_set_poll_func (GMainContext *context,
+                                       GPollFunc     func);
+GPollFunc g_main_context_get_poll_func (GMainContext *context);
+
+/* Low level functions for use by source implementations
+ */
+void g_main_context_add_poll      (GMainContext *context,
+                                  GPollFD      *fd,
+                                  gint          priority);
+void g_main_context_remove_poll   (GMainContext *context,
+                                  GPollFD      *fd);
+
+/* GMainLoop: */
+
+GMainLoop *g_main_loop_new        (GMainContext *context,
+                                  gboolean      is_running);
+void       g_main_loop_run        (GMainLoop    *loop);
+void       g_main_loop_quit       (GMainLoop    *loop);
+void       g_main_loop_destroy    (GMainLoop    *loop);
+gboolean   g_main_loop_is_running (GMainLoop    *loop);
+
+/* GSource: */
+
+GSource *g_source_new             (GSourceFuncs   *source_funcs,
+                                  guint           struct_size);
+GSource *g_source_ref             (GSource        *source);
+void     g_source_unref           (GSource        *source);
+
+guint    g_source_attach          (GSource        *source,
+                                  GMainContext   *context);
+void     g_source_destroy         (GSource        *source);
+
+void     g_source_set_priority    (GSource        *source,
+                                  gint            priority);
+gint     g_source_get_priority    (GSource        *source);
+void     g_source_set_can_recurse (GSource        *source,
+                                  gboolean        can_recurse);
+gboolean g_source_get_can_recurse (GSource        *source);
+guint    g_source_get_id          (GSource        *source);
+
+GMainContext *g_source_get_context (GSource       *source);
+
+void g_source_set_callback          (GSource              *source,
+                                    GSourceFunc           func,
+                                    gpointer              data,
+                                    GDestroyNotify        notify);
+
+
+/* Used to implement g_source_connect_closure and internally*/
+void g_source_set_callback_indirect (GSource              *source,
+                                    gpointer              callback_data,
+                                    GSourceCallbackFuncs *callback_funcs);
+
+void     g_source_add_poll         (GSource        *source,
+                                   GPollFD        *fd);
+void     g_source_remove_poll      (GSource        *source,
+                                   GPollFD        *fd);
+
+void     g_source_get_current_time (GSource        *source,
+                                   GTimeVal       *timeval);
+
+ /* void g_source_connect_closure (GSource        *source,
+                                  GClosure       *closure);
+ */
+
+/* Specific source types
+ */
+GSource *g_idle_source_new    (void);
+GSource *g_timeout_source_new (guint         interval);
+
+/* Miscellaneous functions
+ */
+void g_get_current_time                        (GTimeVal       *result);
+
+/* ============== Compat main loop stuff ================== */
+
+/* Legacy names for GMainLoop functions
+ */
+#define        g_main_new(is_running)  g_main_loop_new (NULL, is_running);
+#define         g_main_run(loop)        g_main_loop_run(loop)
+#define         g_main_quit(loop)       g_main_loop_quit(loop)
+#define         g_main_destroy(loop)    g_main_loop_destroy(loop)
+#define         g_main_is_running(loop) g_main_loop_is_running(loop)
+
+/* Source manipulation by ID */
+gboolean g_source_remove                     (guint          tag);
+gboolean g_source_remove_by_user_data        (gpointer       user_data);
+gboolean g_source_remove_by_funcs_user_data  (GSourceFuncs  *funcs,
+                                             gpointer       user_data);
+
+/* Functions to manipulate the default main loop
+ */
+
+#define        g_main_iteration(may_block) g_main_context_iteration      (NULL, may_block)
+#define g_main_pending()            g_main_context_pending        (NULL)
+
+#define g_main_set_poll_func(func)   g_main_context_set_poll_func (NULL, func)
+
+/* Idles and timeouts */
+guint          g_timeout_add_full      (gint           priority,
+                                        guint          interval, 
+                                        GSourceFunc    function,
+                                        gpointer       data,
+                                        GDestroyNotify notify);
+guint          g_timeout_add           (guint          interval,
+                                        GSourceFunc    function,
+                                        gpointer       data);
+guint          g_idle_add              (GSourceFunc    function,
+                                        gpointer       data);
+guint          g_idle_add_full         (gint           priority,
+                                        GSourceFunc    function,
+                                        gpointer       data,
+                                        GDestroyNotify notify);
+gboolean       g_idle_remove_by_data   (gpointer       data);
+
+#ifdef G_OS_WIN32
+
+/* This is used to add polling for Windows messages. GDK (GTK+) programs
+ * should *not* use this.
+ */
+void        g_main_poll_win32_msg_add (gint        priority,
+                                      GPollFD    *fd,
+                                      guint       hwnd);
+#endif G_OS_WIN32
 
 G_END_DECLS
 
 #endif /* __G_MAIN_H__ */
-
index e99c78d5e4b2cab78572b0bcf480423ab807bcce..12681784be2be1807ccf7bed16254931828d8eb3 100644 (file)
--- a/gthread.h
+++ b/gthread.h
@@ -28,7 +28,6 @@
 #define __G_THREAD_H__
 
 #include <gerror.h>
-#include <gmain.h>
 
 G_BEGIN_DECLS
 
index c43d322a70c6806632176d830d68feee5686b5d1..9614c73db4072e12a89e622f227e91d85b01a736 100644 (file)
--- a/gtypes.h
+++ b/gtypes.h
@@ -314,6 +314,14 @@ union _GDoubleIEEE754
 #error unknown ENDIAN type
 #endif /* !G_LITTLE_ENDIAN && !G_BIG_ENDIAN */
 
+typedef struct _GTimeVal                GTimeVal;
+
+struct _GTimeVal
+{
+  glong tv_sec;
+  glong tv_usec;
+};
+
 G_END_DECLS
 
 #endif /* __G_TYPES_H__ */
index a714e362e315f1b415d8416e86214ec390a6411d..4f67021e9b59f0940e59d288bb1baed2b3b9d06f 100644 (file)
@@ -11,6 +11,7 @@ date-test
 dirname-test
 hash-test
 list-test
+mainloop-test
 markup-test
 node-test
 queue-test
index 0838262b9c9d6e6fa6131dcfcaad8a35562ab80e..3195e8fa3b2dc09e181ac7706222e0e2fc10ceb2 100644 (file)
@@ -19,6 +19,7 @@ TESTS = \
        gio-test        \
        hash-test       \
        list-test       \
+       mainloop-test   \
        markup-test     \
        node-test       \
        queue-test      \
@@ -45,6 +46,7 @@ dirname_test_LDADD = $(progs_LDADD)
 gio_test_LDADD = $(progs_LDADD)
 hash_test_LDADD = $(progs_LDADD)
 list_test_LDADD = $(progs_LDADD)
+mainloop_test_LDADD = $(thread_LDADD)
 markup_test_LDADD = $(progs_LDADD)
 node_test_LDADD = $(progs_LDADD)
 queue_test_LDADD = $(progs_LDADD)
diff --git a/tests/mainloop-test.c b/tests/mainloop-test.c
new file mode 100644 (file)
index 0000000..aebefd1
--- /dev/null
@@ -0,0 +1,396 @@
+#include <errno.h>
+#include <glib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define ITERS 10000
+#define INCREMENT 10
+#define NTHREADS 4
+#define NCRAWLERS 4
+#define CRAWLER_TIMEOUT_RANGE 40
+#define RECURSER_TIMEOUT 50
+
+G_LOCK_DEFINE_STATIC (context_array_lock);
+GPtrArray *context_array;
+GMainLoop *main_loop;
+
+G_LOCK_DEFINE_STATIC (crawler_array_lock);
+GPtrArray *crawler_array;
+
+typedef struct _AddrData AddrData;
+typedef struct _TestData TestData;
+
+struct _AddrData
+{
+  GMainLoop *loop;
+  GIOChannel *dest;
+  gint count;
+};
+
+struct _TestData
+{
+  gint current_val;
+  gint iters;
+  GIOChannel *in;
+};
+
+static void cleanup_crawlers (GMainContext *context);
+
+gboolean
+read_all (GIOChannel *channel, char *buf, int len)
+{
+  int bytes_read = 0;
+  int count;
+  GIOError err;
+
+  while (bytes_read < len)
+    {
+      err = g_io_channel_read (channel, buf + bytes_read, len - bytes_read, &count);
+      if (err)
+       {
+         if (err != G_IO_ERROR_AGAIN)
+           return FALSE;
+       }
+      else if (count == 0)
+       return FALSE;
+
+      bytes_read += count;
+    }
+
+  return TRUE;
+}
+
+gboolean
+write_all (GIOChannel *channel, char *buf, int len)
+{
+  int bytes_written = 0;
+  int count;
+  GIOError err;
+
+  while (bytes_written < len)
+    {
+      err = g_io_channel_write (channel, buf + bytes_written, len - bytes_written, &count);
+      if (err && err != G_IO_ERROR_AGAIN)
+       return FALSE;
+
+      bytes_written += count;
+    }
+
+  return TRUE;
+}
+
+gboolean
+adder_callback (GIOChannel   *source,
+               GIOCondition  condition,
+               gpointer      data)
+{
+  char buf1[32];
+  char buf2[32];
+
+  char result[32];
+
+  AddrData *addr_data = data;
+
+  if (!read_all (source, buf1, 32) ||
+      !read_all (source, buf2, 32))
+    {
+      g_main_loop_quit (addr_data->loop);
+      return FALSE;
+    }
+
+  sprintf (result, "%d", atoi(buf1) + atoi(buf2));
+  write_all (addr_data->dest, result, 32);
+  
+  return TRUE;
+}
+
+gboolean
+timeout_callback (gpointer data)
+{
+  AddrData *addr_data = data;
+
+  addr_data->count++;
+  
+  return TRUE;
+}
+
+void
+adder_thread (gpointer data)
+{
+  GMainContext *context;
+  GSource *adder_source;
+  GSource *timeout_source;
+
+  GIOChannel **channels = data;
+  AddrData addr_data;
+
+  context = g_main_context_get (g_thread_self());
+
+  G_LOCK (context_array_lock);
+  g_ptr_array_add (context_array, context);
+  G_UNLOCK (context_array_lock);
+
+  addr_data.dest = channels[1];
+  addr_data.loop = g_main_loop_new (context, FALSE);
+  addr_data.count = 0;
+  
+  adder_source = g_io_create_watch (channels[0], G_IO_IN | G_IO_HUP);
+  g_source_set_callback (adder_source, (GSourceFunc)adder_callback, &addr_data, NULL);
+  g_source_attach (adder_source, context);
+
+  timeout_source = g_timeout_source_new (10);
+  g_source_set_callback (timeout_source, (GSourceFunc)timeout_callback, &addr_data, NULL);
+  g_source_set_priority (timeout_source, G_PRIORITY_HIGH);
+  g_source_attach (timeout_source, context);
+
+  g_main_run (addr_data.loop);
+
+  g_io_channel_close (channels[0]);
+  g_io_channel_close (channels[1]);
+  g_io_channel_unref (channels[0]);
+  g_io_channel_unref (channels[1]);
+
+  g_free (channels);
+  
+  g_main_loop_destroy (addr_data.loop);
+
+  g_print ("Timeout run %d times\n", addr_data.count);
+
+  G_LOCK (context_array_lock);
+  g_ptr_array_remove (context_array, context);
+  if (context_array->len == 0)
+    g_main_loop_quit (main_loop);
+  G_UNLOCK (context_array_lock);
+
+  cleanup_crawlers (context);
+}
+
+void
+io_pipe (GIOChannel **channels)
+{
+  gint fds[2];
+
+  if (pipe(fds) < 0)
+    {
+      g_warning ("Cannot create pipe %s\n", g_strerror (errno));
+      exit (1);
+    }
+
+  channels[0] = g_io_channel_unix_new (fds[0]);
+  channels[1] = g_io_channel_unix_new (fds[1]);
+}
+
+void
+do_add (GIOChannel *in, gint a, gint b)
+{
+  char buf1[32];
+  char buf2[32];
+
+  sprintf (buf1, "%d", a);
+  sprintf (buf2, "%d", b);
+
+  write_all (in, buf1, 32);
+  write_all (in, buf2, 32);
+}
+
+gboolean
+adder_response (GIOChannel   *source,
+               GIOCondition  condition,
+               gpointer      data)
+{
+  char result[32];
+  TestData *test_data = data;
+  
+  if (!read_all (source, result, 32))
+    return FALSE;
+
+  test_data->current_val = atoi (result);
+  test_data->iters--;
+
+  if (test_data->iters == 0)
+    {
+      if (test_data->current_val != ITERS * INCREMENT)
+       {
+         g_print ("Addition failed: %d != %d\n",
+                  test_data->current_val, ITERS * INCREMENT);
+         exit (1);
+       }
+
+      g_io_channel_close (source);
+      g_io_channel_close (test_data->in);
+
+      g_io_channel_unref (source);
+      g_io_channel_unref (test_data->in);
+      
+      return FALSE;
+    }
+  
+  do_add (test_data->in, test_data->current_val, INCREMENT);
+
+  return TRUE;
+}
+
+void
+create_adder_thread (void)
+{
+  GError *err = NULL;
+  TestData *test_data;
+  
+  GIOChannel *in_channels[2];
+  GIOChannel *out_channels[2];
+
+  GIOChannel **sub_channels;
+
+  sub_channels = g_new (GIOChannel *, 2);
+
+  io_pipe (in_channels);
+  io_pipe (out_channels);
+
+  sub_channels[0] = in_channels[0];
+  sub_channels[1] = out_channels[1];
+
+  g_thread_create (adder_thread, sub_channels, 0,
+                  FALSE, TRUE, G_THREAD_PRIORITY_NORMAL, &err);
+
+  if (err)
+    {
+      g_warning ("Cannot create thread: %s", err->message);
+      exit (1);
+    }
+
+  test_data = g_new (TestData, 1);
+  test_data->in = in_channels[1];
+  test_data->current_val = 0;
+  test_data->iters = ITERS;
+
+  g_io_add_watch (out_channels[0], G_IO_IN | G_IO_HUP,
+                 adder_response, test_data);
+  
+  do_add (test_data->in, test_data->current_val, INCREMENT);
+}
+
+static void create_crawler (void);
+
+static void
+remove_crawler (void)
+{
+  GSource *other_source;
+
+  if (crawler_array->len > 0)
+    {
+      other_source = crawler_array->pdata[g_random_int_range (0, crawler_array->len)];
+      g_source_destroy (other_source);
+      g_assert (g_ptr_array_remove_fast (crawler_array, other_source));
+    }
+}
+
+static gint
+crawler_callback (gpointer data)
+{
+  GSource *source = data;
+
+  G_LOCK (crawler_array_lock);
+  
+  if (!g_ptr_array_remove_fast (crawler_array, source))
+    remove_crawler();
+
+  remove_crawler();
+  G_UNLOCK (crawler_array_lock);
+           
+  create_crawler();
+  create_crawler();
+
+  return FALSE;
+}
+
+static void
+create_crawler (void)
+{
+  GSource *source = g_timeout_source_new (g_random_int_range (0, CRAWLER_TIMEOUT_RANGE));
+  g_source_set_callback (source, (GSourceFunc)crawler_callback, source, NULL);
+
+  G_LOCK (context_array_lock);
+  g_source_attach (source, context_array->pdata[g_random_int_range (0, context_array->len)]);
+  G_UNLOCK (context_array_lock);
+
+  G_LOCK (crawler_array_lock);
+  g_ptr_array_add (crawler_array, source);
+  G_UNLOCK (crawler_array_lock);
+}
+
+static void
+cleanup_crawlers (GMainContext *context)
+{
+  gint i;
+  
+  G_LOCK (crawler_array_lock);
+  for (i=0; i < crawler_array->len; i++)
+    {
+      if (g_source_get_context (crawler_array->pdata[i]) == context)
+       {
+         g_source_destroy (g_ptr_array_remove_index (crawler_array, i));
+         i--;
+       }
+    }
+  G_UNLOCK (crawler_array_lock);
+}
+
+static gboolean
+recurser_idle (gpointer data)
+{
+  GMainContext *context = data;
+  gint i;
+
+  for (i = 0; i < 10; i++)
+    g_main_context_iteration (context, TRUE);
+
+  return FALSE;
+}
+
+static gboolean
+recurser_start (gpointer data)
+{
+  GMainContext *context;
+  GSource *source;
+  
+  G_LOCK (context_array_lock);
+  context = context_array->pdata[g_random_int_range (0, context_array->len)];
+  source = g_idle_source_new ();
+  g_source_set_callback (source, recurser_idle, context, NULL);
+  g_source_attach (source, context);
+  G_UNLOCK (context_array_lock);
+
+  return TRUE;
+}
+
+int 
+main (int   argc,
+      char *argv[])
+{
+  /* Only run the test, if threads are enabled and a default thread
+     implementation is available */
+#if defined(G_THREADS_ENABLED) && ! defined(G_THREADS_IMPL_NONE)
+  gint i;
+
+  g_thread_init (NULL);
+
+  context_array = g_ptr_array_new ();
+  crawler_array = g_ptr_array_new ();
+
+  main_loop = g_main_loop_new (NULL, FALSE);
+
+  for (i = 0; i < NTHREADS; i++)
+    create_adder_thread ();
+
+  for (i = 0; i < NCRAWLERS; i++)
+    create_crawler ();
+
+  g_timeout_add (RECURSER_TIMEOUT, recurser_start, NULL);
+
+  g_main_loop_run (main_loop);
+  g_main_loop_destroy (main_loop);
+
+#endif
+  return 0;
+}
diff --git a/tests/timeloop-basic.c b/tests/timeloop-basic.c
new file mode 100644 (file)
index 0000000..64d4133
--- /dev/null
@@ -0,0 +1,232 @@
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+
+#define TRUE 1
+#define FALSE 0
+
+static int n_children = 3;
+static int n_active_children;
+static int n_iters = 10000;
+
+static int write_fds[1024];
+static struct pollfd poll_fds[1024];
+
+void
+my_pipe (int *fds)
+{
+  if (pipe(fds) < 0)
+    {
+      fprintf (stderr, "Cannot create pipe %s\n", strerror (errno));
+      exit (1);
+    }
+}
+
+int
+read_all (int fd, char *buf, int len)
+{
+  int bytes_read = 0;
+  int count;
+
+  while (bytes_read < len)
+    {
+      count = read (fd, buf + bytes_read, len - bytes_read);
+      if (count < 0)
+       {
+         if (errno != EAGAIN)
+           return FALSE;
+       }
+      else if (count == 0)
+       return FALSE;
+
+      bytes_read += count;
+    }
+
+  return TRUE;
+}
+
+int
+write_all (int fd, char *buf, int len)
+{
+  int bytes_written = 0;
+  int count;
+
+  while (bytes_written < len)
+    {
+      count = write (fd, buf + bytes_written, len - bytes_written);
+      if (count < 0)
+       {
+         if (errno != EAGAIN)
+           return FALSE;
+       }
+
+      bytes_written += count;
+    }
+
+  return TRUE;
+}
+
+void
+run_child (int in_fd, int out_fd)
+{
+  int i;
+  int val = 1;
+
+  for (i = 0; i < n_iters; i++)
+    {
+      write_all (out_fd, (char *)&val, sizeof (val));
+      read_all (in_fd, (char *)&val, sizeof (val));
+    }
+
+  val = 0;
+  write_all (out_fd, (char *)&val, sizeof (val));
+
+  exit (0);
+}
+
+int
+input_callback (int source, int dest)
+{
+  int val;
+  
+  if (!read_all (source, (char *)&val, sizeof(val)))
+    {
+      fprintf (stderr,"Unexpected EOF\n");
+      exit (1);
+    }
+
+  if (val)
+    {
+      write_all (dest, (char *)&val, sizeof(val));
+      return TRUE;
+    }
+  else
+    {
+      close (source);
+      close (dest);
+      
+      n_active_children--;
+      return FALSE;
+    }
+}
+
+void
+create_child (int pos)
+{
+  int pid;
+  int in_fds[2];
+  int out_fds[2];
+  
+  my_pipe (in_fds);
+  my_pipe (out_fds);
+
+  pid = fork ();
+
+  if (pid > 0)                 /* Parent */
+    {
+      close (in_fds[0]);
+      close (out_fds[1]);
+
+      write_fds[pos] = in_fds[1];
+      poll_fds[pos].fd = out_fds[0];
+      poll_fds[pos].events = POLLIN;
+    }
+  else if (pid == 0)           /* Child */
+    {
+      close (in_fds[1]);
+      close (out_fds[0]);
+
+      setsid ();
+
+      run_child (in_fds[0], out_fds[1]);
+    }
+  else                         /* Error */
+    {
+      fprintf (stderr,"Cannot fork: %s\n", strerror (errno));
+      exit (1);
+    }
+}
+
+static double 
+difftimeval (struct timeval *old, struct timeval *new)
+{
+  return
+    (new->tv_sec - old->tv_sec) * 1000. + (new->tv_usec - old->tv_usec) / 1000;
+}
+
+int 
+main (int argc, char **argv)
+{
+  int i, j;
+  struct rusage old_usage;
+  struct rusage new_usage;
+  
+  if (argc > 1)
+    n_children = atoi(argv[1]);
+
+  if (argc > 2)
+    n_iters = atoi(argv[2]);
+
+  printf ("Children: %d     Iters: %d\n", n_children, n_iters);
+
+  n_active_children = n_children;
+  for (i = 0; i < n_children; i++)
+    create_child (i);
+
+  getrusage (RUSAGE_SELF, &old_usage);
+
+  while (n_active_children > 0)
+    {
+      int old_n_active_children = n_active_children;
+
+      poll (poll_fds, n_active_children, -1);
+
+      for (i=0; i<n_active_children; i++)
+       {
+         if (poll_fds[i].events & (POLLIN | POLLHUP))
+           {
+             if (!input_callback (poll_fds[i].fd, write_fds[i]))
+               write_fds[i] = -1;
+           }
+       }
+
+      if (old_n_active_children > n_active_children)
+       {
+         j = 0;
+         for (i=0; i<old_n_active_children; i++)
+           {
+             if (write_fds[i] != -1)
+               {
+                 if (j < i)
+                   {
+                     poll_fds[j] = poll_fds[i];
+                     write_fds[j] = write_fds[i];
+                   }
+                 j++;
+               }
+           }
+       }
+    }
+
+  getrusage (RUSAGE_SELF, &new_usage);
+
+  printf ("Elapsed user: %g\n",
+          difftimeval (&old_usage.ru_utime, &new_usage.ru_utime));
+  printf ("Elapsed system: %g\n",
+          difftimeval (&old_usage.ru_stime, &new_usage.ru_stime));
+  printf ("Elapsed total: %g\n",
+         difftimeval (&old_usage.ru_utime, &new_usage.ru_utime) +         
+          difftimeval (&old_usage.ru_stime, &new_usage.ru_stime));
+  printf ("total / iteration: %g\n",
+          (difftimeval (&old_usage.ru_utime, &new_usage.ru_utime) +       
+           difftimeval (&old_usage.ru_stime, &new_usage.ru_stime)) /
+          (n_iters * n_children));
+
+  return 0;
+}
+
diff --git a/tests/timeloop.c b/tests/timeloop.c
new file mode 100644 (file)
index 0000000..cacc8e4
--- /dev/null
@@ -0,0 +1,213 @@
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+
+#include <glib.h>
+
+static int n_children = 3;
+static int n_active_children;
+static int n_iters = 10000;
+static GMainLoop *loop;
+
+void
+io_pipe (GIOChannel **channels)
+{
+  int fds[2];
+
+  if (pipe(fds) < 0)
+    {
+      fprintf (stderr, "Cannot create pipe %s\n", g_strerror (errno));
+      exit (1);
+    }
+
+  channels[0] = g_io_channel_unix_new (fds[0]);
+  channels[1] = g_io_channel_unix_new (fds[1]);
+}
+
+gboolean
+read_all (GIOChannel *channel, char *buf, int len)
+{
+  int bytes_read = 0;
+  int count;
+  GIOError err;
+
+  while (bytes_read < len)
+    {
+      err = g_io_channel_read (channel, buf + bytes_read, len - bytes_read, &count);
+      if (err)
+       {
+         if (err != G_IO_ERROR_AGAIN)
+           return FALSE;
+       }
+      else if (count == 0)
+       return FALSE;
+
+      bytes_read += count;
+    }
+
+  return TRUE;
+}
+
+gboolean
+write_all (GIOChannel *channel, char *buf, int len)
+{
+  int bytes_written = 0;
+  int count;
+  GIOError err;
+
+  while (bytes_written < len)
+    {
+      err = g_io_channel_write (channel, buf + bytes_written, len - bytes_written, &count);
+      if (err && err != G_IO_ERROR_AGAIN)
+       return FALSE;
+
+      bytes_written += count;
+    }
+
+  return TRUE;
+}
+
+void
+run_child (GIOChannel *in_channel, GIOChannel *out_channel)
+{
+  int i;
+  int val = 1;
+  GTimer *timer = g_timer_new();
+
+  for (i = 0; i < n_iters; i++)
+    {
+      write_all (out_channel, (char *)&val, sizeof (val));
+      read_all (in_channel, (char *)&val, sizeof (val));
+    }
+
+  val = 0;
+  write_all (out_channel, (char *)&val, sizeof (val));
+
+  val = g_timer_elapsed (timer, NULL) * 1000;
+  
+  write_all (out_channel, (char *)&val, sizeof (val));
+  g_timer_destroy (timer);
+
+  exit (0);
+}
+
+gboolean
+input_callback (GIOChannel   *source,
+               GIOCondition  condition,
+               gpointer      data)
+{
+  int val;
+  GIOChannel *dest = (GIOChannel *)data;
+  
+  if (!read_all (source, (char *)&val, sizeof(val)))
+    {
+      fprintf (stderr, "Unexpected EOF\n");
+      exit (1);
+    }
+
+  if (val)
+    {
+      write_all (dest, (char *)&val, sizeof(val));
+      
+      return TRUE;
+    }
+  else
+    {
+      g_io_channel_close (source);
+      g_io_channel_close (dest);
+      
+      g_io_channel_unref (source);
+      g_io_channel_unref (dest);
+
+      n_active_children--;
+      if (n_active_children == 0)
+       g_main_quit (loop);
+      
+      return FALSE;
+    }
+}
+
+void
+create_child ()
+{
+  int pid;
+  GIOChannel *in_channels[2];
+  GIOChannel *out_channels[2];
+  
+  io_pipe (in_channels);
+  io_pipe (out_channels);
+
+  pid = fork ();
+
+  if (pid > 0)                 /* Parent */
+    {
+      g_io_channel_close (in_channels[0]);
+      g_io_channel_close (out_channels[1]);
+
+      g_io_add_watch (out_channels[0], G_IO_IN | G_IO_HUP,
+                     input_callback, in_channels[1]);
+    }
+  else if (pid == 0)           /* Child */
+    {
+      g_io_channel_close (in_channels[1]);
+      g_io_channel_close (out_channels[0]);
+
+      setsid ();
+
+      run_child (in_channels[0], out_channels[1]);
+    }
+  else                         /* Error */
+    {
+      fprintf (stderr, "Cannot fork: %s\n", g_strerror (errno));
+      exit (1);
+    }
+}
+
+static double 
+difftimeval (struct timeval *old, struct timeval *new)
+{
+  return
+    (new->tv_sec - old->tv_sec) * 1000. + (new->tv_usec - old->tv_usec) / 1000;
+}
+
+int 
+main (int argc, char **argv)
+{
+  int i;
+  struct rusage old_usage;
+  struct rusage new_usage;
+  
+  if (argc > 1)
+    n_children = atoi(argv[1]);
+
+  if (argc > 2)
+    n_iters = atoi(argv[2]);
+
+  printf ("Children: %d     Iters: %d\n", n_children, n_iters);
+
+  n_active_children = n_children;
+  for (i = 0; i < n_children; i++)
+    create_child ();
+
+  getrusage (RUSAGE_SELF, &old_usage);
+  loop = g_main_new (FALSE);
+  g_main_run (loop);
+  getrusage (RUSAGE_SELF, &new_usage);
+
+  printf ("Elapsed user: %g\n",
+         difftimeval (&old_usage.ru_utime, &new_usage.ru_utime));
+  printf ("Elapsed system: %g\n",
+         difftimeval (&old_usage.ru_stime, &new_usage.ru_stime));
+  printf ("Elapsed total: %g\n",
+         difftimeval (&old_usage.ru_utime, &new_usage.ru_utime) +         
+         difftimeval (&old_usage.ru_stime, &new_usage.ru_stime));
+  printf ("total / iteration: %g\n",
+         (difftimeval (&old_usage.ru_utime, &new_usage.ru_utime) +        
+          difftimeval (&old_usage.ru_stime, &new_usage.ru_stime)) /
+         (n_iters * n_children));
+
+  return 0;
+}
diff --git a/timeloop-basic.c b/timeloop-basic.c
new file mode 100644 (file)
index 0000000..64d4133
--- /dev/null
@@ -0,0 +1,232 @@
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+
+#define TRUE 1
+#define FALSE 0
+
+static int n_children = 3;
+static int n_active_children;
+static int n_iters = 10000;
+
+static int write_fds[1024];
+static struct pollfd poll_fds[1024];
+
+void
+my_pipe (int *fds)
+{
+  if (pipe(fds) < 0)
+    {
+      fprintf (stderr, "Cannot create pipe %s\n", strerror (errno));
+      exit (1);
+    }
+}
+
+int
+read_all (int fd, char *buf, int len)
+{
+  int bytes_read = 0;
+  int count;
+
+  while (bytes_read < len)
+    {
+      count = read (fd, buf + bytes_read, len - bytes_read);
+      if (count < 0)
+       {
+         if (errno != EAGAIN)
+           return FALSE;
+       }
+      else if (count == 0)
+       return FALSE;
+
+      bytes_read += count;
+    }
+
+  return TRUE;
+}
+
+int
+write_all (int fd, char *buf, int len)
+{
+  int bytes_written = 0;
+  int count;
+
+  while (bytes_written < len)
+    {
+      count = write (fd, buf + bytes_written, len - bytes_written);
+      if (count < 0)
+       {
+         if (errno != EAGAIN)
+           return FALSE;
+       }
+
+      bytes_written += count;
+    }
+
+  return TRUE;
+}
+
+void
+run_child (int in_fd, int out_fd)
+{
+  int i;
+  int val = 1;
+
+  for (i = 0; i < n_iters; i++)
+    {
+      write_all (out_fd, (char *)&val, sizeof (val));
+      read_all (in_fd, (char *)&val, sizeof (val));
+    }
+
+  val = 0;
+  write_all (out_fd, (char *)&val, sizeof (val));
+
+  exit (0);
+}
+
+int
+input_callback (int source, int dest)
+{
+  int val;
+  
+  if (!read_all (source, (char *)&val, sizeof(val)))
+    {
+      fprintf (stderr,"Unexpected EOF\n");
+      exit (1);
+    }
+
+  if (val)
+    {
+      write_all (dest, (char *)&val, sizeof(val));
+      return TRUE;
+    }
+  else
+    {
+      close (source);
+      close (dest);
+      
+      n_active_children--;
+      return FALSE;
+    }
+}
+
+void
+create_child (int pos)
+{
+  int pid;
+  int in_fds[2];
+  int out_fds[2];
+  
+  my_pipe (in_fds);
+  my_pipe (out_fds);
+
+  pid = fork ();
+
+  if (pid > 0)                 /* Parent */
+    {
+      close (in_fds[0]);
+      close (out_fds[1]);
+
+      write_fds[pos] = in_fds[1];
+      poll_fds[pos].fd = out_fds[0];
+      poll_fds[pos].events = POLLIN;
+    }
+  else if (pid == 0)           /* Child */
+    {
+      close (in_fds[1]);
+      close (out_fds[0]);
+
+      setsid ();
+
+      run_child (in_fds[0], out_fds[1]);
+    }
+  else                         /* Error */
+    {
+      fprintf (stderr,"Cannot fork: %s\n", strerror (errno));
+      exit (1);
+    }
+}
+
+static double 
+difftimeval (struct timeval *old, struct timeval *new)
+{
+  return
+    (new->tv_sec - old->tv_sec) * 1000. + (new->tv_usec - old->tv_usec) / 1000;
+}
+
+int 
+main (int argc, char **argv)
+{
+  int i, j;
+  struct rusage old_usage;
+  struct rusage new_usage;
+  
+  if (argc > 1)
+    n_children = atoi(argv[1]);
+
+  if (argc > 2)
+    n_iters = atoi(argv[2]);
+
+  printf ("Children: %d     Iters: %d\n", n_children, n_iters);
+
+  n_active_children = n_children;
+  for (i = 0; i < n_children; i++)
+    create_child (i);
+
+  getrusage (RUSAGE_SELF, &old_usage);
+
+  while (n_active_children > 0)
+    {
+      int old_n_active_children = n_active_children;
+
+      poll (poll_fds, n_active_children, -1);
+
+      for (i=0; i<n_active_children; i++)
+       {
+         if (poll_fds[i].events & (POLLIN | POLLHUP))
+           {
+             if (!input_callback (poll_fds[i].fd, write_fds[i]))
+               write_fds[i] = -1;
+           }
+       }
+
+      if (old_n_active_children > n_active_children)
+       {
+         j = 0;
+         for (i=0; i<old_n_active_children; i++)
+           {
+             if (write_fds[i] != -1)
+               {
+                 if (j < i)
+                   {
+                     poll_fds[j] = poll_fds[i];
+                     write_fds[j] = write_fds[i];
+                   }
+                 j++;
+               }
+           }
+       }
+    }
+
+  getrusage (RUSAGE_SELF, &new_usage);
+
+  printf ("Elapsed user: %g\n",
+          difftimeval (&old_usage.ru_utime, &new_usage.ru_utime));
+  printf ("Elapsed system: %g\n",
+          difftimeval (&old_usage.ru_stime, &new_usage.ru_stime));
+  printf ("Elapsed total: %g\n",
+         difftimeval (&old_usage.ru_utime, &new_usage.ru_utime) +         
+          difftimeval (&old_usage.ru_stime, &new_usage.ru_stime));
+  printf ("total / iteration: %g\n",
+          (difftimeval (&old_usage.ru_utime, &new_usage.ru_utime) +       
+           difftimeval (&old_usage.ru_stime, &new_usage.ru_stime)) /
+          (n_iters * n_children));
+
+  return 0;
+}
+
diff --git a/timeloop.c b/timeloop.c
new file mode 100644 (file)
index 0000000..cacc8e4
--- /dev/null
@@ -0,0 +1,213 @@
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+
+#include <glib.h>
+
+static int n_children = 3;
+static int n_active_children;
+static int n_iters = 10000;
+static GMainLoop *loop;
+
+void
+io_pipe (GIOChannel **channels)
+{
+  int fds[2];
+
+  if (pipe(fds) < 0)
+    {
+      fprintf (stderr, "Cannot create pipe %s\n", g_strerror (errno));
+      exit (1);
+    }
+
+  channels[0] = g_io_channel_unix_new (fds[0]);
+  channels[1] = g_io_channel_unix_new (fds[1]);
+}
+
+gboolean
+read_all (GIOChannel *channel, char *buf, int len)
+{
+  int bytes_read = 0;
+  int count;
+  GIOError err;
+
+  while (bytes_read < len)
+    {
+      err = g_io_channel_read (channel, buf + bytes_read, len - bytes_read, &count);
+      if (err)
+       {
+         if (err != G_IO_ERROR_AGAIN)
+           return FALSE;
+       }
+      else if (count == 0)
+       return FALSE;
+
+      bytes_read += count;
+    }
+
+  return TRUE;
+}
+
+gboolean
+write_all (GIOChannel *channel, char *buf, int len)
+{
+  int bytes_written = 0;
+  int count;
+  GIOError err;
+
+  while (bytes_written < len)
+    {
+      err = g_io_channel_write (channel, buf + bytes_written, len - bytes_written, &count);
+      if (err && err != G_IO_ERROR_AGAIN)
+       return FALSE;
+
+      bytes_written += count;
+    }
+
+  return TRUE;
+}
+
+void
+run_child (GIOChannel *in_channel, GIOChannel *out_channel)
+{
+  int i;
+  int val = 1;
+  GTimer *timer = g_timer_new();
+
+  for (i = 0; i < n_iters; i++)
+    {
+      write_all (out_channel, (char *)&val, sizeof (val));
+      read_all (in_channel, (char *)&val, sizeof (val));
+    }
+
+  val = 0;
+  write_all (out_channel, (char *)&val, sizeof (val));
+
+  val = g_timer_elapsed (timer, NULL) * 1000;
+  
+  write_all (out_channel, (char *)&val, sizeof (val));
+  g_timer_destroy (timer);
+
+  exit (0);
+}
+
+gboolean
+input_callback (GIOChannel   *source,
+               GIOCondition  condition,
+               gpointer      data)
+{
+  int val;
+  GIOChannel *dest = (GIOChannel *)data;
+  
+  if (!read_all (source, (char *)&val, sizeof(val)))
+    {
+      fprintf (stderr, "Unexpected EOF\n");
+      exit (1);
+    }
+
+  if (val)
+    {
+      write_all (dest, (char *)&val, sizeof(val));
+      
+      return TRUE;
+    }
+  else
+    {
+      g_io_channel_close (source);
+      g_io_channel_close (dest);
+      
+      g_io_channel_unref (source);
+      g_io_channel_unref (dest);
+
+      n_active_children--;
+      if (n_active_children == 0)
+       g_main_quit (loop);
+      
+      return FALSE;
+    }
+}
+
+void
+create_child ()
+{
+  int pid;
+  GIOChannel *in_channels[2];
+  GIOChannel *out_channels[2];
+  
+  io_pipe (in_channels);
+  io_pipe (out_channels);
+
+  pid = fork ();
+
+  if (pid > 0)                 /* Parent */
+    {
+      g_io_channel_close (in_channels[0]);
+      g_io_channel_close (out_channels[1]);
+
+      g_io_add_watch (out_channels[0], G_IO_IN | G_IO_HUP,
+                     input_callback, in_channels[1]);
+    }
+  else if (pid == 0)           /* Child */
+    {
+      g_io_channel_close (in_channels[1]);
+      g_io_channel_close (out_channels[0]);
+
+      setsid ();
+
+      run_child (in_channels[0], out_channels[1]);
+    }
+  else                         /* Error */
+    {
+      fprintf (stderr, "Cannot fork: %s\n", g_strerror (errno));
+      exit (1);
+    }
+}
+
+static double 
+difftimeval (struct timeval *old, struct timeval *new)
+{
+  return
+    (new->tv_sec - old->tv_sec) * 1000. + (new->tv_usec - old->tv_usec) / 1000;
+}
+
+int 
+main (int argc, char **argv)
+{
+  int i;
+  struct rusage old_usage;
+  struct rusage new_usage;
+  
+  if (argc > 1)
+    n_children = atoi(argv[1]);
+
+  if (argc > 2)
+    n_iters = atoi(argv[2]);
+
+  printf ("Children: %d     Iters: %d\n", n_children, n_iters);
+
+  n_active_children = n_children;
+  for (i = 0; i < n_children; i++)
+    create_child ();
+
+  getrusage (RUSAGE_SELF, &old_usage);
+  loop = g_main_new (FALSE);
+  g_main_run (loop);
+  getrusage (RUSAGE_SELF, &new_usage);
+
+  printf ("Elapsed user: %g\n",
+         difftimeval (&old_usage.ru_utime, &new_usage.ru_utime));
+  printf ("Elapsed system: %g\n",
+         difftimeval (&old_usage.ru_stime, &new_usage.ru_stime));
+  printf ("Elapsed total: %g\n",
+         difftimeval (&old_usage.ru_utime, &new_usage.ru_utime) +         
+         difftimeval (&old_usage.ru_stime, &new_usage.ru_stime));
+  printf ("total / iteration: %g\n",
+         (difftimeval (&old_usage.ru_utime, &new_usage.ru_utime) +        
+          difftimeval (&old_usage.ru_stime, &new_usage.ru_stime)) /
+         (n_iters * n_children));
+
+  return 0;
+}