* Copyright 1998 Owen Taylor
*
* This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
+ * modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
*
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/*
- * Modified by the GLib Team and others 1997-1999. See the AUTHORS
+ * 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/.
#include "config.h"
+/* uncomment the next line to get poll() debugging info */
+/* #define G_MAIN_POLL_DEBUG */
+
+
+
#include "glib.h"
#include <sys/types.h>
#include <time.h>
#endif /* HAVE_UNISTD_H */
#include <errno.h>
-#ifdef NATIVE_WIN32
+#ifdef G_OS_WIN32
#define STRICT
#include <windows.h>
-#endif /* NATIVE_WIN32 */
+#endif /* G_OS_WIN32 */
-#ifdef GLIB_NATIVE_BEOS
+#ifdef G_OS_BEOS
#include <net/socket.h>
-#endif /* GLIB_NATIVE_BEOS */
+#endif /* G_OS_BEOS */
/* Types */
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);
+ gint *timeout,
+ gpointer user_data);
static gboolean g_timeout_check (gpointer source_data,
- GTimeVal *current_time);
-static gboolean g_timeout_dispatch (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);
+ gint *timeout,
+ gpointer user_data);
static gboolean g_idle_check (gpointer source_data,
- GTimeVal *current_time);
-static gboolean g_idle_dispatch (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 guint n_poll_records = 0;
#ifdef G_THREADS_ENABLED
-#ifndef NATIVE_WIN32
+#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 /* NATIVE_WIN32 */
+#else /* G_OS_WIN32 */
static HANDLE wake_up_semaphore = NULL;
-#endif /* NATIVE_WIN32 */
+#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 NATIVE_WIN32
+#ifdef G_OS_WIN32
static gint
g_poll (GPollFD *fds, guint nfds, gint timeout)
return 1;
}
-#else /* !NATIVE_WIN32 */
+#else /* !G_OS_WIN32 */
/* The following implementation of poll() comes from the GNU C Library.
* Copyright (C) 1994, 1996, 1997 Free Software Foundation, Inc.
#include <sys/select.h>
#endif /* HAVE_SYS_SELECT_H */
-#ifdef GLIB_NATIVE_BEOS
+#ifdef G_OS_BEOS
#undef NO_FD_SET
-#endif /* GLIB_NATIVE_BEOS */
+#endif /* G_OS_BEOS */
#ifndef NO_FD_SET
# define SELECT_MASK fd_set
#else /* !NO_FD_SET */
+# ifndef _AIX
+typedef long fd_mask;
+# endif /* _AIX */
# ifdef _IBMR2
# define SELECT_MASK void
# else /* !_IBMR2 */
return ready;
}
-#endif /* !NATIVE_WIN32 */
+#endif /* !G_OS_WIN32 */
static GPollFunc poll_func = g_poll;
#endif /* !HAVE_POLL */
#ifdef G_THREADS_ENABLED
/* Now wake up the main loop if it is waiting in the poll() */
-
- if (poll_waiting)
- {
- poll_waiting = FALSE;
-#ifndef NATIVE_WIN32
- write (wake_up_pipe[1], "A", 1);
-#else
- ReleaseSemaphore (wake_up_semaphore, 1, NULL);
-#endif
- }
+ g_main_wakeup ();
#endif
+
G_UNLOCK (main_loop);
return return_val;
void
g_get_current_time (GTimeVal *result)
{
-#ifndef NATIVE_WIN32
+#ifndef G_OS_WIN32
struct timeval r;
g_return_if_fail (result != NULL);
/* HOLDS: main_loop_lock */
static void
-g_main_dispatch (GTimeVal *current_time)
+g_main_dispatch (GTimeVal *dispatch_time)
{
while (pending_dispatches != NULL)
{
G_UNLOCK (main_loop);
need_destroy = ! dispatch (source_data,
- current_time,
+ dispatch_time,
hook_data);
G_LOCK (main_loop);
gboolean dispatch)
{
GHook *hook;
- GTimeVal current_time ={ 0, 0 };
+ GTimeVal current_time = { 0, 0 };
gint n_ready = 0;
gint current_priority = 0;
gint timeout;
g_get_current_time (¤t_time);
G_LOCK (main_loop);
+
+#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
/* If recursing, finish up current dispatch, before starting over */
if (pending_dispatches)
hook = g_hook_first_valid (&source_list, TRUE);
while (hook)
{
- GSource *source = (GSource *)hook;
+ GSource *source = (GSource*) hook;
gint source_timeout = -1;
if ((n_ready > 0) && (source->priority > current_priority))
{
gboolean (*prepare) (gpointer source_data,
GTimeVal *current_time,
- gint *timeout);
+ gint *timeout,
+ gpointer user_data);
prepare = ((GSourceFuncs *) hook->func)->prepare;
in_check_or_prepare++;
G_UNLOCK (main_loop);
- if ((*prepare) (source->source_data, ¤t_time, &source_timeout))
+ if ((*prepare) (source->source_data, ¤t_time, &source_timeout, source->hook.data))
hook->flags |= G_SOURCE_READY;
G_LOCK (main_loop);
g_main_poll (timeout, n_ready > 0, current_priority);
+ if (timeout != 0)
+ g_get_current_time (¤t_time);
+
/* Check to see what sources need to be dispatched */
n_ready = 0;
if (!(hook->flags & G_SOURCE_READY))
{
gboolean (*check) (gpointer source_data,
- GTimeVal *current_time);
+ GTimeVal *current_time,
+ gpointer user_data);
check = ((GSourceFuncs *) hook->func)->check;
in_check_or_prepare++;
G_UNLOCK (main_loop);
- if ((*check) (source->source_data, ¤t_time))
+ if ((*check) (source->source_data, ¤t_time, source->hook.data))
hook->flags |= G_SOURCE_READY;
G_LOCK (main_loop);
hook = g_hook_next_valid (&source_list, hook, TRUE);
}
-
+
/* Now invoke the callbacks */
if (pending_dispatches)
"prepare() member or from a second thread, iteration not possible");
return;
}
-
+
loop->is_running = TRUE;
while (loop->is_running)
g_main_iterate (TRUE, TRUE);
gboolean use_priority,
gint priority)
{
+#ifdef G_MAIN_POLL_DEBUG
+ GTimer *poll_timer;
+#endif
GPollFD *fd_array;
GPollRec *pollrec;
-
gint i;
gint npoll;
+
#ifdef G_THREADS_ENABLED
-#ifndef NATIVE_WIN32
+#ifndef G_OS_WIN32
if (wake_up_pipe[0] < 0)
{
if (pipe (wake_up_pipe) < 0)
i = 0;
while (pollrec && (!use_priority || priority >= pollrec->priority))
{
- fd_array[i].fd = pollrec->fd->fd;
- fd_array[i].events = pollrec->fd->events;
- fd_array[i].revents = 0;
-
+ 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;
- i++;
}
#ifdef G_THREADS_ENABLED
poll_waiting = TRUE;
+ poll_changed = FALSE;
#endif
- G_UNLOCK (main_loop);
+
npoll = i;
- (*poll_func) (fd_array, npoll, timeout);
- G_LOCK (main_loop);
-
+ if (npoll || timeout != 0)
+ {
+#ifdef G_MAIN_POLL_DEBUG
+ g_print ("g_main_poll(%d) timeout: %d\r", npoll, timeout);
+ poll_timer = g_timer_new ();
+#endif
+
+ G_UNLOCK (main_loop);
+ if ((*poll_func) (fd_array, npoll, timeout) < 0)
+ g_warning ("poll(2) failed due to: %s.",
+ g_strerror (errno));
+ G_LOCK (main_loop);
+
+#ifdef G_MAIN_POLL_DEBUG
+ g_print ("g_main_poll(%d) timeout: %d - elapsed %12.10f seconds",
+ npoll,
+ timeout,
+ g_timer_elapsed (poll_timer, NULL));
+ g_timer_destroy (poll_timer);
+ pollrec = poll_records;
+ i = 0;
+ while (i < npoll)
+ {
+ if (pollrec->fd->events)
+ {
+ if (fd_array[i].revents)
+ {
+ g_print (" [%d:", fd_array[i].fd);
+ if (fd_array[i].revents & G_IO_IN)
+ g_print ("i");
+ if (fd_array[i].revents & G_IO_OUT)
+ g_print ("o");
+ if (fd_array[i].revents & G_IO_PRI)
+ g_print ("p");
+ if (fd_array[i].revents & G_IO_ERR)
+ g_print ("e");
+ if (fd_array[i].revents & G_IO_HUP)
+ g_print ("h");
+ if (fd_array[i].revents & G_IO_NVAL)
+ g_print ("n");
+ g_print ("]");
+ }
+ i++;
+ }
+ pollrec = pollrec->next;
+ }
+ g_print ("\n");
+#endif
+ } /* if (npoll || timeout != 0) */
+
#ifdef G_THREADS_ENABLED
if (!poll_waiting)
{
-#ifndef NATIVE_WIN32
+#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)
{
- pollrec->fd->revents = fd_array[i].revents;
+ if (pollrec->fd->events)
+ {
+ pollrec->fd->revents = fd_array[i].revents;
+ i++;
+ }
pollrec = pollrec->next;
- i++;
}
g_free (fd_array);
newrec->next = pollrec;
n_poll_records++;
+
+#ifdef G_THREADS_ENABLED
+ poll_changed = TRUE;
+
+ /* Now wake up the main loop if it is waiting in the poll() */
+ g_main_wakeup ();
+#endif
}
void
else
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 = pollrec->next;
}
+#ifdef G_THREADS_ENABLED
+ poll_changed = TRUE;
+
+ /* Now wake up the main loop if it is waiting in the poll() */
+ g_main_wakeup ();
+#endif
+
G_UNLOCK (main_loop);
}
#endif
}
+/* Wake the main loop up from a poll() */
+static void
+g_main_wakeup (void)
+{
+#ifdef G_THREADS_ENABLED
+ if (poll_waiting)
+ {
+ poll_waiting = FALSE;
+#ifndef G_OS_WIN32
+ write (wake_up_pipe[1], "A", 1);
+#else
+ ReleaseSemaphore (wake_up_semaphore, 1, NULL);
+#endif
+ }
+#endif
+}
+
/* Timeouts */
-static gboolean
-g_timeout_prepare (gpointer source_data,
+static void
+g_timeout_set_expiration (GTimeoutData *data,
+ GTimeVal *current_time)
+{
+ guint seconds = data->interval / 1000;
+ guint msecs = data->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)
+ {
+ data->expiration.tv_usec -= 1000000;
+ data->expiration.tv_sec++;
+ }
+}
+
+static gboolean
+g_timeout_prepare (gpointer source_data,
GTimeVal *current_time,
- gint *timeout)
+ gint *timeout,
+ gpointer user_data)
{
glong msec;
GTimeoutData *data = source_data;
-
- msec = (data->expiration.tv_sec - current_time->tv_sec) * 1000 +
- (data->expiration.tv_usec - current_time->tv_usec) / 1000;
-
- *timeout = (msec <= 0) ? 0 : msec;
-
- return (msec <= 0);
+
+ msec = ((data->expiration.tv_sec - current_time->tv_sec) * 1000 +
+ (data->expiration.tv_usec - current_time->tv_usec) / 1000);
+
+ if (msec < 0)
+ msec = 0;
+ else if (msec > data->interval)
+ {
+ /* The system time has been set backwards, so we
+ * reset the expiration time to now + data->interval;
+ * this at least avoids hanging for long periods of time.
+ */
+ g_timeout_set_expiration (data, current_time);
+ msec = data->interval;
+ }
+
+ *timeout = msec;
+
+ return msec == 0;
}
static gboolean
-g_timeout_check (gpointer source_data,
- GTimeVal *current_time)
+g_timeout_check (gpointer source_data,
+ GTimeVal *current_time,
+ gpointer user_data)
{
GTimeoutData *data = source_data;
-
- 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 ((data->expiration.tv_sec < current_time->tv_sec) ||
+ ((data->expiration.tv_sec == current_time->tv_sec) &&
+ (data->expiration.tv_usec <= current_time->tv_usec)));
}
static gboolean
-g_timeout_dispatch (gpointer source_data,
- GTimeVal *current_time,
+g_timeout_dispatch (gpointer source_data,
+ GTimeVal *dispatch_time,
gpointer user_data)
{
GTimeoutData *data = source_data;
if (data->callback (user_data))
{
- guint seconds = data->interval / 1000;
- guint msecs = data->interval - seconds * 1000;
+ g_timeout_set_expiration (data, dispatch_time);
- 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)
- {
- data->expiration.tv_usec -= 1000000;
- data->expiration.tv_sec++;
- }
return TRUE;
}
else
return FALSE;
}
-guint
+guint
g_timeout_add_full (gint priority,
- guint interval,
+ guint interval,
GSourceFunc function,
gpointer data,
GDestroyNotify notify)
{
- guint seconds;
- guint msecs;
GTimeoutData *timeout_data = g_new (GTimeoutData, 1);
+ GTimeVal current_time;
timeout_data->interval = interval;
timeout_data->callback = function;
- g_get_current_time (&timeout_data->expiration);
-
- seconds = timeout_data->interval / 1000;
- msecs = timeout_data->interval - seconds * 1000;
+ g_get_current_time (¤t_time);
- timeout_data->expiration.tv_sec += seconds;
- timeout_data->expiration.tv_usec += msecs * 1000;
- if (timeout_data->expiration.tv_usec >= 1000000)
- {
- timeout_data->expiration.tv_usec -= 1000000;
- timeout_data->expiration.tv_sec++;
- }
+ g_timeout_set_expiration (timeout_data, ¤t_time);
return g_source_add (priority, FALSE, &timeout_funcs, timeout_data, data, notify);
}
/* Idle functions */
static gboolean
-g_idle_prepare (gpointer source_data,
+g_idle_prepare (gpointer source_data,
GTimeVal *current_time,
- gint *timeout)
+ gint *timeout,
+ gpointer user_data)
{
- timeout = 0;
+ *timeout = 0;
+
return TRUE;
}
static gboolean
g_idle_check (gpointer source_data,
- GTimeVal *current_time)
+ GTimeVal *current_time,
+ gpointer user_data)
{
return TRUE;
}
static gboolean
g_idle_dispatch (gpointer source_data,
- GTimeVal *current_time,
+ GTimeVal *dispatch_time,
gpointer user_data)
{
GSourceFunc func = source_data;