Imported Upstream version 3.4.0
[platform/upstream/python-gobject.git] / gi / _glib / pygmainloop.c
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
24 #ifdef HAVE_CONFIG_H
25 #  include <config.h>
26 #endif
27 #include <fcntl.h>
28
29 #include <Python.h>
30 #include <pythread.h>
31 #include <glib.h>
32
33 #include "pygmainloop.h"
34 #include "pygmaincontext.h"
35 #include "pyglib.h"
36 #include "pyglib-private.h"
37
38 static int pipe_fds[2];
39
40 typedef struct {
41     GSource source;
42     GPollFD fd;
43 } PySignalWatchSource;
44
45 #ifdef DISABLE_THREADING
46 static GMainLoop *pyg_current_main_loop = NULL;;
47
48 static inline GMainLoop *
49 pyg_save_current_main_loop (GMainLoop *main_loop)
50 {
51     GMainLoop *retval = pyg_current_main_loop;
52
53     g_return_val_if_fail(main_loop != NULL, NULL);
54
55     pyg_current_main_loop = g_main_loop_ref(main_loop);
56
57     return retval;
58 }
59
60 static inline void
61 pyg_restore_current_main_loop (GMainLoop *main_loop)
62 {
63     if (pyg_current_main_loop != NULL)
64         g_main_loop_unref(pyg_current_main_loop);
65     pyg_current_main_loop = main_loop;
66 }
67
68 static inline GMainLoop *
69 pyg_get_current_main_loop (void)
70 {
71     return pyg_current_main_loop;
72 }
73 #else /* !defined(#ifndef DISABLE_THREADING) */
74
75 static int pyg_current_main_loop_key = -1;
76
77 static inline GMainLoop *
78 pyg_save_current_main_loop (GMainLoop *main_loop)
79 {
80     GMainLoop *retval;
81
82     g_return_val_if_fail(main_loop != NULL, NULL);
83
84     if (pyg_current_main_loop_key == -1)
85         pyg_current_main_loop_key = PyThread_create_key();
86
87     retval = PyThread_get_key_value(pyg_current_main_loop_key);
88     PyThread_delete_key_value(pyg_current_main_loop_key);
89     PyThread_set_key_value(pyg_current_main_loop_key, 
90                            g_main_loop_ref(main_loop));
91
92     return retval;
93 }
94
95 static inline void
96 pyg_restore_current_main_loop (GMainLoop *main_loop)
97 {
98     GMainLoop *prev;
99
100     g_return_if_fail (pyg_current_main_loop_key != -1);
101
102     prev = PyThread_get_key_value(pyg_current_main_loop_key);
103     if (prev != NULL)
104         g_main_loop_unref(prev);
105     PyThread_delete_key_value(pyg_current_main_loop_key);
106     if (main_loop != NULL)
107         PyThread_set_key_value(pyg_current_main_loop_key, main_loop);
108 }
109
110 static inline GMainLoop *
111 pyg_get_current_main_loop (void)
112 {
113     if (pyg_current_main_loop_key == -1)
114         return NULL;
115     return PyThread_get_key_value(pyg_current_main_loop_key);
116 }
117 #endif /* DISABLE_THREADING */
118
119 static gboolean
120 pyg_signal_watch_prepare(GSource *source,
121                          int     *timeout)
122 {
123     /* Python only invokes signal handlers from the main thread,
124      * so if a thread other than the main thread receives the signal
125      * from the kernel, PyErr_CheckSignals() from that thread will
126      * do nothing.
127      */
128
129 #ifdef HAVE_PYSIGNAL_SETWAKEUPFD
130     return FALSE;
131 #else /* !HAVE_PYSIGNAL_SETWAKEUPFD */
132     /* On Windows g_poll() won't be interrupted by a signal
133      * (AFAIK), so we need the timeout there too, even if there's
134      * only one thread.
135      */
136 #ifndef PLATFORM_WIN32
137     if (!pyglib_threads_enabled())
138         return FALSE;
139 #endif /* PLATFORM_WIN32 */
140
141     /* If we're using 2.5 or an earlier version of python we
142      * will default to a timeout every second, be aware,
143      * this will cause unnecessary wakeups, see
144      * http://bugzilla.gnome.org/show_bug.cgi?id=481569
145      */
146     *timeout = 1000;
147     return FALSE;
148 #endif /* HAVE_PYSIGNAL_SETWAKEUPFD */
149 }
150
151 static gboolean
152 pyg_signal_watch_check(GSource *source)
153 {
154     PyGILState_STATE state;
155     GMainLoop *main_loop;
156
157 #ifdef HAVE_PYSIGNAL_SETWAKEUPFD
158     PySignalWatchSource *real_source = (PySignalWatchSource *)source;
159     GPollFD *poll_fd = &real_source->fd;
160     unsigned char dummy;
161     if (poll_fd->revents & G_IO_IN)
162         (void) read(poll_fd->fd, &dummy, 1);
163 #endif
164
165     state = pyglib_gil_state_ensure();
166
167     main_loop = pyg_get_current_main_loop();
168
169     if (PyErr_CheckSignals() == -1 && main_loop != NULL) {
170         PyErr_SetNone(PyExc_KeyboardInterrupt);
171         g_main_loop_quit(main_loop);
172     }
173
174     pyglib_gil_state_release(state);
175
176     return FALSE;
177 }
178
179 static gboolean
180 pyg_signal_watch_dispatch(GSource     *source,
181                           GSourceFunc  callback,
182                           gpointer     user_data)
183 {
184     /* We should never be dispatched */
185     g_assert_not_reached();
186     return TRUE;
187 }
188
189 static GSourceFuncs pyg_signal_watch_funcs =
190 {
191     pyg_signal_watch_prepare,
192     pyg_signal_watch_check,
193     pyg_signal_watch_dispatch
194 };
195
196 static GSource *
197 pyg_signal_watch_new(void)
198 {
199     GSource *source = g_source_new(&pyg_signal_watch_funcs,
200         sizeof(PySignalWatchSource));
201
202 #ifdef HAVE_PYSIGNAL_SETWAKEUPFD
203     PySignalWatchSource *real_source = (PySignalWatchSource *)source;
204     int flag;
205
206     /* Unfortunately we need to create a new pipe here instead of
207      * reusing the pipe inside the GMainContext.
208      * Ideally an api should be added to GMainContext which allows us
209      * to reuse that pipe which would suit us perfectly fine.
210      * XXX More efficient than a pipe, we could use an eventfd on Linux
211      * kernels that support it.
212      */
213     gint already_piped = (pipe_fds[0] > 0);
214     if (!already_piped) {
215         if (pipe(pipe_fds) < 0)
216             g_error("Cannot create main loop pipe: %s\n",
217                     g_strerror(errno));
218
219         /* Make both ends of the fd non blocking */
220         flag = fcntl(pipe_fds[0], F_GETFL, 0);
221         flag |= O_NONBLOCK;
222         fcntl(pipe_fds[0], F_SETFL, flag);
223         flag = fcntl(pipe_fds[1], F_GETFL, 0);
224         flag |= O_NONBLOCK;
225         fcntl(pipe_fds[1], F_SETFL, flag);
226     }
227
228     real_source->fd.fd = pipe_fds[0];
229     real_source->fd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
230     g_source_add_poll(source, &real_source->fd);
231
232     if (!already_piped)
233       PySignal_SetWakeupFd(pipe_fds[1]);
234 #endif
235     return source;
236 }
237
238 PYGLIB_DEFINE_TYPE("gi._glib.MainLoop", PyGMainLoop_Type, PyGMainLoop)
239
240 static int
241 pyg_main_loop_init(PyGMainLoop *self, PyObject *args, PyObject *kwargs)
242 {
243     static char *kwlist[] = { "context", "is_running", NULL };
244     PyObject *py_context = Py_None;
245     int is_running = 0;
246     GMainContext *context;
247     
248     if (!PyArg_ParseTupleAndKeywords(args, kwargs,
249                                      "|Ob:GMainLoop.__init__",
250                                      kwlist, &py_context, &is_running))
251         return -1;
252
253     if (!PyObject_TypeCheck(py_context, &PyGMainContext_Type) &&
254         py_context != Py_None) {
255         PyErr_SetString(PyExc_TypeError,
256                         "context must be a gi._glib.MainContext or None");
257         return -1;
258     }
259
260     if (py_context != Py_None) {
261         context = ((PyGMainContext*)py_context)->context;
262     } else {
263         context = NULL;
264     }
265
266     self->loop = g_main_loop_new(context, is_running);
267
268     self->signal_source = pyg_signal_watch_new();
269     g_source_attach(self->signal_source, context);
270     g_source_unref(self->signal_source);
271
272     return 0;
273 }
274
275 static void
276 pyg_main_loop_dealloc(PyGMainLoop *self)
277 {
278     if (self->signal_source != NULL) {
279         g_source_destroy(self->signal_source);
280         self->signal_source = NULL;
281     }
282
283     if (self->loop != NULL) {
284         g_main_loop_unref(self->loop);
285         self->loop = NULL;
286     }
287
288     PyObject_Del(self);
289 }
290
291 static PyObject*
292 pyg_main_loop_richcompare(PyObject *self, PyObject *other, int op)
293 {
294     if (Py_TYPE(self) == Py_TYPE(other) && Py_TYPE(self) == &PyGMainLoop_Type)
295         return _pyglib_generic_ptr_richcompare(((PyGMainLoop*)self)->loop,
296                                                ((PyGMainLoop*)other)->loop,
297                                                op);
298     else {
299        Py_INCREF(Py_NotImplemented);
300        return Py_NotImplemented;
301     }
302 }
303
304 static PyObject *
305 _wrap_g_main_loop_get_context (PyGMainLoop *loop)
306 {
307     return pyg_main_context_new(g_main_loop_get_context(loop->loop));
308 }
309
310 static PyObject *
311 _wrap_g_main_loop_is_running (PyGMainLoop *self)
312 {
313     return PyBool_FromLong(g_main_loop_is_running(self->loop));
314 }
315
316 static PyObject *
317 _wrap_g_main_loop_quit (PyGMainLoop *self, PyObject *args, PyObject *kwargs)
318 {
319     g_main_loop_quit(self->loop);
320     
321     Py_INCREF(Py_None);
322     return Py_None;
323 }
324
325 static PyObject *
326 _wrap_g_main_loop_run (PyGMainLoop *self)
327 {
328     GMainLoop *prev_loop;
329
330     prev_loop = pyg_save_current_main_loop(self->loop);
331
332     pyglib_begin_allow_threads;
333     g_main_loop_run(self->loop);
334     pyglib_end_allow_threads;
335
336     pyg_restore_current_main_loop(prev_loop);
337    
338     if (PyErr_Occurred())
339         return NULL;
340
341     Py_INCREF(Py_None);
342     return Py_None;
343 }
344
345 static PyMethodDef _PyGMainLoop_methods[] = {
346     { "get_context", (PyCFunction)_wrap_g_main_loop_get_context, METH_NOARGS },
347     { "is_running", (PyCFunction)_wrap_g_main_loop_is_running, METH_NOARGS },
348     { "quit", (PyCFunction)_wrap_g_main_loop_quit, METH_VARARGS|METH_KEYWORDS },
349     { "run", (PyCFunction)_wrap_g_main_loop_run, METH_NOARGS },
350     { NULL, NULL, 0 }
351 };
352
353 void
354 pyglib_mainloop_register_types(PyObject *d)
355 {
356     PyGMainLoop_Type.tp_dealloc = (destructor)pyg_main_loop_dealloc;
357     PyGMainLoop_Type.tp_richcompare = pyg_main_loop_richcompare;
358     PyGMainLoop_Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
359     PyGMainLoop_Type.tp_methods = _PyGMainLoop_methods;
360     PyGMainLoop_Type.tp_init = (initproc)pyg_main_loop_init;
361     PYGLIB_REGISTER_TYPE(d, PyGMainLoop_Type, "MainLoop"); 
362 }