PageRenderTime 89ms CodeModel.GetById 9ms RepoModel.GetById 1ms app.codeStats 0ms

/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
  1. /* -*- Mode: C; c-basic-offset: 4 -*-
  2. * pygtk- Python bindings for the GTK toolkit.
  3. * Copyright (C) 1998-2003 James Henstridge
  4. * Copyright (C) 2004 Johan Dahlin
  5. *
  6. * pygmainloop.c: GMainLoop wrapper
  7. *
  8. * This library is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU Lesser General Public
  10. * License as published by the Free Software Foundation; either
  11. * version 2.1 of the License, or (at your option) any later version.
  12. *
  13. * This library is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * Lesser General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU Lesser General Public
  19. * License along with this library; if not, write to the Free Software
  20. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
  21. * USA
  22. */
  23. #ifdef HAVE_CONFIG_H
  24. # include <config.h>
  25. #endif
  26. #include <fcntl.h>
  27. #include <Python.h>
  28. #include <pythread.h>
  29. #include <glib.h>
  30. #include "pygmainloop.h"
  31. #include "pygmaincontext.h"
  32. #include "pyglib.h"
  33. #include "pyglib-private.h"
  34. static int pipe_fds[2];
  35. typedef struct {
  36. GSource source;
  37. GPollFD fd;
  38. } PySignalWatchSource;
  39. #ifdef DISABLE_THREADING
  40. static GMainLoop *pyg_current_main_loop = NULL;;
  41. static inline GMainLoop *
  42. pyg_save_current_main_loop (GMainLoop *main_loop)
  43. {
  44. GMainLoop *retval = pyg_current_main_loop;
  45. g_return_val_if_fail(main_loop != NULL, NULL);
  46. pyg_current_main_loop = g_main_loop_ref(main_loop);
  47. return retval;
  48. }
  49. static inline void
  50. pyg_restore_current_main_loop (GMainLoop *main_loop)
  51. {
  52. if (pyg_current_main_loop != NULL)
  53. g_main_loop_unref(pyg_current_main_loop);
  54. pyg_current_main_loop = main_loop;
  55. }
  56. static inline GMainLoop *
  57. pyg_get_current_main_loop (void)
  58. {
  59. return pyg_current_main_loop;
  60. }
  61. #else /* !defined(#ifndef DISABLE_THREADING) */
  62. static int pyg_current_main_loop_key = -1;
  63. static inline GMainLoop *
  64. pyg_save_current_main_loop (GMainLoop *main_loop)
  65. {
  66. GMainLoop *retval;
  67. g_return_val_if_fail(main_loop != NULL, NULL);
  68. if (pyg_current_main_loop_key == -1)
  69. pyg_current_main_loop_key = PyThread_create_key();
  70. retval = PyThread_get_key_value(pyg_current_main_loop_key);
  71. PyThread_delete_key_value(pyg_current_main_loop_key);
  72. PyThread_set_key_value(pyg_current_main_loop_key,
  73. g_main_loop_ref(main_loop));
  74. return retval;
  75. }
  76. static inline void
  77. pyg_restore_current_main_loop (GMainLoop *main_loop)
  78. {
  79. GMainLoop *prev;
  80. g_return_if_fail (pyg_current_main_loop_key != -1);
  81. prev = PyThread_get_key_value(pyg_current_main_loop_key);
  82. if (prev != NULL)
  83. g_main_loop_unref(prev);
  84. PyThread_delete_key_value(pyg_current_main_loop_key);
  85. if (main_loop != NULL)
  86. PyThread_set_key_value(pyg_current_main_loop_key, main_loop);
  87. }
  88. static inline GMainLoop *
  89. pyg_get_current_main_loop (void)
  90. {
  91. if (pyg_current_main_loop_key == -1)
  92. return NULL;
  93. return PyThread_get_key_value(pyg_current_main_loop_key);
  94. }
  95. #endif /* DISABLE_THREADING */
  96. static gboolean
  97. pyg_signal_watch_prepare(GSource *source,
  98. int *timeout)
  99. {
  100. /* Python only invokes signal handlers from the main thread,
  101. * so if a thread other than the main thread receives the signal
  102. * from the kernel, PyErr_CheckSignals() from that thread will
  103. * do nothing.
  104. */
  105. #ifdef HAVE_PYSIGNAL_SETWAKEUPFD
  106. return FALSE;
  107. #else /* !HAVE_PYSIGNAL_SETWAKEUPFD */
  108. /* On Windows g_poll() won't be interrupted by a signal
  109. * (AFAIK), so we need the timeout there too, even if there's
  110. * only one thread.
  111. */
  112. #ifndef PLATFORM_WIN32
  113. if (!pyglib_threads_enabled())
  114. return FALSE;
  115. #endif /* PLATFORM_WIN32 */
  116. /* If we're using 2.5 or an earlier version of python we
  117. * will default to a timeout every second, be aware,
  118. * this will cause unnecessary wakeups, see
  119. * http://bugzilla.gnome.org/show_bug.cgi?id=481569
  120. */
  121. *timeout = 1000;
  122. return FALSE;
  123. #endif /* HAVE_PYSIGNAL_SETWAKEUPFD */
  124. }
  125. static gboolean
  126. pyg_signal_watch_check(GSource *source)
  127. {
  128. PyGILState_STATE state;
  129. GMainLoop *main_loop;
  130. #ifdef HAVE_PYSIGNAL_SETWAKEUPFD
  131. PySignalWatchSource *real_source = (PySignalWatchSource *)source;
  132. GPollFD *poll_fd = &real_source->fd;
  133. unsigned char dummy;
  134. gssize ret;
  135. if (poll_fd->revents & G_IO_IN)
  136. ret = read(poll_fd->fd, &dummy, 1);
  137. #endif
  138. state = pyglib_gil_state_ensure();
  139. main_loop = pyg_get_current_main_loop();
  140. if (PyErr_CheckSignals() == -1 && main_loop != NULL) {
  141. PyErr_SetNone(PyExc_KeyboardInterrupt);
  142. g_main_loop_quit(main_loop);
  143. }
  144. pyglib_gil_state_release(state);
  145. return FALSE;
  146. }
  147. static gboolean
  148. pyg_signal_watch_dispatch(GSource *source,
  149. GSourceFunc callback,
  150. gpointer user_data)
  151. {
  152. /* We should never be dispatched */
  153. g_assert_not_reached();
  154. return TRUE;
  155. }
  156. static GSourceFuncs pyg_signal_watch_funcs =
  157. {
  158. pyg_signal_watch_prepare,
  159. pyg_signal_watch_check,
  160. pyg_signal_watch_dispatch
  161. };
  162. static GSource *
  163. pyg_signal_watch_new(void)
  164. {
  165. GSource *source = g_source_new(&pyg_signal_watch_funcs,
  166. sizeof(PySignalWatchSource));
  167. #ifdef HAVE_PYSIGNAL_SETWAKEUPFD
  168. PySignalWatchSource *real_source = (PySignalWatchSource *)source;
  169. int flag;
  170. /* Unfortunately we need to create a new pipe here instead of
  171. * reusing the pipe inside the GMainContext.
  172. * Ideally an api should be added to GMainContext which allows us
  173. * to reuse that pipe which would suit us perfectly fine.
  174. * XXX More efficient than a pipe, we could use an eventfd on Linux
  175. * kernels that support it.
  176. */
  177. gint already_piped = (pipe_fds[0] > 0);
  178. if (!already_piped) {
  179. if (pipe(pipe_fds) < 0)
  180. g_error("Cannot create main loop pipe: %s\n",
  181. g_strerror(errno));
  182. /* Make the write end of the fd non blocking */
  183. flag = fcntl(pipe_fds[1], F_GETFL, 0);
  184. flag |= O_NONBLOCK;
  185. fcntl(pipe_fds[1], F_SETFL, flag);
  186. }
  187. real_source->fd.fd = pipe_fds[0];
  188. real_source->fd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
  189. g_source_add_poll(source, &real_source->fd);
  190. if (!already_piped)
  191. PySignal_SetWakeupFd(pipe_fds[1]);
  192. #endif
  193. return source;
  194. }
  195. PYGLIB_DEFINE_TYPE("glib.MainLoop", PyGMainLoop_Type, PyGMainLoop)
  196. static int
  197. pyg_main_loop_init(PyGMainLoop *self, PyObject *args, PyObject *kwargs)
  198. {
  199. static char *kwlist[] = { "context", "is_running", NULL };
  200. PyObject *py_context = Py_None;
  201. int is_running = 0;
  202. GMainContext *context;
  203. if (!PyArg_ParseTupleAndKeywords(args, kwargs,
  204. "|Ob:GMainLoop.__init__",
  205. kwlist, &py_context, &is_running))
  206. return -1;
  207. if (!PyObject_TypeCheck(py_context, &PyGMainContext_Type) &&
  208. py_context != Py_None) {
  209. PyErr_SetString(PyExc_TypeError,
  210. "context must be a glib.MainContext or None");
  211. return -1;
  212. }
  213. if (py_context != Py_None) {
  214. context = ((PyGMainContext*)py_context)->context;
  215. } else {
  216. context = NULL;
  217. }
  218. self->loop = g_main_loop_new(context, is_running);
  219. self->signal_source = pyg_signal_watch_new();
  220. g_source_attach(self->signal_source, context);
  221. g_source_unref(self->signal_source);
  222. return 0;
  223. }
  224. static void
  225. pyg_main_loop_dealloc(PyGMainLoop *self)
  226. {
  227. if (self->signal_source != NULL) {
  228. g_source_destroy(self->signal_source);
  229. self->signal_source = NULL;
  230. }
  231. if (self->loop != NULL) {
  232. g_main_loop_unref(self->loop);
  233. self->loop = NULL;
  234. }
  235. PyObject_Del(self);
  236. }
  237. static PyObject*
  238. pyg_main_loop_richcompare(PyObject *self, PyObject *other, int op)
  239. {
  240. if (Py_TYPE(self) == Py_TYPE(other) && Py_TYPE(self) == &PyGMainLoop_Type)
  241. return _pyglib_generic_ptr_richcompare(((PyGMainLoop*)self)->loop,
  242. ((PyGMainLoop*)other)->loop,
  243. op);
  244. else {
  245. Py_INCREF(Py_NotImplemented);
  246. return Py_NotImplemented;
  247. }
  248. }
  249. static PyObject *
  250. _wrap_g_main_loop_get_context (PyGMainLoop *loop)
  251. {
  252. return pyg_main_context_new(g_main_loop_get_context(loop->loop));
  253. }
  254. static PyObject *
  255. _wrap_g_main_loop_is_running (PyGMainLoop *self)
  256. {
  257. return PyBool_FromLong(g_main_loop_is_running(self->loop));
  258. }
  259. static PyObject *
  260. _wrap_g_main_loop_quit (PyGMainLoop *self)
  261. {
  262. g_main_loop_quit(self->loop);
  263. Py_INCREF(Py_None);
  264. return Py_None;
  265. }
  266. static PyObject *
  267. _wrap_g_main_loop_run (PyGMainLoop *self)
  268. {
  269. GMainLoop *prev_loop;
  270. prev_loop = pyg_save_current_main_loop(self->loop);
  271. pyglib_begin_allow_threads;
  272. g_main_loop_run(self->loop);
  273. pyglib_end_allow_threads;
  274. pyg_restore_current_main_loop(prev_loop);
  275. if (PyErr_Occurred())
  276. return NULL;
  277. Py_INCREF(Py_None);
  278. return Py_None;
  279. }
  280. static PyMethodDef _PyGMainLoop_methods[] = {
  281. { "get_context", (PyCFunction)_wrap_g_main_loop_get_context, METH_NOARGS },
  282. { "is_running", (PyCFunction)_wrap_g_main_loop_is_running, METH_NOARGS },
  283. { "quit", (PyCFunction)_wrap_g_main_loop_quit, METH_NOARGS },
  284. { "run", (PyCFunction)_wrap_g_main_loop_run, METH_NOARGS },
  285. { NULL, NULL, 0 }
  286. };
  287. void
  288. pyglib_mainloop_register_types(PyObject *d)
  289. {
  290. PyGMainLoop_Type.tp_dealloc = (destructor)pyg_main_loop_dealloc;
  291. PyGMainLoop_Type.tp_richcompare = pyg_main_loop_richcompare;
  292. PyGMainLoop_Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
  293. PyGMainLoop_Type.tp_methods = _PyGMainLoop_methods;
  294. PyGMainLoop_Type.tp_init = (initproc)pyg_main_loop_init;
  295. PYGLIB_REGISTER_TYPE(d, PyGMainLoop_Type, "MainLoop");
  296. }