summaryrefslogtreecommitdiffstats
path: root/glib/pygmainloop.c
diff options
context:
space:
mode:
authorJohan Dahlin <johan@gnome.org>2008-07-20 14:17:04 +0000
committerJohan Dahlin <johan@src.gnome.org>2008-07-20 14:17:04 +0000
commit45ccf5f6ff1a1805ed326b397c70cf55bba28124 (patch)
tree59b30668a1b28efced9f417b1e6bc2f4bc9709ec /glib/pygmainloop.c
parent9cd866c9066eaa933b54e77225ada672fcfacad3 (diff)
downloadpygobject-45ccf5f6ff1a1805ed326b397c70cf55bba28124.tar.gz
pygobject-45ccf5f6ff1a1805ed326b397c70cf55bba28124.tar.xz
pygobject-45ccf5f6ff1a1805ed326b397c70cf55bba28124.zip
Move maincontext and mainloop over to glib. Update the threadstate api to
2008-07-20 Johan Dahlin <johan@gnome.org> * glib/Makefile.am: * glib/glibmodule.c (pyglib_main_context_default), (init_glib): * glib/pyglib.c (pyglib_init), (pyglib_threads_enabled), (pyglib_main_context_new): * glib/pyglib.h: * glib/pygmaincontext.c (_wrap_g_main_context_iteration), (pyglib_maincontext_register_types): * glib/pygmaincontext.h: * glib/pygmainloop.c (pyg_signal_watch_prepare), (pyg_signal_watch_check), (pyg_main_loop_new), (_wrap_g_main_loop_get_context), (_wrap_g_main_loop_run), (pyglib_mainloop_register_types): * glib/pygmainloop.h: * gobject/Makefile.am: * gobject/__init__.py: * gobject/gobjectmodule.c (pyg_destroy_notify), (pyobject_free), (pyg_object_set_property), (pyg_object_get_property), (_pyg_signal_accumulator), (pygobject__g_instance_init), (pyg_handler_marshal), (pygobject_gil_state_ensure), (pygobject_gil_state_release), (marshal_emission_hook), (_log_func), (init_gobject): * gobject/pygboxed.c (pyg_boxed_dealloc), (pyg_boxed_new): * gobject/pygenum.c (pyg_enum_add): * gobject/pygflags.c (pyg_flags_add): * gobject/pygiochannel.c (pyg_iowatch_marshal): * gobject/pygmaincontext.c: * gobject/pygmainloop.c: * gobject/pygobject-private.h: * gobject/pygobject.c (pygobject_data_free), (pyg_toggle_notify), (pygobject_new_with_interfaces), (pygobject_weak_ref_notify): * gobject/pygobject.h: * gobject/pygoptiongroup.c (destroy_g_group), (arg_func): * gobject/pygpointer.c (pyg_pointer_new): * gobject/pygsource.c (pyg_source_get_context), (pyg_source_prepare), (pyg_source_check), (pyg_source_dispatch), (pyg_source_finalize): * gobject/pygtype.c (pyg_closure_invalidate), (pyg_closure_marshal), (pyg_signal_class_closure_marshal): * tests/common.py: Move maincontext and mainloop over to glib. Update the threadstate api to use the variant in glib. svn path=/trunk/; revision=843
Diffstat (limited to 'glib/pygmainloop.c')
-rw-r--r--glib/pygmainloop.c383
1 files changed, 383 insertions, 0 deletions
diff --git a/glib/pygmainloop.c b/glib/pygmainloop.c
new file mode 100644
index 0000000..04653de
--- /dev/null
+++ b/glib/pygmainloop.c
@@ -0,0 +1,383 @@
+/* -*- Mode: C; c-basic-offset: 4 -*-
+ * pygtk- Python bindings for the GTK toolkit.
+ * Copyright (C) 1998-2003 James Henstridge
+ * Copyright (C) 2004 Johan Dahlin
+ *
+ * pygmainloop.c: GMainLoop wrapper
+ *
+ * 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.1 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
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <Python.h>
+#include <pythread.h>
+#include <glib.h>
+
+#include "pygmainloop.h"
+#include "pygmaincontext.h"
+#include "pyglib.h"
+#include "pyglib-private.h"
+
+
+typedef struct {
+ GSource source;
+ int fds[2];
+ GPollFD fd;
+} PySignalWatchSource;
+
+#ifdef DISABLE_THREADING
+static GMainLoop *pyg_current_main_loop = NULL;;
+
+static inline GMainLoop *
+pyg_save_current_main_loop (GMainLoop *main_loop)
+{
+ GMainLoop *retval = pyg_current_main_loop;
+
+ g_return_val_if_fail(main_loop != NULL, NULL);
+
+ pyg_current_main_loop = g_main_loop_ref(main_loop);
+
+ return retval;
+}
+
+static inline void
+pyg_restore_current_main_loop (GMainLoop *main_loop)
+{
+ if (pyg_current_main_loop != NULL)
+ g_main_loop_unref(pyg_current_main_loop);
+ pyg_current_main_loop = main_loop;
+}
+
+static inline GMainLoop *
+pyg_get_current_main_loop (void)
+{
+ return pyg_current_main_loop;
+}
+#else /* !defined(#ifndef DISABLE_THREADING) */
+
+static int pyg_current_main_loop_key = -1;
+
+static inline GMainLoop *
+pyg_save_current_main_loop (GMainLoop *main_loop)
+{
+ GMainLoop *retval;
+
+ g_return_val_if_fail(main_loop != NULL, NULL);
+
+ if (pyg_current_main_loop_key == -1)
+ pyg_current_main_loop_key = PyThread_create_key();
+
+ retval = PyThread_get_key_value(pyg_current_main_loop_key);
+ PyThread_delete_key_value(pyg_current_main_loop_key);
+ PyThread_set_key_value(pyg_current_main_loop_key,
+ g_main_loop_ref(main_loop));
+
+ return retval;
+}
+
+static inline void
+pyg_restore_current_main_loop (GMainLoop *main_loop)
+{
+ GMainLoop *prev;
+
+ g_return_if_fail (pyg_current_main_loop_key != -1);
+
+ prev = PyThread_get_key_value(pyg_current_main_loop_key);
+ if (prev != NULL)
+ g_main_loop_unref(prev);
+ PyThread_delete_key_value(pyg_current_main_loop_key);
+ if (main_loop != NULL)
+ PyThread_set_key_value(pyg_current_main_loop_key, main_loop);
+}
+
+static inline GMainLoop *
+pyg_get_current_main_loop (void)
+{
+ if (pyg_current_main_loop_key == -1)
+ return NULL;
+ return PyThread_get_key_value(pyg_current_main_loop_key);
+}
+#endif /* DISABLE_THREADING */
+
+static gboolean
+pyg_signal_watch_prepare(GSource *source,
+ int *timeout)
+{
+#ifdef HAVE_PYSIGNAL_SETWAKEUPFD
+ PySignalWatchSource *real_source = (PySignalWatchSource *)source;
+#endif
+
+ /* Python only invokes signal handlers from the main thread,
+ * so if a thread other than the main thread receives the signal
+ * from the kernel, PyErr_CheckSignals() from that thread will
+ * do nothing.
+ */
+
+ /* On Windows g_poll() won't be interrupted by a signal
+ * (AFAIK), so we need the timeout there too, even if there's
+ * only one thread.
+ */
+#ifndef PLATFORM_WIN32
+ if (!pyglib_threads_enabled())
+ return FALSE;
+#endif
+
+#ifdef HAVE_PYSIGNAL_SETWAKEUPFD
+ if (real_source->fds[0] != 0)
+ return FALSE;
+
+ /* Unfortunately we need to create a new pipe here instead of
+ * reusing the pipe inside the GMainContext.
+ * Ideally an api should be added to GMainContext which allows us
+ * to reuse that pipe which would suit us perfectly fine.
+ */
+ if (pipe(real_source->fds) < 0)
+ g_error("Cannot create main loop pipe: %s\n",
+ g_strerror(errno));
+
+ real_source->fd.fd = real_source->fds[0];
+ real_source->fd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
+ g_source_add_poll(source, &real_source->fd);
+
+ PySignal_SetWakeupFd(real_source->fds[1]);
+
+#else /* !HAVE_PYSIGNAL_SETWAKEUPFD */
+ /* If we're using 2.5 or an earlier version of python we
+ * will default to a timeout every second, be aware,
+ * this will cause unnecessary wakeups, see
+ * http://bugzilla.gnome.org/show_bug.cgi?id=481569
+ */
+ *timeout = 1000;
+#endif /* HAVE_PYSIGNAL_SETWAKEUPFD */
+
+ return FALSE;
+}
+
+static gboolean
+pyg_signal_watch_check(GSource *source)
+{
+ PyGILState_STATE state;
+ GMainLoop *main_loop;
+
+ state = pyglib_gil_state_ensure();
+
+ main_loop = pyg_get_current_main_loop();
+
+ if (PyErr_CheckSignals() == -1 && main_loop != NULL) {
+ PyErr_SetNone(PyExc_KeyboardInterrupt);
+ g_main_loop_quit(main_loop);
+ }
+
+ pyglib_gil_state_release(state);
+
+ return FALSE;
+}
+
+static gboolean
+pyg_signal_watch_dispatch(GSource *source,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+ /* We should never be dispatched */
+ g_assert_not_reached();
+ return TRUE;
+}
+
+static void
+pyg_signal_watch_finalize(GSource *source)
+{
+ PySignalWatchSource *real_source = (PySignalWatchSource*)source;
+
+ if (source != NULL) {
+ if (real_source->fds[0] != 0)
+ close(real_source->fds[0]);
+
+ if (real_source->fds[1] != 0)
+ close(real_source->fds[1]);
+ }
+}
+
+static GSourceFuncs pyg_signal_watch_funcs =
+{
+ pyg_signal_watch_prepare,
+ pyg_signal_watch_check,
+ pyg_signal_watch_dispatch,
+ pyg_signal_watch_finalize
+};
+
+static GSource *
+pyg_signal_watch_new(void)
+{
+ return g_source_new(&pyg_signal_watch_funcs, sizeof(PySignalWatchSource));
+}
+
+static int
+pyg_main_loop_new(PyGMainLoop *self, PyObject *args, PyObject *kwargs)
+{
+
+ static char *kwlist[] = { "context", "is_running", NULL };
+ PyObject *py_context = Py_None;
+ int is_running;
+ GMainContext *context;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "|Ob:GMainLoop.__init__",
+ kwlist, &py_context, &is_running))
+ return -1;
+
+ if (!PyObject_TypeCheck(py_context, &PyGMainContext_Type) &&
+ py_context != Py_None) {
+ PyErr_SetString(PyExc_TypeError,
+ "context must be a glib.GMainContext or None");
+ return -1;
+ }
+
+ if (py_context != Py_None) {
+ context = ((PyGMainContext*)py_context)->context;
+ } else {
+ context = NULL;
+ }
+
+ self->loop = g_main_loop_new(context, is_running);
+
+ self->signal_source = pyg_signal_watch_new();
+ g_source_attach(self->signal_source, context);
+
+ return 0;
+}
+
+static void
+pyg_main_loop_dealloc(PyGMainLoop *self)
+{
+ if (self->signal_source != NULL) {
+ g_source_destroy(self->signal_source);
+ self->signal_source = NULL;
+ }
+
+ if (self->loop != NULL) {
+ g_main_loop_unref(self->loop);
+ self->loop = NULL;
+ }
+
+ PyObject_Del(self);
+}
+
+static int
+pyg_main_loop_compare(PyGMainLoop *self, PyGMainLoop *v)
+{
+ if (self->loop == v->loop) return 0;
+ if (self->loop > v->loop) return -1;
+ return 1;
+}
+
+static PyObject *
+_wrap_g_main_loop_get_context (PyGMainLoop *loop)
+{
+ return pyglib_main_context_new(g_main_loop_get_context(loop->loop));
+}
+
+static PyObject *
+_wrap_g_main_loop_is_running (PyGMainLoop *self)
+{
+ return PyBool_FromLong(g_main_loop_is_running(self->loop));
+}
+
+static PyObject *
+_wrap_g_main_loop_quit (PyGMainLoop *self)
+{
+ g_main_loop_quit(self->loop);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_g_main_loop_run (PyGMainLoop *self)
+{
+ GMainLoop *prev_loop;
+
+ prev_loop = pyg_save_current_main_loop(self->loop);
+
+ pyglib_begin_allow_threads;
+ g_main_loop_run(self->loop);
+ pyglib_end_allow_threads;
+
+ pyg_restore_current_main_loop(prev_loop);
+
+ if (PyErr_Occurred())
+ return NULL;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyMethodDef _PyGMainLoop_methods[] = {
+ { "get_context", (PyCFunction)_wrap_g_main_loop_get_context, METH_NOARGS },
+ { "is_running", (PyCFunction)_wrap_g_main_loop_is_running, METH_NOARGS },
+ { "quit", (PyCFunction)_wrap_g_main_loop_quit, METH_NOARGS },
+ { "run", (PyCFunction)_wrap_g_main_loop_run, METH_NOARGS },
+ { NULL, NULL, 0 }
+};
+
+PyTypeObject PyGMainLoop_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0,
+ "glib.MainLoop",
+ sizeof(PyGMainLoop),
+ 0,
+ /* methods */
+ (destructor)pyg_main_loop_dealloc,
+ (printfunc)0,
+ (getattrfunc)0,
+ (setattrfunc)0,
+ (cmpfunc)pyg_main_loop_compare,
+ (reprfunc)0,
+ 0,
+ 0,
+ 0,
+ (hashfunc)0,
+ (ternaryfunc)0,
+ (reprfunc)0,
+ (getattrofunc)0,
+ (setattrofunc)0,
+ 0,
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ NULL,
+ (traverseproc)0,
+ (inquiry)0,
+ (richcmpfunc)0,
+ 0,
+ (getiterfunc)0,
+ (iternextfunc)0,
+ _PyGMainLoop_methods,
+ 0,
+ 0,
+ NULL,
+ NULL,
+ (descrgetfunc)0,
+ (descrsetfunc)0,
+ 0,
+ (initproc)pyg_main_loop_new,
+};
+
+void
+pyglib_mainloop_register_types(PyObject *d)
+{
+ PYGLIB_REGISTER_TYPE(d, PyGMainLoop_Type, "MainLoop");
+}