📄 gmain.c
字号:
/* GLIB - Library of useful routines for C programming * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * gmain.c: Main loop abstraction, timeouts, and idle functions * Copyright 1998 Owen Taylor * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. *//* * 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/. *//* * MT safe */#include "glibconfig.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>#ifdef HAVE_SYS_TIME_H#include <sys/time.h>#endif /* HAVE_SYS_TIME_H */#ifdef GLIB_HAVE_SYS_POLL_H# include <sys/poll.h># undef events /* AIX 4.1.5 & 4.3.2 define this for SVR3,4 compatibility */# undef revents /* AIX 4.1.5 & 4.3.2 define this for SVR3,4 compatibility */#endif /* GLIB_HAVE_SYS_POLL_H */#ifdef HAVE_UNISTD_H#include <unistd.h>#endif /* HAVE_UNISTD_H */#include <errno.h>#ifdef G_OS_WIN32#define STRICT#include <windows.h>#endif /* G_OS_WIN32 */#ifdef G_OS_BEOS#include <net/socket.h>#endif /* G_OS_BEOS *//* Types */typedef struct _GTimeoutSource GTimeoutSource;typedef struct _GPollRec GPollRec;typedef struct _GSourceCallback GSourceCallback;typedef enum{ G_SOURCE_READY = 1 << G_HOOK_FLAG_USER_SHIFT, G_SOURCE_CAN_RECURSE = 1 << (G_HOOK_FLAG_USER_SHIFT + 1)} GSourceFlags;#ifdef G_THREADS_ENABLEDtypedef struct _GMainWaiter GMainWaiter;struct _GMainWaiter{ GCond *cond; GMutex *mutex;};#endif struct _GMainContext{#ifdef G_THREADS_ENABLED /* The following lock is used for both the list of sources * and the list of poll records */ GStaticMutex mutex; GCond *cond; GThread *owner; guint owner_count; GSList *waiters;#endif guint ref_count; 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; guint 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; guint ref_count;};struct _GTimeoutSource{ GSource source; GTimeVal expiration; guint interval;};struct _GPollRec{ gint priority; GPollFD *fd; GPollRec *next;};#ifdef G_THREADS_ENABLED#define LOCK_CONTEXT(context) g_static_mutex_lock (&context->mutex)#define UNLOCK_CONTEXT(context) g_static_mutex_unlock (&context->mutex)#define G_THREAD_SELF g_thread_self ()#else#define LOCK_CONTEXT(context) (void)0#define UNLOCK_CONTEXT(context) (void)0#define G_THREAD_SELF NULL#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 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_unlocked (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 GSList *main_contexts_without_pipe = NULL;#if defined(G_PLATFORM_WIN32) && defined(__GNUC__)__declspec(dllexport)#endifGSourceFuncs g_timeout_funcs ={ g_timeout_prepare, g_timeout_check, g_timeout_dispatch, NULL};#if defined(G_PLATFORM_WIN32) && defined(__GNUC__)__declspec(dllexport)#endifGSourceFuncs g_idle_funcs ={ g_idle_prepare, g_idle_check, g_idle_dispatch, NULL};#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 */#else /* !HAVE_POLL */#ifdef G_OS_WIN32static gintg_poll (GPollFD *fds, guint nfds, gint timeout){ HANDLE handles[MAXIMUM_WAIT_OBJECTS]; gboolean poll_msgs = FALSE; GPollFD *f; DWORD ready; MSG msg; UINT timer; gint nhandles = 0; for (f = fds; f < &fds[nfds]; ++f) if (f->fd >= 0) { if (f->fd == G_WIN32_MSG_HANDLE) poll_msgs = TRUE; else {#ifdef G_MAIN_POLL_DEBUG g_print ("g_poll: waiting for %#x\n", f->fd);#endif handles[nhandles++] = (HANDLE) f->fd; } } if (timeout == -1) timeout = INFINITE; if (poll_msgs) { /* Waiting for messages, and maybe events * -> First PeekMessage */#ifdef G_MAIN_POLL_DEBUG g_print ("PeekMessage\n");#endif if (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) ready = WAIT_OBJECT_0 + nhandles; else { if (nhandles == 0) { /* Waiting just for messages */ if (timeout == INFINITE) { /* Infinite timeout * -> WaitMessage */#ifdef G_MAIN_POLL_DEBUG g_print ("WaitMessage\n");#endif if (!WaitMessage ()) g_warning (G_STRLOC ": WaitMessage() failed"); ready = WAIT_OBJECT_0 + nhandles; } else if (timeout == 0) { /* Waiting just for messages, zero timeout. * If we got here, there was no message */ ready = WAIT_TIMEOUT; } else { /* Waiting just for messages, some timeout * -> Set a timer, wait for message, * kill timer, use PeekMessage */ timer = SetTimer (NULL, 0, timeout, NULL); if (timer == 0) { g_warning (G_STRLOC ": SetTimer() failed"); ready = WAIT_TIMEOUT; } else {#ifdef G_MAIN_POLL_DEBUG g_print ("WaitMessage\n");#endif WaitMessage (); KillTimer (NULL, timer);#ifdef G_MAIN_POLL_DEBUG g_print ("PeekMessage\n");#endif if (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE) && msg.message != WM_TIMER) ready = WAIT_OBJECT_0; else ready = WAIT_TIMEOUT; } } } else { /* Wait for either message or event * -> Use MsgWaitForMultipleObjects */#ifdef G_MAIN_POLL_DEBUG g_print ("MsgWaitForMultipleObjects(%d, %d)\n", nhandles, timeout);#endif ready = MsgWaitForMultipleObjects (nhandles, handles, FALSE, timeout, QS_ALLINPUT); if (ready == WAIT_FAILED) g_warning (G_STRLOC ": MsgWaitForMultipleObjects() failed"); } } } else if (nhandles == 0) { /* Wait for nothing (huh?) */ return 0; } else { /* Wait for just events * -> Use WaitForMultipleObjects */#ifdef G_MAIN_POLL_DEBUG g_print ("WaitForMultipleObjects(%d, %d)\n", nhandles, timeout);#endif ready = WaitForMultipleObjects (nhandles, handles, FALSE, timeout); if (ready == WAIT_FAILED) g_warning (G_STRLOC ": WaitForMultipleObjects() failed"); }#ifdef G_MAIN_POLL_DEBUG g_print ("wait returns %ld%s\n", ready, (ready == WAIT_FAILED ? " (WAIT_FAILED)" : (ready == WAIT_TIMEOUT ? " (WAIT_TIMEOUT)" : (poll_msgs && ready == WAIT_OBJECT_0 + nhandles ? " (msg)" : ""))));#endif for (f = fds; f < &fds[nfds]; ++f) f->revents = 0; if (ready == WAIT_FAILED) return -1; else if (ready == WAIT_TIMEOUT) return 0; else if (poll_msgs && ready == WAIT_OBJECT_0 + nhandles) { for (f = fds; f < &fds[nfds]; ++f) if (f->fd >= 0) { if (f->events & G_IO_IN) if (f->fd == G_WIN32_MSG_HANDLE) f->revents |= G_IO_IN; } }#if 1 /* TEST_WITHOUT_THIS */ else if (ready >= WAIT_OBJECT_0 && ready < WAIT_OBJECT_0 + nhandles) for (f = fds; f < &fds[nfds]; ++f) { if ((f->events & (G_IO_IN | G_IO_OUT)) && f->fd == (gint) handles[ready - WAIT_OBJECT_0]) { if (f->events & G_IO_IN) f->revents |= G_IO_IN; else f->revents |= G_IO_OUT;#ifdef G_MAIN_POLL_DEBUG g_print ("g_poll: got event %#x\n", f->fd);#endif#if 0 ResetEvent ((HANDLE) f->fd);#endif } }#endif return 1;}#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 <string.h> /* for bzero on BSD systems */#ifdef HAVE_SYS_SELECT_H#include <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#else /* !NO_FD_SET */# ifndef _AIXtypedef long fd_mask;# endif /* _AIX */# ifdef _IBMR2# define SELECT_MASK void# else /* !_IBMR2 */# define SELECT_MASK int# endif /* !_IBMR2 */#endif /* !NO_FD_SET */static gint g_poll (GPollFD *fds, guint nfds, gint timeout){ struct timeval tv; SELECT_MASK rset, wset, xset; GPollFD *f; int ready; int maxfd = 0; FD_ZERO (&rset); FD_ZERO (&wset); FD_ZERO (&xset); for (f = fds; f < &fds[nfds]; ++f) if (f->fd >= 0) { if (f->events & G_IO_IN) FD_SET (f->fd, &rset); if (f->events & G_IO_OUT) FD_SET (f->fd, &wset); if (f->events & G_IO_PRI) FD_SET (f->fd, &xset); if (f->fd > maxfd && (f->events & (G_IO_IN|G_IO_OUT|G_IO_PRI))) maxfd = f->fd; } tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000; ready = select (maxfd + 1, &rset, &wset, &xset, timeout == -1 ? NULL : &tv); if (ready > 0) for (f = fds; f < &fds[nfds]; ++f) { f->revents = 0; if (f->fd >= 0) { if (FD_ISSET (f->fd, &rset)) f->revents |= G_IO_IN; if (FD_ISSET (f->fd, &wset)) f->revents |= G_IO_OUT; if (FD_ISSET (f->fd, &xset)) f->revents |= G_IO_PRI; } } return ready;}#endif /* !G_OS_WIN32 */#endif /* !HAVE_POLL *//** * g_main_context_ref: * @context: a #GMainContext * * Increases the reference count on a #GMainContext object by one. **/voidg_main_context_ref (GMainContext *context){ g_return_if_fail (context != NULL); g_return_if_fail (context->ref_count > 0); LOCK_CONTEXT (context); context->ref_count++; UNLOCK_CONTEXT (context);}static voidg_main_context_unref_and_unlock (GMainContext *context){ GSource *source; context->ref_count--; if (context->ref_count != 0) { UNLOCK_CONTEXT (context); return; } source = context->source_list; while (source) { GSource *next = source->next; g_source_destroy_internal (source, context, TRUE); source = next; } UNLOCK_CONTEXT (context);#ifdef G_THREADS_ENABLED
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -