summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuo Jinghua <sunmoon1997@gmail.com>2009-11-01 18:09:34 +0800
committerLuo Jinghua <sunmoon1997@gmail.com>2009-11-01 18:09:34 +0800
commit751f11b166aa6819f5e1a4307ab91bfcb669c84d (patch)
tree60dccbd855c8790c5cb39c0e0d208159a14b0a93
parent897d49d92094eddb24702aaa4f8c3e1f760f7fc6 (diff)
milkway: add mw-main-context, steal it from glib again
-rw-r--r--milkway/Makefile.am7
-rw-r--r--milkway/mw-main-context-private.h50
-rw-r--r--milkway/mw-main-context.c1427
-rw-r--r--milkway/mw-main-context.h125
-rw-r--r--milkway/mw-source.c120
-rw-r--r--milkway/mw-source.h87
6 files changed, 1808 insertions, 8 deletions
diff --git a/milkway/Makefile.am b/milkway/Makefile.am
index 6130ad4..ce75f78 100644
--- a/milkway/Makefile.am
+++ b/milkway/Makefile.am
@@ -30,7 +30,8 @@ milkway_headers = \
mw-slist.h \
mw-source.h \
mw-poll.h \
- mw-poll-imp.h
+ mw-poll-imp.h \
+ mw-main-context.h
libmilkway_la_LDFLAGS = -version-info $(LT_VERSION_INFO) -no-undefined
libmilkway_la_LIBADD = @DEP_LIBS@ $(PTHREAD_LIBS)
@@ -75,7 +76,9 @@ libmilkway_la_SOURCES = \
mw-poll-select.c \
mw-poll-win32-private.h \
mw-poll-win32.c \
- mw-poll-imp.c
+ mw-poll-imp.c \
+ mw-main-context-private.h \
+ mw-main-context.c
milkwayincludedir = $(includedir)/milkway/milkway
milkwayinclude_HEADERS = $(milkway_headers)
diff --git a/milkway/mw-main-context-private.h b/milkway/mw-main-context-private.h
new file mode 100644
index 0000000..ca02f4a
--- /dev/null
+++ b/milkway/mw-main-context-private.h
@@ -0,0 +1,50 @@
+/* Milkway
+ *
+ * Copyright (C) 2008- Luo Jinghua <sunmoon1997@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * 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
+ * Library General Public License for more details.
+ *
+ * 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.
+ */
+#ifndef MW_MAIN_CONTEXT_PRIVATE_H
+#define MW_MAIN_CONTEXT_PRIVATE_H
+
+mw_public void
+mw_main_context_lock(mw_main_context_t *self);
+
+mw_public void
+mw_main_context_unlock(mw_main_context_t *self);
+
+mw_private void
+mw_main_context_add_poll_unlocked (mw_main_context_t *context,
+ int priority,
+ mw_pollfd_t *fd);
+
+mw_private void
+mw_main_context_remove_poll_unlocked (mw_main_context_t *context,
+ mw_pollfd_t *fd);
+
+mw_private void
+mw_main_context_wakeup_unlocked (mw_main_context_t *context);
+
+mw_private void
+mw_main_context_remove_source (mw_main_context_t *context,
+ mw_source_t *source);
+
+mw_private void
+mw_main_context_set_source_priority (mw_main_context_t *context,
+ mw_source_t *source,
+ int priority);
+
+#endif
diff --git a/milkway/mw-main-context.c b/milkway/mw-main-context.c
new file mode 100644
index 0000000..d0c31bb
--- /dev/null
+++ b/milkway/mw-main-context.c
@@ -0,0 +1,1427 @@
+/* Milkway
+ *
+ * Copyright (C) 2008- Luo Jinghua <sunmoon1997@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * 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
+ * Library General Public License for more details.
+ *
+ * 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.
+ */
+/* 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 "milkwayint.h"
+#include "milkway/mw-poll-imp.h"
+#include "milkway/mw-main-context.h"
+#include "milkway/mw-main-context-private.h"
+#include "milkway/mw-ptr-array.h"
+
+/* Uncomment the next line (and the corresponding line in gpoll.c) to
+ * enable debugging printouts if the environment variable
+ * MW_MAIN_POLL_DEBUG is set to some value.
+ */
+/* #define MW_MAIN_POLL_DEBUG */
+
+#ifdef _WIN32
+/* Always enable debugging printout on Windows, as it is more often
+ * needed there...
+ */
+#define MW_MAIN_POLL_DEBUG
+#endif
+
+#include <sys/types.h>
+#include <time.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif /* HAVE_SYS_TIME_H */
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+#include <errno.h>
+
+#ifdef MW_OS_WINDOWS
+#define STRICT
+#include <windows.h>
+#endif /* MW_OS_WIN32 */
+
+#ifndef MW_OS_WINDOWs
+#include <fcntl.h>
+#include <sys/wait.h>
+#endif
+
+#ifndef MIN
+#define MIN(a, b) (a) > (b) ? (b) : (a)
+#endif
+
+typedef struct _mw_poll_rec mw_poll_rec_t;
+
+struct _mw_main_context_type {
+ mw_object_type_t base;
+};
+
+typedef struct _mw_main_waiter mw_main_waiter_t;
+struct _mw_main_waiter
+{
+ mw_cond_t *cond;
+ mw_mutex_t *mutex;
+};
+
+
+#ifdef MW_MAIN_POLL_DEBUG
+mw_bool_t _mw_main_poll_debug = MW_FALSE;
+#endif
+
+struct _mw_main_context {
+ mw_object_t base;
+
+ /* The following lock is used for both the list of sources
+ * and the list of poll records
+ */
+ mw_static_mutex_t mutex;
+ mw_cond_t *cond;
+ mw_thread_t *owner;
+ mw_uint_t owner_count;
+ mw_slist_t *waiters;
+
+ mw_ptr_array_t *pending_dispatches;
+ int timeout; /* Timeout for current iteration */
+
+ mw_uint_t next_id;
+ mw_source_t *source_list;
+ int in_check_or_prepare;
+
+ mw_poll_rec_t *poll_records;
+ mw_uint_t n_poll_records;
+ mw_pollfd_t *cached_poll_array;
+ mw_uint_t cached_poll_array_size;
+
+#ifndef MW_OS_WINDOWS
+ /* this pipe is used to wake up the main loop when a source is added.
+ */
+ int wake_up_pipe[2];
+#else /* MW_OS_WIN32 */
+ HANDLE wake_up_semaphore;
+#endif /* MW_OS_WIN32 */
+
+ mw_pollfd_t wake_up_rec;
+ mw_bool_t poll_waiting;
+
+ /* Flag indicating whether the set of fd's changed during a poll */
+ mw_bool_t poll_changed;
+
+ mw_timeval_t current_time;
+ mw_bool_t time_is_current;
+
+ mw_poll_t *poll;
+};
+
+struct _mw_poll_rec
+{
+ mw_pollfd_t *fd;
+ mw_poll_rec_t *next;
+ int prio;
+};
+
+#define LOCK_CONTEXT(context) mw_static_mutex_lock (&context->mutex)
+#define UNLOCK_CONTEXT(context) mw_static_mutex_unlock (&context->mutex)
+#define MW_THREAD_SELF mw_thread_self ()
+
+/* Forward declarations */
+static void mw_main_context_poll (mw_main_context_t *context,
+ int timeout,
+ int priority,
+ mw_pollfd_t *fds,
+ int n_fds);
+
+static mw_inline void
+poll_rec_list_free (mw_main_context_t *context,
+ mw_poll_rec_t *list)
+{
+ mw_poll_rec_t *last = list;
+ mw_poll_rec_t *next;
+
+ if (!list)
+ return;
+
+ for (; last; last = next) {
+ next = last->next;
+ free (last);
+ }
+}
+
+static void
+mw_main_context_finalize (mw_object_t *super)
+{
+ mw_main_context_t *context = (mw_main_context_t*)super;
+ mw_source_t *source;
+
+ source = context->source_list;
+ while (source) {
+ mw_source_t *next = source->next;
+ mw_source_destroy (source);
+ source = next;
+ }
+
+ mw_static_mutex_free (&context->mutex);
+
+ mw_object_unref (context->pending_dispatches);
+ free (context->cached_poll_array);
+
+ poll_rec_list_free (context, context->poll_records);
+
+#ifndef MW_OS_WINDOWS
+ close (context->wake_up_pipe[0]);
+ close (context->wake_up_pipe[1]);
+#else
+ CloseHandle (context->wake_up_semaphore);
+#endif
+
+ if (context->cond != NULL)
+ mw_cond_free (context->cond);
+
+ MW_SUPER_FINALIZE(super, MW_MAIN_CONTEXT_TYPE)(super);
+}
+
+static void
+mw_main_context_init_pipe (mw_main_context_t *context)
+{
+# ifndef MW_OS_WINDOWS
+ if (context->wake_up_pipe[0] != -1)
+ return;
+
+ if (pipe (context->wake_up_pipe) < 0)
+ fprintf (stderr, "Cannot create pipe main loop wake-up: %s\n",
+ strerror (errno));
+
+#ifdef FD_CLOEXEC
+ fcntl (context->wake_up_pipe[0], F_SETFD, FD_CLOEXEC);
+ fcntl (context->wake_up_pipe[1], F_SETFD, FD_CLOEXEC);
+#endif
+
+ context->wake_up_rec.fd = context->wake_up_pipe[0];
+ context->wake_up_rec.events = MW_IO_IN;
+# else
+ if (context->wake_up_semaphore != NULL)
+ return;
+ context->wake_up_semaphore = CreateSemaphore (NULL, 0, 100, NULL);
+ if (context->wake_up_semaphore == NULL)
+ fprintf (stderr, "Cannot create wake-up semaphore\n");
+ context->wake_up_rec.fd = (intptr) context->wake_up_semaphore;
+ context->wake_up_rec.events = MW_IO_IN;
+
+ if (_mw_main_poll_debug)
+ printf ("wake-up semaphore: %p\n", context->wake_up_semaphore);
+# endif
+ mw_main_context_add_poll_unlocked (context, 0, &context->wake_up_rec);
+}
+
+static mw_main_context_t *
+mw_main_context_init (mw_main_context_t *context)
+{
+ if (!mw_object_init(&context->base))
+ return NULL;
+
+ mw_static_mutex_init (&context->mutex);
+
+ context->owner = NULL;
+ context->waiters = NULL;
+
+# ifndef MW_OS_WINDOWS
+ context->wake_up_pipe[0] = -1;
+ context->wake_up_pipe[1] = -1;
+# else
+ context->wake_up_semaphore = NULL;
+# endif
+
+ context->next_id = 1;
+
+ context->source_list = NULL;
+
+ context->cached_poll_array = NULL;
+ context->cached_poll_array_size = 0;
+
+ context->pending_dispatches = mw_ptr_array_new ();
+
+ context->time_is_current = MW_FALSE;
+
+ mw_main_context_init_pipe (context);
+
+ return context;
+}
+
+/**
+ * mw_main_context_new:
+ *
+ * Creates a new #mw_main_context_t structure.
+ *
+ * Return value: the new #mw_main_context_t
+ **/
+mw_main_context_t *
+mw_main_context_new (void)
+{
+ mw_main_context_t *context;
+
+ context = (mw_main_context_t*)mw_object_alloc(MW_MAIN_CONTEXT_TYPE);
+ if (!context)
+ return NULL;
+
+ return mw_main_context_init(context);
+}
+
+void
+mw_main_context_lock(mw_main_context_t *self)
+{
+ LOCK_CONTEXT(self);
+}
+
+mw_public void
+mw_main_context_unlock(mw_main_context_t *self)
+{
+ UNLOCK_CONTEXT(self);
+}
+
+/* Holds context's lock
+ */
+static void
+mw_source_list_add (mw_source_t *source,
+ mw_main_context_t *context)
+{
+ mw_source_t *tmp_source, *last_source;
+
+ last_source = NULL;
+ tmp_source = context->source_list;
+ while (tmp_source && tmp_source->prio <= source->prio) {
+ last_source = tmp_source;
+ tmp_source = tmp_source->next;
+ }
+
+ 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;
+}
+
+/* Holds context's lock
+ */
+static void
+mw_source_list_remove (mw_source_t *source,
+ mw_main_context_t *context)
+{
+ if (source->prev)
+ source->prev->next = source->next;
+ else
+ context->source_list = source->next;
+
+ if (source->next)
+ source->next->prev = source->prev;
+
+ source->prev = NULL;
+ source->next = NULL;
+}
+
+/**
+ * mw_source_add_source:
+ * @context: a #mw_main_context_t (if %NULL, the default context will be used)
+ * @source: a #mw_source_t
+ *
+ * Adds a #mw_source_t to a @context so that it will be executed within
+ * that context. Remove it by calling mw_source_destroy().
+ *
+ * Return value: the ID (greater than 0) for the source within the
+ * #mw_main_context_t.
+ **/
+mw_uint_t
+mw_main_context_add_source (mw_main_context_t *context,
+ mw_source_t *source)
+{
+ mw_uint_t result = 0;
+ mw_slist_t *tmp_list;
+
+
+ LOCK_CONTEXT (context);
+
+ source->context = context;
+ result = source->id = context->next_id++;
+
+ mw_object_ref (source);
+ mw_source_list_add (source, context);
+
+ tmp_list = source->fds;
+ while (tmp_list) {
+ mw_main_context_add_poll_unlocked (context, source->prio, tmp_list->data);
+ tmp_list = tmp_list->next;
+ }
+
+ /* Now wake up the main loop if it is waiting in the poll() */
+ mw_main_context_wakeup_unlocked (context);
+
+ UNLOCK_CONTEXT (context);
+
+ return result;
+}
+
+void
+mw_main_context_set_source_priority (mw_main_context_t *context,
+ mw_source_t *source,
+ int priority)
+{
+ mw_slist_t *tmp_list;
+
+ mw_assert (context && context == source->context);
+
+ LOCK_CONTEXT (context);
+
+ /* Remove the source from the context's source and then
+ * add it back so it is sorted in the correct plcae
+ */
+ mw_source_list_remove (source, source->context);
+ mw_source_list_add (source, source->context);
+
+ if (!mw_source_is_blocked (source)) {
+ tmp_list = source->fds;
+ while (tmp_list) {
+ mw_main_context_remove_poll_unlocked (context, tmp_list->data);
+ mw_main_context_add_poll_unlocked (context, priority, tmp_list->data);
+
+ tmp_list = tmp_list->next;
+ }
+ }
+
+ UNLOCK_CONTEXT (context);
+}
+
+/* HOLDS: context's lock */
+void
+mw_main_context_remove_source (mw_main_context_t *context,
+ mw_source_t *source)
+{
+
+
+ if (!mw_source_is_blocked (source)) {
+ mw_slist_t *tmp_list;
+
+ tmp_list = source->fds;
+ while (tmp_list) {
+ mw_main_context_remove_poll_unlocked (context, tmp_list->data);
+ tmp_list = tmp_list->next;
+ }
+ }
+
+ mw_source_list_remove (source, context);
+}
+
+/* Temporarily remove all this source's file descriptors from the
+ * poll(), so that if data comes available for one of the file descriptors
+ * we don't continually spin in the poll()
+ */
+/* HOLDS: source->context's lock */
+static void
+block_source (mw_source_t *source)
+{
+ mw_slist_t *tmp_list;
+
+ mw_assert (!mw_source_is_blocked (source));
+
+ tmp_list = source->fds;
+ while (tmp_list) {
+ mw_main_context_remove_poll_unlocked (source->context, tmp_list->data);
+ tmp_list = tmp_list->next;
+ }
+}
+
+/* HOLDS: source->context's lock */
+static void
+unblock_source (mw_source_t *source)
+{
+ mw_slist_t *tmp_list;
+
+ mw_assert (!mw_source_is_blocked (source)); /* Source already unblocked */
+ mw_assert (mw_source_is_active (source));
+
+ tmp_list = source->fds;
+ while (tmp_list) {
+ mw_main_context_add_poll_unlocked (source->context, source->prio, tmp_list->data);
+ tmp_list = tmp_list->next;
+ }
+}
+
+/* HOLDS: context's lock */
+static void
+mw_main_dispatch (mw_main_context_t *context)
+{
+ mw_uint_t i;
+
+ for (i = 0; i < context->pending_dispatches->len; i++) {
+ mw_source_t *source = context->pending_dispatches->pdata[i];
+
+ context->pending_dispatches->pdata[i] = NULL;
+ mw_assert (source);
+
+ source->flags &= ~MW_SOURCE_READY;
+
+ if (mw_source_is_active (source)) {
+ mw_bool_t was_in_call;
+ mw_bool_t need_destroy;
+
+ mw_object_ref(source);
+
+ if ((source->flags & MW_SOURCE_CAN_RECURSE) == 0)
+ block_source (source);
+
+ was_in_call = source->flags & MW_SOURCE_DISPATCHING;
+ source->flags |= MW_SOURCE_DISPATCHING;
+
+ UNLOCK_CONTEXT (context);
+
+ need_destroy = !mw_source_dispatch(source);
+
+ mw_object_unref(source);
+
+ LOCK_CONTEXT (context);
+
+ if (!was_in_call)
+ source->flags &= ~MW_SOURCE_DISPATCHING;
+
+ if ((source->flags & MW_SOURCE_CAN_RECURSE) == 0 &&
+ mw_source_is_active (source))
+ unblock_source (source);
+
+ /* Note: this depends on the fact that we can't switch
+ * sources from one main context to another
+ */
+ if (need_destroy && mw_source_is_active (source)) {
+ mw_assert (source->context == context);
+ UNLOCK_CONTEXT(context);
+ mw_source_destroy (source);
+ LOCK_CONTEXT(context);
+ }
+ }
+
+ mw_object_unref(source);
+ }
+
+ mw_ptr_array_set_size (context->pending_dispatches, 0);
+}
+
+/* Holds context's lock */
+static inline mw_source_t *
+next_valid_source (mw_main_context_t *context,
+ mw_source_t *source)
+{
+ mw_source_t *new_source = source ? source->next : context->source_list;
+
+ while (new_source) {
+ if (!mw_source_is_active (new_source)) {
+ mw_object_ref (new_source);
+ break;
+ }
+
+ new_source = new_source->next;
+ }
+
+ if (source)
+ mw_object_unref (source);
+
+ return new_source;
+}
+
+static mw_inline mw_bool_t
+mw_main_context_is_internal_mutex(mw_main_context_t *context,
+ mw_mutex_t *mutex)
+{
+ return mutex == mw_static_mutex_get_mutex (&context->mutex);
+}
+
+/**
+ * mw_main_context_acquire:
+ * @context: a #mw_main_context_t
+ *
+ * Tries to become the owner of the specified context.
+ * If some other thread is the owner of the context,
+ * returns %MW_FALSE immediately. Ownership is properly
+ * recursive: the owner can require ownership again
+ * and will release ownership when mw_main_context_release()
+ * is called as many times as mw_main_context_acquire().
+ *
+ * You must be the owner of a context before you
+ * can call mw_main_context_prepare(), mw_main_context_query(),
+ * mw_main_context_check(), mw_main_context_dispatch().
+ *
+ * Return value: %MW_TRUE if the operation succeeded, and
+ * this thread is now the owner of @context.
+ **/
+mw_bool_t
+mw_main_context_acquire (mw_main_context_t *context)
+{
+ mw_bool_t result = MW_FALSE;
+ mw_thread_t *self = MW_THREAD_SELF;
+
+ mw_assert (context != NULL);
+
+ LOCK_CONTEXT (context);
+
+ if (!context->owner) {
+ context->owner = self;
+ mw_assert (context->owner_count == 0);
+ }
+
+ if (context->owner == self) {
+ context->owner_count++;
+ result = MW_TRUE;
+ }
+
+ UNLOCK_CONTEXT (context);
+
+ return result;
+}
+
+/**
+ * mw_main_context_release:
+ * @context: a #mw_main_context_t
+ *
+ * Releases ownership of a context previously acquired by this thread
+ * with mw_main_context_acquire(). If the context was acquired multiple
+ * times, the ownership will be released only when mw_main_context_release()
+ * is called as many times as it was acquired.
+ **/
+void
+mw_main_context_release (mw_main_context_t *context)
+{
+ mw_assert (context != NULL);
+
+ LOCK_CONTEXT (context);
+
+ context->owner_count--;
+ if (context->owner_count == 0) {
+ context->owner = NULL;
+
+ if (context->waiters) {
+ mw_main_waiter_t *waiter = context->waiters->data;
+ mw_bool_t loop_internal_waiter =
+ mw_main_context_is_internal_mutex(context, waiter->mutex);
+ context->waiters = mw_slist_delete_link (context->waiters,
+ context->waiters);
+ if (!loop_internal_waiter)
+ mw_mutex_lock (waiter->mutex);
+
+ mw_cond_signal (waiter->cond);
+
+ if (!loop_internal_waiter)
+ mw_mutex_unlock (waiter->mutex);
+ }
+ }
+
+ UNLOCK_CONTEXT (context);
+}
+
+/**
+ * mw_main_context_wait:
+ * @context: a #mw_main_context_t
+ * @cond: a condition variable
+ * @mutex: a mutex, currently held
+ *
+ * Tries to become the owner of the specified context,
+ * as with mw_main_context_acquire(). But if another thread
+ * is the owner, atomically drop @mutex and wait on @cond until
+ * that owner releases ownership or until @cond is signaled, then
+ * try again (once) to become the owner.
+ *
+ * Return value: %MW_TRUE if the operation succeeded, and
+ * this thread is now the owner of @context.
+ **/
+mw_bool_t
+mw_main_context_wait (mw_main_context_t *context,
+ mw_cond_t *cond,
+ mw_mutex_t *mutex)
+{
+ mw_bool_t result = MW_FALSE;
+ mw_thread_t *self = MW_THREAD_SELF;
+ mw_bool_t loop_internal_waiter;
+
+ mw_assert (context != NULL);
+
+ loop_internal_waiter = mw_main_context_is_internal_mutex(context, mutex);
+
+ if (!loop_internal_waiter)
+ LOCK_CONTEXT (context);
+
+ if (context->owner && context->owner != self) {
+ mw_main_waiter_t waiter;
+
+ waiter.cond = cond;
+ waiter.mutex = mutex;
+
+ context->waiters = mw_slist_append (context->waiters, &waiter);
+
+ if (!loop_internal_waiter)
+ UNLOCK_CONTEXT (context);
+ mw_cond_wait (cond, mutex);
+ if (!loop_internal_waiter)
+ LOCK_CONTEXT (context);
+
+ context->waiters = mw_slist_remove (context->waiters, &waiter);
+ }
+
+ if (!context->owner) {
+ context->owner = self;
+ mw_assert (context->owner_count == 0);
+ }
+
+ if (context->owner == self) {
+ context->owner_count++;
+ result = MW_TRUE;
+ }
+
+ if (!loop_internal_waiter)
+ UNLOCK_CONTEXT (context);
+
+ return result;
+}
+
+/**
+ * mw_main_context_prepare:
+ * @context: a #mw_main_context_t
+ * @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 mw_main_context_query ().
+ *
+ * Return value: %MW_TRUE if some source is ready to be dispatched
+ * prior to polling.
+ **/
+mw_bool_t
+mw_main_context_prepare (mw_main_context_t *context,
+ int *priority)
+{
+ mw_uint_t i;
+ int n_ready = 0;
+ int current_priority = INT_MAX;
+ mw_source_t *source;
+
+ mw_assert (context != NULL);
+
+ LOCK_CONTEXT (context);
+
+ context->time_is_current = MW_FALSE;
+
+ if (context->in_check_or_prepare) {
+ fprintf (stderr, "mw_main_context_prepare() called recursively from within a source's check() or "
+ "prepare() member.");
+ UNLOCK_CONTEXT (context);
+ return MW_FALSE;
+ }
+
+ if (context->poll_waiting) {
+ fprintf(stderr, "mw_main_context_prepare(): main loop already active in another thread");
+ UNLOCK_CONTEXT (context);
+ return MW_FALSE;
+ }
+
+ context->poll_waiting = MW_TRUE;
+
+ for (i = 0; i < context->pending_dispatches->len; i++) {
+ if (context->pending_dispatches->pdata[i])
+ mw_object_unref ((mw_source_t *)context->pending_dispatches->pdata[i]);
+ }
+ mw_ptr_array_set_size (context->pending_dispatches, 0);
+
+ /* Prepare all sources */
+ context->timeout = -1;
+
+ source = next_valid_source (context, NULL);
+ while (source) {
+ int source_timeout = -1;
+
+ if ((n_ready > 0) && (source->prio > current_priority)) {
+ mw_object_unref (source);
+ break;
+ }
+ if (mw_source_is_blocked (source))
+ goto next;
+
+ if (!mw_source_is_ready(source)) {
+ mw_bool_t result;
+
+ context->in_check_or_prepare++;
+ UNLOCK_CONTEXT (context);
+
+ result = mw_source_prepare (source, &source_timeout);
+
+ LOCK_CONTEXT (context);
+ context->in_check_or_prepare--;
+
+ if (result)
+ mw_source_set_ready (source);
+ }
+
+ if (mw_source_is_ready (source)) {
+ n_ready++;
+ current_priority = source->prio;
+ context->timeout = 0;
+ }
+
+ if (source_timeout >= 0) {
+ if (context->timeout < 0)
+ context->timeout = source_timeout;
+ else
+ context->timeout = MIN (context->timeout, source_timeout);
+ }
+
+ next:
+ source = next_valid_source (context, source);
+ }
+
+ UNLOCK_CONTEXT (context);
+
+ if (priority)
+ *priority = current_priority;
+
+ return n_ready > 0;
+}
+
+/**
+ * mw_main_context_query:
+ * @context: a #mw_main_context_t
+ * @max_priority: maximum priority source to check
+ * @timeout_: location to store timeout to be used in polling
+ * @fds: location to store #mw_pollfd_t records that need to be polled.
+ * @n_fds: length of @fds.
+ *
+ * Determines information necessary to poll this main loop.
+ *
+ * Return value: the number of records actually stored in @fds,
+ * or, if more than @n_fds records need to be stored, the number
+ * of records that need to be stored.
+ **/
+int
+mw_main_context_query (mw_main_context_t *context,
+ int max_priority,
+ int *timeout,
+ mw_pollfd_t *fds,
+ int n_fds)
+{
+ int n_poll;
+ mw_poll_rec_t *pollrec;
+
+ mw_assert (context != NULL);
+
+ LOCK_CONTEXT (context);
+
+ pollrec = context->poll_records;
+ n_poll = 0;
+ while (pollrec && max_priority >= pollrec->prio) {
+ /* We need to include entries with fd->events == 0 in the array because
+ * otherwise if the application changes fd->events behind our back and
+ * makes it non-zero, we'll be out of sync when we check the fds[] array.
+ * (Changing fd->events after adding an FD wasn't an anticipated use of
+ * this API, but it occurs in practice.) */
+ 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 & ~(MW_IO_ERR | MW_IO_HUP | MW_IO_NVAL);
+ fds[n_poll].revents = 0;
+ }
+
+ pollrec = pollrec->next;
+ n_poll++;
+ }
+
+ context->poll_changed = MW_FALSE;
+ if (timeout) {
+ *timeout = context->timeout;
+ if (*timeout != 0)
+ context->time_is_current = MW_FALSE;
+ }
+
+ UNLOCK_CONTEXT (context);
+
+ return n_poll;
+}
+
+/**
+ * mw_main_context_check:
+ * @context: a #mw_main_context_t
+ * @max_priority: the maximum numerical priority of sources to check
+ * @fds: array of #mw_pollfd_t's that was passed to the last call to
+ * mw_main_context_query()
+ * @n_fds: return value of mw_main_context_query()
+ *
+ * Passes the results of polling back to the main loop.
+ *
+ * Return value: %MW_TRUE if some sources are ready to be dispatched.
+ **/
+mw_bool_t
+mw_main_context_check (mw_main_context_t *context,
+ int max_priority,
+ mw_pollfd_t *fds,
+ int n_fds)
+{
+ mw_source_t *source;
+ mw_poll_rec_t *pollrec;
+ int n_ready = 0;
+ int i;
+
+ LOCK_CONTEXT (context);
+
+ if (context->in_check_or_prepare) {
+ fprintf (stderr, "mw_main_context_check() called recursively from within a source's check() or "
+ "prepare() member.");
+ UNLOCK_CONTEXT (context);
+ return MW_FALSE;
+ }
+
+ if (!context->poll_waiting) {
+#ifndef MW_OS_WIN32
+ char a;
+ read (context->wake_up_pipe[0], &a, 1);
+#endif
+ } else {
+ context->poll_waiting = MW_FALSE;
+ }
+
+ /* If the set of poll file descriptors changed, bail out
+ * and let the main loop rerun
+ */
+ if (context->poll_changed) {
+ UNLOCK_CONTEXT (context);
+ return MW_FALSE;
+ }
+
+ pollrec = context->poll_records;
+ i = 0;
+ while (i < n_fds) {
+ if (pollrec->fd->events)
+ pollrec->fd->revents = fds[i].revents;
+
+ pollrec = pollrec->next;
+ i++;
+ }
+
+ source = next_valid_source (context, NULL);
+ while (source) {
+ if ((n_ready > 0) && (source->prio > max_priority)) {
+ mw_object_unref (source);
+ break;
+ }
+ if (mw_source_is_blocked (source))
+ goto next;
+
+ if (!mw_source_is_ready (source)) {
+ mw_bool_t result;
+
+ context->in_check_or_prepare++;
+ UNLOCK_CONTEXT (context);
+
+ result = mw_source_check (source);
+
+ LOCK_CONTEXT (context);
+ context->in_check_or_prepare--;
+
+ if (result)
+ mw_source_set_ready (source);
+ }
+
+ if (mw_source_is_ready (source)) {
+ mw_ptr_array_add (context->pending_dispatches, mw_object_ref(source));
+
+ n_ready++;
+
+ /* never dispatch sources with less priority than the first
+ * one we choose to dispatch
+ */
+ max_priority = source->prio;
+ }
+
+ next:
+ source = next_valid_source (context, source);
+ }
+
+ UNLOCK_CONTEXT (context);
+
+ return n_ready > 0;
+}
+
+/**
+ * mw_main_context_dispatch:
+ * @context: a #mw_main_context_t
+ *
+ * Dispatches all pending sources.
+ **/
+void
+mw_main_context_dispatch (mw_main_context_t *context)
+{
+ LOCK_CONTEXT (context);
+
+ if (context->pending_dispatches->len > 0)
+ mw_main_dispatch (context);
+
+ UNLOCK_CONTEXT (context);
+}
+
+/* HOLDS context lock */
+static mw_bool_t
+mw_main_context_iterate (mw_main_context_t *context,
+ mw_bool_t block,
+ mw_bool_t dispatch,
+ mw_thread_t *self)
+{
+ int max_priority;
+ int timeout;
+ mw_bool_t some_ready;
+ int nfds, allocated_nfds;
+ mw_pollfd_t *fds = NULL;
+
+ UNLOCK_CONTEXT (context);
+
+ if (!mw_main_context_acquire (context)) {
+ mw_bool_t got_ownership;
+
+ LOCK_CONTEXT (context);
+
+ if (!block)
+ return MW_FALSE;
+
+ if (!context->cond)
+ context->cond = mw_cond_new ();
+
+ got_ownership = mw_main_context_wait (context,
+ context->cond,
+ mw_static_mutex_get_mutex (&context->mutex));
+
+ if (!got_ownership)
+ return MW_FALSE;
+ } else {
+ LOCK_CONTEXT (context);
+ }
+
+ if (!context->cached_poll_array) {
+ context->cached_poll_array_size = context->n_poll_records;
+ context->cached_poll_array = calloc (sizeof (mw_pollfd_t), context->n_poll_records);
+ }
+
+ allocated_nfds = context->cached_poll_array_size;
+ fds = context->cached_poll_array;
+
+ UNLOCK_CONTEXT (context);
+
+ mw_main_context_prepare (context, &max_priority);
+
+ while ((nfds = mw_main_context_query (context, max_priority, &timeout, fds,
+ allocated_nfds)) > allocated_nfds) {
+ LOCK_CONTEXT (context);
+ free (fds);
+ context->cached_poll_array_size = allocated_nfds = nfds;
+ context->cached_poll_array = fds = calloc (sizeof(mw_pollfd_t), nfds);
+ UNLOCK_CONTEXT (context);
+ }
+
+ if (!block)
+ timeout = 0;
+
+ mw_main_context_poll (context, timeout, max_priority, fds, nfds);
+
+ some_ready = mw_main_context_check (context, max_priority, fds, nfds);
+
+ if (dispatch)
+ mw_main_context_dispatch (context);
+
+ mw_main_context_release (context);
+
+ LOCK_CONTEXT (context);
+
+ return some_ready;
+}
+
+/**
+ * mw_main_context_pending:
+ * @context: a #mw_main_context_t (if %NULL, the default context will be used)
+ *
+ * Checks if any sources have pending events for the given context.
+ *
+ * Return value: %MW_TRUE if events are pending.
+ **/
+mw_bool_t
+mw_main_context_pending (mw_main_context_t *context)
+{
+ mw_bool_t retval;
+
+ mw_assert (context != NULL);
+
+ LOCK_CONTEXT (context);
+ retval = mw_main_context_iterate (context, MW_FALSE, MW_FALSE, MW_THREAD_SELF);
+ UNLOCK_CONTEXT (context);
+
+ return retval;
+}
+
+/**
+ * mw_main_context_iteration:
+ * @context: a #mw_main_context_t (if %NULL, the default context will be used)
+ * @may_block: whether the call may block.
+ *
+ * Runs 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 %MW_TRUE, waiting
+ * for a source to become ready, then dispatching the highest priority
+ * events sources that are ready. Otherwise, if @may_block is %MW_FALSE
+ * sources are not waited to become ready, only those highest priority
+ * events sources will be dispatched (if any), that are ready at this
+ * given moment without further waiting.
+ *
+ * Note that even when @may_block is %MW_TRUE, it is still possible for
+ * mw_main_context_iteration() to return %MW_FALSE, since the the wait may
+ * be interrupted for other reasons than an event source becoming ready.
+ *
+ * Return value: %MW_TRUE if events were dispatched.
+ **/
+mw_bool_t
+mw_main_context_iteration (mw_main_context_t *context, mw_bool_t may_block)
+{
+ mw_bool_t retval;
+
+ mw_assert (context != NULL);
+
+ LOCK_CONTEXT (context);
+ retval = mw_main_context_iterate (context, may_block, MW_TRUE, MW_THREAD_SELF);
+ UNLOCK_CONTEXT (context);
+
+ return retval;
+}
+
+/* HOLDS: context's lock */
+static void
+mw_main_context_poll (mw_main_context_t *context,
+ int timeout,
+ int priority,
+ mw_pollfd_t *fds,
+ int n_fds)
+{
+#ifdef MW_MAIN_POLL_DEBUG
+ mw_poll_rec_t *pollrec;
+ int i;
+#endif
+
+ mw_poll_t *poll;
+
+
+ if (n_fds || timeout != 0) {
+#ifdef MW_MAIN_POLL_DEBUG
+ if (_mw_main_poll_debug) {
+ mw_print ("polling context=%p n=%d timeout=%d\n",
+ context, n_fds, timeout);
+ poll_timer = mw_timer_new ();
+ }
+#endif
+
+ LOCK_CONTEXT (context);
+
+ poll = mw_object_ref (context->poll);
+
+ UNLOCK_CONTEXT (context);
+ if (mw_poll_poll (poll, fds, n_fds, timeout) < 0 && errno != EINTR) {
+#ifndef MW_OS_WINDOWS
+ fprintf (stderr, "poll(2) failed due to: %s.",
+ strerror (errno));
+#else
+ /* If mw_poll () returns -1, it has already called mw_warning() */
+#endif
+ }
+ mw_object_unref (poll);
+
+#ifdef MW_MAIN_POLL_DEBUG
+ if (_mw_main_poll_debug) {
+ LOCK_CONTEXT (context);
+
+ pollrec = context->poll_records;
+ while (pollrec != NULL) {
+ i = 0;
+ while (i < n_fds) {
+ if (fds[i].fd == pollrec->fd->fd &&
+ pollrec->fd->events &&
+ fds[i].revents) {
+ printf (" [ 0x%ld :", (long)fds[i].fd);
+ if (fds[i].revents & MW_IO_IN)
+ print ("i");
+ if (fds[i].revents & MW_IO_OUT)
+ printf ("o");
+ if (fds[i].revents & MW_IO_PRI)
+ printf ("p");
+ if (fds[i].revents & MW_IO_ERR)
+ printf ("e");
+ if (fds[i].revents & MW_IO_HUP)
+ printf ("h");
+ if (fds[i].revents & MW_IO_NVAL)
+ printf ("n");
+ printf ("]");
+ }
+ i++;
+ }
+ pollrec = pollrec->next;
+ }
+ printf ("\n");
+
+ UNLOCK_CONTEXT (context);
+ }
+#endif
+ } /* if (n_fds || timeout != 0) */
+}
+
+/**
+ * mw_main_context_add_poll:
+ * @context: a #mw_main_context_t (or %NULL for the default context)
+ * @fd: a #mw_pollfd_t 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 mw_source_attach() to ensure that the
+ * file descriptor is polled whenever the results may be needed.
+ *
+ * Adds a file descriptor to the set of file descriptors polled for
+ * this context. This will very seldomly be used directly. Instead
+ * a typical event source will use mw_source_add_poll() instead.
+ **/
+void
+mw_main_context_add_poll (mw_main_context_t *context,
+ mw_pollfd_t *fd,
+ int priority)
+{
+ mw_assert (context && fd);
+
+ LOCK_CONTEXT (context);
+ mw_main_context_add_poll_unlocked (context, priority, fd);
+ UNLOCK_CONTEXT (context);
+}
+
+/* HOLDS: main_loop_lock */
+void
+mw_main_context_add_poll_unlocked (mw_main_context_t *context,
+ int priority,
+ mw_pollfd_t *fd)
+{
+ mw_poll_rec_t *lastrec, *pollrec;
+ mw_poll_rec_t *newrec = malloc (sizeof(mw_poll_rec_t));
+
+ /* This file descriptor may be checked before we ever poll */
+ fd->revents = 0;
+ newrec->fd = fd;
+ newrec->prio = priority;
+
+ lastrec = NULL;
+ pollrec = context->poll_records;
+ while (pollrec && priority >= pollrec->prio) {
+ lastrec = pollrec;
+ pollrec = pollrec->next;
+ }
+
+ if (lastrec)
+ lastrec->next = newrec;
+ else
+ context->poll_records = newrec;
+
+ newrec->next = pollrec;
+
+ context->n_poll_records++;
+
+ context->poll_changed = MW_TRUE;
+
+ /* Now wake up the main loop if it is waiting in the poll() */
+ mw_main_context_wakeup_unlocked (context);
+}
+
+/**
+ * mw_main_context_remove_poll:
+ * @context:a #mw_main_context_t
+ * @fd: a #mw_pollfd_t descriptor previously added with mw_main_context_add_poll()
+ *
+ * Removes file descriptor from the set of file descriptors to be
+ * polled for a particular context.
+ **/
+void
+mw_main_context_remove_poll (mw_main_context_t *context,
+ mw_pollfd_t *fd)
+{
+ mw_assert (context && fd);
+
+ LOCK_CONTEXT (context);
+ mw_main_context_remove_poll_unlocked (context, fd);
+ UNLOCK_CONTEXT (context);
+}
+
+void
+mw_main_context_remove_poll_unlocked (mw_main_context_t *context,
+ mw_pollfd_t *fd)
+{
+ mw_poll_rec_t *pollrec, *lastrec;
+
+ lastrec = NULL;
+ pollrec = context->poll_records;
+
+ while (pollrec) {
+ if (pollrec->fd == fd) {
+ if (lastrec != NULL)
+ lastrec->next = pollrec->next;
+ else
+ context->poll_records = pollrec->next;
+
+ free (pollrec);
+
+ context->n_poll_records--;
+ break;
+ }
+ lastrec = pollrec;
+ pollrec = pollrec->next;
+ }
+
+ context->poll_changed = MW_TRUE;
+
+ /* Now wake up the main loop if it is waiting in the poll() */
+ mw_main_context_wakeup_unlocked (context);
+}
+
+/**
+ * mw_main_context_get_current_time:
+ * @context: a #mw_main_context_t
+ * @timeval: #mw_timeval_t 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 mw_get_system_time() directly is that when
+ * checking multiple sources, Milkway can cache a single value
+ * instead of having to repeatedly get the system time.
+ **/
+void
+mw_main_context_get_current_time (mw_main_context_t *context,
+ mw_timeval_t *timeval)
+{
+ mw_assert (context != NULL);
+
+ LOCK_CONTEXT (context);
+
+ if (!context->time_is_current) {
+ mw_get_system_time (&context->current_time);
+ context->time_is_current = MW_TRUE;
+ }
+
+ *timeval = context->current_time;
+
+ UNLOCK_CONTEXT (context);
+}
+
+/* HOLDS: context's lock */
+/* Wake the main loop up from a poll() */
+void
+mw_main_context_wakeup_unlocked (mw_main_context_t *context)
+{
+ if (context->poll_waiting) {
+ context->poll_waiting = MW_FALSE;
+#ifndef MW_OS_WINDOWNS
+ write (context->wake_up_pipe[1], "A", 1);
+#else
+ ReleaseSemaphore (context->wake_up_semaphore, 1, NULL);
+#endif
+ }
+}
+
+/**
+ * mw_main_context_wakeup:
+ * @context: a #mw_main_context_t
+ *
+ * If @context is currently waiting in a poll(), interrupt
+ * the poll(), and continue the iteration process.
+ **/
+void
+mw_main_context_wakeup (mw_main_context_t *context)
+{
+ mw_assert (context != NULL);
+
+ LOCK_CONTEXT (context);
+ mw_main_context_wakeup_unlocked (context);
+ UNLOCK_CONTEXT (context);
+}
+
+/**
+ * mw_main_context_is_owner:
+ * @context: a #mw_main_context_t
+ *
+ * Determines whether this thread holds the (recursive)
+ * ownership of this #GMaincontext. This is useful to
+ * know before waiting on another thread that may be
+ * blocking to get ownership of @context.
+ *
+ * Returns: %MW_TRUE if current thread is owner of @context.
+ *
+ * Since: 2.10
+ **/
+mw_bool_t
+mw_main_context_is_owner (mw_main_context_t *context)
+{
+ mw_bool_t is_owner;
+
+ mw_assert (context != NULL);
+
+ LOCK_CONTEXT (context);
+ is_owner = context->owner == MW_THREAD_SELF;
+ UNLOCK_CONTEXT (context);
+ return is_owner;
+}
+
+static void
+mw_main_context_type_init(mw_main_context_type_t *self)
+{
+ self->base.finalize = mw_main_context_finalize;
+}
+
+MW_DEFINE_GET_TYPE(mw_main_context, mw_main_context_type_t,
+ MW_OBJECT_TYPE, "MWMainContext", 0);
diff --git a/milkway/mw-main-context.h b/milkway/mw-main-context.h
new file mode 100644
index 0000000..c355947
--- /dev/null
+++ b/milkway/mw-main-context.h
@@ -0,0 +1,125 @@
+/* Milkway
+ *
+ * Copyright (C) 2008- Luo Jinghua <sunmoon1997@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * 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
+ * Library General Public License for more details.
+ *
+ * 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.
+ */
+/* 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 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
+ * Library General Public License for more details.
+ *
+ * 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.
+ */
+
+#ifndef __MW_MAIN_H__
+#define __MW_MAIN_H__
+
+#include <milkway/mw-poll.h>
+#include <milkway/mw-source.h>
+
+MW_BEGIN_DECLS
+
+typedef struct _mw_main_context_type mw_main_context_type_t;
+typedef struct _mw_main_context mw_main_context_t; /* Opaque */
+
+#define MW_MAIN_CONTEXT_TYPE mw_main_context_get_type()
+
+mw_public mw_main_context_type_t*
+mw_main_context_get_type(void);
+
+mw_public mw_main_context_t*
+mw_main_context_new (void);
+
+mw_public mw_bool_t
+mw_main_context_iteration (mw_main_context_t *context,
+ mw_bool_t may_block);
+
+mw_public mw_bool_t
+mw_main_context_pending (mw_main_context_t *context);
+
+/* Low level functions for implementing custom main loops.
+ */
+mw_public void
+mw_main_context_wakeup (mw_main_context_t *context);
+
+mw_public mw_bool_t
+mw_main_context_acquire (mw_main_context_t *context);
+
+mw_public void
+mw_main_context_release (mw_main_context_t *context);
+
+mw_public mw_bool_t
+mw_main_context_is_owner (mw_main_context_t *context);
+
+mw_public mw_bool_t
+mw_main_context_wait (mw_main_context_t *context,
+ mw_cond_t *cond,
+ mw_mutex_t *mutex);
+
+mw_public mw_bool_t
+mw_main_context_prepare (mw_main_context_t *context,
+ int *priority);
+
+mw_public int
+mw_main_context_query (mw_main_context_t *context,
+ int max_priority,
+ int *timeout_,
+ mw_pollfd_t *fds,
+ int n_fds);
+
+mw_public int
+mw_main_context_check (mw_main_context_t *context,
+ int max_priority,
+ mw_pollfd_t *fds,
+ int n_fds);
+
+mw_public void
+mw_main_context_dispatch (mw_main_context_t *context);
+
+mw_public mw_uint_t
+mw_main_context_add_source (mw_main_context_t *context,
+ mw_source_t *source);
+
+/* Low level functions for use by source implementations
+ */
+mw_public void
+mw_main_context_add_poll (mw_main_context_t *context,
+ mw_pollfd_t *fd,
+ int priority);
+
+mw_public void
+mw_main_context_remove_poll (mw_main_context_t *context,
+ mw_pollfd_t *fd);
+
+mw_public void
+mw_main_context_get_current_time (mw_main_context_t *context,
+ mw_timeval_t *timeval);
+
+MW_END_DECLS
+
+#endif /* __MW_MAIN_H__ */
diff --git a/milkway/mw-source.c b/milkway/mw-source.c
index 70895fc..f2a581b 100644
--- a/milkway/mw-source.c
+++ b/milkway/mw-source.c
@@ -19,6 +19,8 @@
*/
#include "milkwayint.h"
#include "milkway/mw-source.h"
+#include "milkway/mw-main-context.h"
+#include "milkway/mw-main-context-private.h"
mw_source_t*
mw_source_init(mw_source_t *self)
@@ -27,6 +29,12 @@ mw_source_init(mw_source_t *self)
return NULL;
self->prio = 0;
+ self->id = 0;
+ self->flags = MW_SOURCE_ACTIVE;
+ self->prev = NULL;
+ self->next = NULL;
+ self->fds = NULL;
+ self->context = NULL;
return self;
}
@@ -35,14 +43,120 @@ mw_source_set_priority(mw_source_t *self,
int prio)
{
self->prio = prio;
+ if (self->context)
+ mw_main_context_set_source_priority (self->context, self, prio);
+}
+
+/**
+ * mw_source_add_poll:
+ * @source:a #mw_source_t
+ * @fd: a #mw_pollfd_t structure holding information about a file
+ * descriptor to watch.
+ *
+ * Adds a file descriptor to the set of file descriptors polled for
+ * this source. This is usually combined with mw_source_new() to add an
+ * event source. The event source's check function will typically test
+ * the @revents field in the #mw_pollfd_t struct and return %MW_TRUE if events need
+ * to be processed.
+ **/
+void
+mw_source_add_poll (mw_source_t *source,
+ mw_pollfd_t *fd)
+{
+ mw_main_context_t *context;
+
+ context = source->context;
+ if (context)
+ mw_main_context_lock (context);
+
+ source->fds = mw_slist_prepend (source->fds, fd);
+
+ if (context) {
+ if (!mw_source_is_blocked (source))
+ mw_main_context_add_poll_unlocked (context, source->prio, fd);
+ mw_main_context_unlock (context);
+ }
+}
+
+/**
+ * mw_source_remove_poll:
+ * @source:a #mw_source_t
+ * @fd: a #mw_pollfd_t structure previously passed to mw_source_add_poll().
+ *
+ * Removes a file descriptor from the set of file descriptors polled for
+ * this source.
+ **/
+void
+mw_source_remove_poll (mw_source_t *source,
+ mw_pollfd_t *fd)
+{
+ mw_main_context_t *context;
+
+ context = source->context;
+
+ if (context)
+ mw_main_context_lock (context);
+
+ source->fds = mw_slist_remove (source->fds, fd);
+
+ if (context) {
+ if (!mw_source_is_blocked (source))
+ mw_main_context_remove_poll_unlocked (context, fd);
+ mw_main_context_unlock (context);
+ }
+}
+
+void
+mw_source_get_current_time (mw_source_t *source,
+ mw_timeval_t *timeval)
+{
+ mw_main_context_t *context = source->context;
+
+ if (!context)
+ mw_get_system_time(timeval);
+ else
+ mw_main_context_get_current_time(context, timeval);
+}
+
+/**
+ * mw_source_destroy:
+ * @source: a #mw_source_t
+ *
+ * Removes a source from its #mw_main_context_t, if any, and mark it as
+ * destroyed. The source cannot be subsequently added to another
+ * context.
+ **/
+void
+mw_source_destroy(mw_source_t *source)
+{
+ mw_main_context_t *context = source->context;
+
+ if (context)
+ mw_main_context_lock(context);
+
+ if (mw_source_is_active (source)) {
+ mw_assert (context != NULL);
+
+ source->flags &= ~MW_SOURCE_ACTIVE;
+
+ if (context)
+ mw_main_context_remove_source (context, source);
+ source->context = NULL;
+
+ mw_object_unref (source);
+ }
+
+ if (context)
+ mw_main_context_unlock (context);
}
static void
-mw_source_finalize(mw_object_t *self)
+mw_source_finalize(mw_object_t *super)
{
- mw_object_type_t *stype = MW_SUPER_TYPE_BASE(self, MW_SOURCE_TYPE);
+ mw_source_t *self = (mw_source_t*)super;
- stype->finalize(self);
+ mw_assert (self->context == NULL);
+ MW_SUPER_FINALIZE(super, MW_SOURCE_TYPE)(super);
}
static void
diff --git a/milkway/mw-source.h b/milkway/mw-source.h
index a273526..dcd6fec 100644
--- a/milkway/mw-source.h
+++ b/milkway/mw-source.h
@@ -20,11 +20,14 @@
#ifndef MW_SOURCE_H
#define MW_SOURCE_H
-#include <milkway/mw-object.h>
+#include <milkway/mw-poll.h>
+#include <milkway/mw-slist.h>
typedef struct mw_source_type mw_source_type_t;
typedef struct mw_source mw_source_t;
+
#define MW_SOURCE_TYPE mw_source_get_type()
+
struct mw_source_type {
mw_object_type_t base;
@@ -34,11 +37,26 @@ struct mw_source_type {
mw_bool_t (*dispatch) (mw_source_t *self);
};
+typedef enum {
+ MW_SOURCE_ACTIVE = 1 << 0,
+ MW_SOURCE_BLOCKED = 1 << 1,
+ MW_SOURCE_READY = 1 << 2,
+ MW_SOURCE_CAN_RECURSE = 1 << 3,
+ MW_SOURCE_DISPATCHING = 1 << 4
+} mw_source_flags_t;
+
struct mw_source {
mw_object_t base;
/** priority */
int prio;
+ mw_source_flags_t flags;
+ mw_slist_t *fds;
+ void *context;
+ mw_uint_t id;
+
+ mw_source_t *next;
+ mw_source_t *prev;
};
#define MW_PRIORITY_HIGH -100
@@ -63,8 +81,7 @@ mw_source_prepare(mw_source_t *self,
}
static mw_inline mw_bool_t
-mw_source_check(mw_source_t *self,
- int timeout_)
+mw_source_check(mw_source_t *self)
{
mw_source_type_t *type = (mw_source_type_t *)MW_TYPE(self);
@@ -79,8 +96,72 @@ mw_source_dispatch(mw_source_t *self)
return type->dispatch(self);
}
+static mw_inline mw_bool_t
+mw_source_is_active(mw_source_t *self)
+{
+ return self->flags & MW_SOURCE_ACTIVE;
+}
+
+static mw_inline mw_bool_t
+mw_source_is_dispatching(mw_source_t *self)
+{
+ return self->flags & MW_SOURCE_DISPATCHING;
+}
+
+static mw_inline mw_bool_t
+mw_source_get_can_recurse(mw_source_t *self)
+{
+ return self->flags & MW_SOURCE_CAN_RECURSE;
+}
+
+static mw_inline void
+mw_source_set_can_recurse(mw_source_t *self)
+{
+ self->flags |= MW_SOURCE_CAN_RECURSE;
+}
+
+static mw_inline mw_bool_t
+mw_source_is_blocked(mw_source_t *self)
+{
+ return self->flags & MW_SOURCE_DISPATCHING &&
+ !(self->flags & MW_SOURCE_CAN_RECURSE);
+}
+
+static mw_inline mw_bool_t
+mw_source_is_ready(mw_source_t *self)
+{
+ return self->flags & MW_SOURCE_READY;
+}
+
+static mw_inline void
+mw_source_set_ready(mw_source_t *self)
+{
+ self->flags |= MW_SOURCE_READY;
+}
+
+static mw_inline mw_uint_t
+mw_source_get_id(mw_source_t *self)
+{
+ return self->id;
+}
+
mw_public void
mw_source_set_priority(mw_source_t *self,
int prio);
+mw_public void
+mw_source_add_poll (mw_source_t *source,
+ mw_pollfd_t *fd);
+
+mw_public void
+mw_source_remove_poll (mw_source_t *source,
+ mw_pollfd_t *fd);
+
+mw_public void
+mw_source_get_current_time (mw_source_t *source,
+ mw_timeval_t *timeval);
+
+mw_public void
+mw_source_destroy(mw_source_t *self);
+
#endif