* Boston, MA 02111-1307, USA.
*/
+/*
+ * Modified by the GLib Team and others 1997-1999. 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/.
+ */
+
/*
* MT safe
*/
#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 _MSC_VER
-#include <fcntl.h>
-#include <io.h>
-#endif /* _MSC_VER */
+#ifdef G_OS_BEOS
+#include <net/socket.h>
+#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 */
/* The following lock is used for both the list of sources
* and the list of poll records
*/
-G_LOCK_DECLARE_STATIC (main_loop);
+G_LOCK_DEFINE_STATIC (main_loop);
static GSourceFuncs timeout_funcs =
{
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.
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
-#endif /* HAVE_SYS_SELECT_H_ */
+#endif /* HAVE_SYS_SELECT_H */
+
+#ifdef G_OS_BEOS
+#undef NO_FD_SET
+#endif /* G_OS_BEOS */
#ifndef NO_FD_SET
# define SELECT_MASK fd_set
return ready;
}
-#endif /* !NATIVE_WIN32 */
+#endif /* !G_OS_WIN32 */
static GPollFunc poll_func = g_poll;
#endif /* !HAVE_POLL */
return (source_a->priority < source_b->priority) ? -1 : 1;
}
-/* HOLDS: main_loop_lock */
+/* HOLDS: main_loop lock */
static void
g_source_destroy_func (GHookList *hook_list,
GHook *hook)
G_LOCK (main_loop);
}
-static void
-g_source_noop (GHookList *hook_list,
- GHook *hook)
-{
-}
-
guint
g_source_add (gint priority,
gboolean can_recurse,
G_LOCK (main_loop);
if (!source_list.is_setup)
- g_hook_list_init (&source_list, sizeof(GSource));
+ {
+ g_hook_list_init (&source_list, sizeof (GSource));
- source_list.hook_destroy = g_source_noop;
- source_list.hook_free = g_source_destroy_func;
+ source_list.hook_destroy = G_HOOK_DEFERRED_DESTROY;
+ source_list.hook_free = g_source_destroy_func;
+ }
- source = (GSource *)g_hook_alloc (&source_list);
+ source = (GSource*) g_hook_alloc (&source_list);
source->priority = priority;
source->source_data = source_data;
source->hook.func = funcs;
#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 _MSC_VER
+#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))
continue;
}
- in_check_or_prepare++;
- if (hook->flags & G_SOURCE_READY ||
- ((GSourceFuncs *) hook->func)->prepare (source->source_data,
- ¤t_time,
- &source_timeout))
+ if (!(hook->flags & G_SOURCE_READY))
{
+ gboolean (*prepare) (gpointer source_data,
+ GTimeVal *current_time,
+ 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, source->hook.data))
+ hook->flags |= G_SOURCE_READY;
+
+ G_LOCK (main_loop);
in_check_or_prepare--;
+ }
+
+ if (hook->flags & G_SOURCE_READY)
+ {
if (!dispatch)
{
- hook->flags |= G_SOURCE_READY;
g_hook_unref (&source_list, hook);
G_UNLOCK (main_loop);
}
else
{
- hook->flags |= G_SOURCE_READY;
n_ready++;
current_priority = source->priority;
timeout = 0;
}
}
- else
- in_check_or_prepare--;
if (source_timeout >= 0)
{
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;
continue;
}
- in_check_or_prepare++;
- if (hook->flags & G_SOURCE_READY ||
- ((GSourceFuncs *) hook->func)->check (source->source_data,
- ¤t_time))
+ if (!(hook->flags & G_SOURCE_READY))
{
+ gboolean (*check) (gpointer source_data,
+ 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, source->hook.data))
+ hook->flags |= G_SOURCE_READY;
+
+ G_LOCK (main_loop);
in_check_or_prepare--;
+ }
+
+ if (hook->flags & G_SOURCE_READY)
+ {
if (dispatch)
{
hook->flags &= ~G_SOURCE_READY;
return TRUE;
}
}
- else
- in_check_or_prepare--;
hook = g_hook_next_valid (&source_list, hook, TRUE);
}
-
+
/* Now invoke the callbacks */
if (pending_dispatches)
if (in_check_or_prepare)
{
g_warning ("g_main_iteration(): called recursively from within a source's check() or "
- "prepare() member, iteration not possible");
+ "prepare() member or from a second thread, iteration not possible");
return FALSE;
}
else
if (in_check_or_prepare)
{
g_warning ("g_main_run(): called recursively from within a source's check() or "
- "prepare() member, iteration not possible");
+ "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);
+ g_get_current_time (¤t_time);
- seconds = timeout_data->interval / 1000;
- msecs = timeout_data->interval - seconds * 1000;
-
- 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;