/pygobject-2.28.6/glib/pygmainloop.c
C | 360 lines | 251 code | 64 blank | 45 comment | 38 complexity | 4b6e992a5c307182a4b8e521a974af79 MD5 | raw file
Possible License(s): LGPL-2.1
- /* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
- * USA
- */
- #ifdef HAVE_CONFIG_H
- # include <config.h>
- #endif
- #include <fcntl.h>
- #include <Python.h>
- #include <pythread.h>
- #include <glib.h>
- #include "pygmainloop.h"
- #include "pygmaincontext.h"
- #include "pyglib.h"
- #include "pyglib-private.h"
- static int pipe_fds[2];
- typedef struct {
- GSource source;
- 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)
- {
- /* 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.
- */
- #ifdef HAVE_PYSIGNAL_SETWAKEUPFD
- return FALSE;
- #else /* !HAVE_PYSIGNAL_SETWAKEUPFD */
- /* 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 /* PLATFORM_WIN32 */
- /* 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;
- return FALSE;
- #endif /* HAVE_PYSIGNAL_SETWAKEUPFD */
- }
- static gboolean
- pyg_signal_watch_check(GSource *source)
- {
- PyGILState_STATE state;
- GMainLoop *main_loop;
- #ifdef HAVE_PYSIGNAL_SETWAKEUPFD
- PySignalWatchSource *real_source = (PySignalWatchSource *)source;
- GPollFD *poll_fd = &real_source->fd;
- unsigned char dummy;
- gssize ret;
- if (poll_fd->revents & G_IO_IN)
- ret = read(poll_fd->fd, &dummy, 1);
- #endif
- 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 GSourceFuncs pyg_signal_watch_funcs =
- {
- pyg_signal_watch_prepare,
- pyg_signal_watch_check,
- pyg_signal_watch_dispatch
- };
- static GSource *
- pyg_signal_watch_new(void)
- {
- GSource *source = g_source_new(&pyg_signal_watch_funcs,
- sizeof(PySignalWatchSource));
- #ifdef HAVE_PYSIGNAL_SETWAKEUPFD
- PySignalWatchSource *real_source = (PySignalWatchSource *)source;
- int flag;
- /* 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.
- * XXX More efficient than a pipe, we could use an eventfd on Linux
- * kernels that support it.
- */
- gint already_piped = (pipe_fds[0] > 0);
- if (!already_piped) {
- if (pipe(pipe_fds) < 0)
- g_error("Cannot create main loop pipe: %s\n",
- g_strerror(errno));
- /* Make the write end of the fd non blocking */
- flag = fcntl(pipe_fds[1], F_GETFL, 0);
- flag |= O_NONBLOCK;
- fcntl(pipe_fds[1], F_SETFL, flag);
- }
- real_source->fd.fd = pipe_fds[0];
- real_source->fd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
- g_source_add_poll(source, &real_source->fd);
- if (!already_piped)
- PySignal_SetWakeupFd(pipe_fds[1]);
- #endif
- return source;
- }
- PYGLIB_DEFINE_TYPE("glib.MainLoop", PyGMainLoop_Type, PyGMainLoop)
- static int
- pyg_main_loop_init(PyGMainLoop *self, PyObject *args, PyObject *kwargs)
- {
- static char *kwlist[] = { "context", "is_running", NULL };
- PyObject *py_context = Py_None;
- int is_running = 0;
- 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.MainContext 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);
- g_source_unref(self->signal_source);
- 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 PyObject*
- pyg_main_loop_richcompare(PyObject *self, PyObject *other, int op)
- {
- if (Py_TYPE(self) == Py_TYPE(other) && Py_TYPE(self) == &PyGMainLoop_Type)
- return _pyglib_generic_ptr_richcompare(((PyGMainLoop*)self)->loop,
- ((PyGMainLoop*)other)->loop,
- op);
- else {
- Py_INCREF(Py_NotImplemented);
- return Py_NotImplemented;
- }
- }
- static PyObject *
- _wrap_g_main_loop_get_context (PyGMainLoop *loop)
- {
- return pyg_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 }
- };
- void
- pyglib_mainloop_register_types(PyObject *d)
- {
- PyGMainLoop_Type.tp_dealloc = (destructor)pyg_main_loop_dealloc;
- PyGMainLoop_Type.tp_richcompare = pyg_main_loop_richcompare;
- PyGMainLoop_Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
- PyGMainLoop_Type.tp_methods = _PyGMainLoop_methods;
- PyGMainLoop_Type.tp_init = (initproc)pyg_main_loop_init;
- PYGLIB_REGISTER_TYPE(d, PyGMainLoop_Type, "MainLoop");
- }