diff options
author | Mark McLoughlin <mark@skynet.ie> | 2005-02-09 11:32:42 +0000 |
---|---|---|
committer | Mark McLoughlin <markmc@src.gnome.org> | 2005-02-09 11:32:42 +0000 |
commit | fa8fde62639f0237cd39f4c06f3ff4e8c2ff5158 (patch) | |
tree | 97e3ddcd8d3082c809fad2e9c8d21c1f24cbb3f5 | |
parent | f0ff018ae9f5fbb5223ec4fbc70092f185ff83d5 (diff) | |
download | pygobject-fa8fde62639f0237cd39f4c06f3ff4e8c2ff5158.tar.gz pygobject-fa8fde62639f0237cd39f4c06f3ff4e8c2ff5158.tar.xz pygobject-fa8fde62639f0237cd39f4c06f3ff4e8c2ff5158.zip |
Fix for bug #154779 - Python signal handlers don't get executed while
2005-02-09 Mark McLoughlin <mark@skynet.ie>
Fix for bug #154779 - Python signal handlers don't
get executed while you're sitting in the main loop.
* gobject/pygmainloop.c:
(pyg_save_current_main_loop),
(pyg_restore_current_main_loop),
(pyg_get_current_main_loop): functions for keeping
track of the currently running main loop. A version
using TLS and another using a global variable, depending
on whether DISABLE_THREADING is defined
(pyg_signal_watch_prepare), (pyg_signal_watch_check),
(pyg_signal_watch_dispatch), (pyg_signal_watch_new):
a GSource which runs the python signal handlers whenever
the mainloop is interrupted by signal delivery.
(pyg_main_loop_new), (pyg_main_loop_dealloc): add and
remove the source.
(_wrap_g_main_loop_run): push/pop the currently running
main loop.
* gobject/pygobject-private.h: add a pointer for the
source to PyGMainLoop.
* gobject/Makefile.am: add -DPLATFORM_WIN32 to cflags
if building on Windows.
-rw-r--r-- | gobject/Makefile.am | 5 | ||||
-rw-r--r-- | gobject/pygmainloop.c | 171 | ||||
-rw-r--r-- | gobject/pygobject-private.h | 1 |
3 files changed, 175 insertions, 2 deletions
diff --git a/gobject/Makefile.am b/gobject/Makefile.am index a3e5e87..286626f 100644 --- a/gobject/Makefile.am +++ b/gobject/Makefile.am @@ -13,6 +13,7 @@ if PLATFORM_WIN32 common_ldflags += -no-undefined endif +gobject_la_CFLAGS = $(GLIB_CFLAGS) gobject_la_LDFLAGS = $(common_ldflags) -export-symbols-regex initgobject gobject_la_LIBADD = $(GLIB_LIBS) gobject_la_SOURCES = \ @@ -28,3 +29,7 @@ gobject_la_SOURCES = \ pygparamspec.c \ pygpointer.c \ pygtype.c + +if PLATFORM_WIN32 +gobject_la_CFLAGS += -DPLATFORM_WIN32 +endif diff --git a/gobject/pygmainloop.c b/gobject/pygmainloop.c index 7a6b1c1..2c65cac 100644 --- a/gobject/pygmainloop.c +++ b/gobject/pygmainloop.c @@ -26,6 +26,144 @@ #endif #include "pygobject-private.h" +#include "pythread.h" + +#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); + 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. So, we need to time out and check for signals + * regularily too. + * Also, on Windows g_poll() won't be interrupted by a signal + * (AFAIK), so we need the timeout there too. + */ +#ifndef PLATFORM_WIN32 + if (pyg_threads_enabled) +#endif + *timeout = 100; + + return FALSE; +} + +static gboolean +pyg_signal_watch_check(GSource *source) +{ + PyGILState_STATE state; + GMainLoop *main_loop; + + state = pyg_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); + } + + pyg_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, + NULL +}; + +static GSource * +pyg_signal_watch_new(void) +{ + return g_source_new(&pyg_signal_watch_funcs, sizeof(GSource)); +} static int pyg_main_loop_new(PyGMainLoop *self, PyObject *args, PyObject *kwargs) @@ -55,9 +193,29 @@ pyg_main_loop_new(PyGMainLoop *self, PyObject *args, PyObject *kwargs) } 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) { @@ -100,10 +258,19 @@ _wrap_g_main_loop_quit (PyGMainLoop *self) static PyObject * _wrap_g_main_loop_run (PyGMainLoop *self) { + GMainLoop *prev_loop; + + prev_loop = pyg_save_current_main_loop(self->loop); + pyg_begin_allow_threads; g_main_loop_run(self->loop); pyg_end_allow_threads; - + + pyg_restore_current_main_loop(prev_loop); + + if (PyErr_Occurred()) + return NULL; + Py_INCREF(Py_None); return Py_None; } @@ -123,7 +290,7 @@ PyTypeObject PyGMainLoop_Type = { sizeof(PyGMainLoop), 0, /* methods */ - (destructor)0, + (destructor)pyg_main_loop_dealloc, (printfunc)0, (getattrfunc)0, (setattrfunc)0, diff --git a/gobject/pygobject-private.h b/gobject/pygobject-private.h index d826f92..c5852e8 100644 --- a/gobject/pygobject-private.h +++ b/gobject/pygobject-private.h @@ -145,6 +145,7 @@ extern PyObject * pyg_enum_from_gtype (GType gtype, typedef struct { PyObject_HEAD GMainLoop *loop; + GSource *signal_source; } PyGMainLoop; extern PyTypeObject PyGMainLoop_Type; |