_dbus_bindings: split out conn, conn-methods into separate translation units
[dbus-python-phuang.git] / _dbus_bindings / pending-call-impl.h
blob25965d48bbbda1f13a21feb64bd2c2a4b267cb11
1 /* Implementation of PendingCall helper type for D-Bus bindings.
3 * Copyright (C) 2006 Collabora Ltd.
5 * Licensed under the Academic Free License version 2.1
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 PyDoc_STRVAR(PendingCall_tp_doc,
26 "Object representing a pending D-Bus call, returned by\n"
27 "Connection.send_message_with_reply(). Cannot be instantiated directly.\n"
30 static PyTypeObject PendingCallType;
32 static inline int PendingCall_Check (PyObject *o)
34 return (o->ob_type == &PendingCallType)
35 || PyObject_IsInstance(o, (PyObject *)&PendingCallType);
38 typedef struct {
39 PyObject_HEAD
40 DBusPendingCall *pc;
41 } PendingCall;
43 PyDoc_STRVAR(PendingCall_cancel__doc__,
44 "cancel()\n\n"
45 "Cancel this pending call. Its reply will be ignored and the associated\n"
46 "reply handler will never be called.\n");
47 static PyObject *
48 PendingCall_cancel(PendingCall *self, PyObject *unused UNUSED)
50 Py_BEGIN_ALLOW_THREADS
51 dbus_pending_call_cancel(self->pc);
52 Py_END_ALLOW_THREADS
53 Py_RETURN_NONE;
56 PyDoc_STRVAR(PendingCall_block__doc__,
57 "block()\n\n"
58 "Block until this pending call has completed and the associated\n"
59 "reply handler has been called.\n"
60 "\n"
61 "This can lead to a deadlock, if the called method tries to make a\n"
62 "synchronous call to a method in this application. As a result, it's\n"
63 "probably a bad idea.\n");
64 static PyObject *
65 PendingCall_block(PendingCall *self, PyObject *unused UNUSED)
67 Py_BEGIN_ALLOW_THREADS
68 dbus_pending_call_block(self->pc);
69 Py_END_ALLOW_THREADS
70 Py_RETURN_NONE;
73 static void
74 _pending_call_notify_function(DBusPendingCall *pc,
75 PyObject *list)
77 PyGILState_STATE gil = PyGILState_Ensure();
78 /* BEGIN CRITICAL SECTION
79 * While holding the GIL, make sure the callback only gets called once
80 * by deleting it from the 1-item list that's held by libdbus.
82 PyObject *handler = PyList_GetItem(list, 0);
83 DBusMessage *msg;
85 if (!handler) {
86 PyErr_Print();
87 goto release;
89 if (handler == Py_None) {
90 /* We've already called (and thrown away) the callback */
91 goto release;
93 Py_INCREF(handler); /* previously borrowed from the list, now owned */
94 Py_INCREF(Py_None); /* take a ref so SetItem can steal it */
95 PyList_SetItem(list, 0, Py_None);
96 /* END CRITICAL SECTION */
98 msg = dbus_pending_call_steal_reply(pc);
100 if (!msg) {
101 /* omg, what happened here? the notify should only get called
102 * when we have a reply */
103 PyErr_Warn(PyExc_UserWarning, "D-Bus notify function was called "
104 "for an incomplete pending call (shouldn't happen)");
105 } else {
106 Message *msg_obj = (Message *)Message_ConsumeDBusMessage(msg);
108 if (msg_obj) {
109 PyObject *ret = PyObject_CallFunctionObjArgs(handler, msg_obj, NULL);
111 if (!ret) {
112 PyErr_Print();
114 Py_XDECREF(ret);
116 /* else OOM has happened - not a lot we can do about that,
117 * except possibly making it fatal (FIXME?) */
120 Py_XDECREF(handler);
121 release:
122 PyGILState_Release(gil);
125 PyDoc_STRVAR(PendingCall_get_completed__doc__,
126 "get_completed() -> bool\n\n"
127 "Return true if this pending call has completed.\n\n"
128 "If so, its associated reply handler has been called and it is no\n"
129 "longer meaningful to cancel it.\n");
130 static PyObject *
131 PendingCall_get_completed(PendingCall *self, PyObject *unused UNUSED)
133 dbus_bool_t ret;
135 Py_BEGIN_ALLOW_THREADS
136 ret = dbus_pending_call_get_completed(self->pc);
137 Py_END_ALLOW_THREADS
138 return PyBool_FromLong(ret);
141 /* Steals the reference to the pending call. */
142 PyObject *
143 DBusPyPendingCall_ConsumeDBusPendingCall(DBusPendingCall *pc,
144 PyObject *callable)
146 dbus_bool_t ret;
147 PyObject *list = PyList_New(1);
148 PendingCall *self = PyObject_New(PendingCall, &PendingCallType);
150 if (!list || !self) {
151 Py_XDECREF(list);
152 Py_XDECREF(self);
153 Py_BEGIN_ALLOW_THREADS
154 dbus_pending_call_cancel(pc);
155 dbus_pending_call_unref(pc);
156 Py_END_ALLOW_THREADS
157 return NULL;
160 /* INCREF because SET_ITEM steals a ref */
161 Py_INCREF(callable);
162 PyList_SET_ITEM(list, 0, callable);
164 /* INCREF so we can give a ref to set_notify and still have one */
165 Py_INCREF(list);
167 Py_BEGIN_ALLOW_THREADS
168 ret = dbus_pending_call_set_notify(pc,
169 (DBusPendingCallNotifyFunction)_pending_call_notify_function,
170 (void *)list, (DBusFreeFunction)dbus_py_take_gil_and_xdecref);
171 Py_END_ALLOW_THREADS
173 if (!ret) {
174 PyErr_NoMemory();
175 /* DECREF twice - one for the INCREF and one for the allocation */
176 Py_DECREF(list);
177 Py_DECREF(list);
178 Py_DECREF(self);
179 Py_BEGIN_ALLOW_THREADS
180 dbus_pending_call_cancel(pc);
181 dbus_pending_call_unref(pc);
182 Py_END_ALLOW_THREADS
183 return NULL;
186 /* As Alexander Larsson pointed out on dbus@lists.fd.o on 2006-11-30,
187 * the API has a race condition if set_notify runs in one thread and a
188 * mail loop runs in another - if the reply gets in before set_notify
189 * runs, the notify isn't called and there is no indication of error.
191 * The workaround is to check for completion immediately, but this also
192 * has a race which might lead to getting the notify called twice if
193 * we're unlucky. So I use the list to arrange for the notify to be
194 * deleted before it's called for the second time. The GIL protects
195 * the critical section in which I delete the callback from the list.
197 if (dbus_pending_call_get_completed(pc)) {
198 /* the first race condition happened, so call the callable here.
199 * FIXME: we ought to arrange for the callable to run from the
200 * mainloop thread, like it would if the race hadn't happened...
201 * this needs a better mainloop abstraction, though.
203 _pending_call_notify_function(pc, list);
206 Py_DECREF(list);
207 self->pc = pc;
208 return (PyObject *)self;
211 static void
212 PendingCall_tp_dealloc (PendingCall *self)
214 if (self->pc) {
215 Py_BEGIN_ALLOW_THREADS
216 dbus_pending_call_unref(self->pc);
217 Py_END_ALLOW_THREADS
219 PyObject_Del (self);
222 static PyMethodDef PendingCall_tp_methods[] = {
223 {"block", (PyCFunction)PendingCall_block, METH_NOARGS,
224 PendingCall_block__doc__},
225 {"cancel", (PyCFunction)PendingCall_cancel, METH_NOARGS,
226 PendingCall_cancel__doc__},
227 {"get_completed", (PyCFunction)PendingCall_get_completed, METH_NOARGS,
228 PendingCall_get_completed__doc__},
229 {NULL, NULL, 0, NULL}
232 static PyTypeObject PendingCallType = {
233 PyObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type))
235 "dbus.lowlevel.PendingCall",
236 sizeof(PendingCall),
238 (destructor)PendingCall_tp_dealloc, /* tp_dealloc */
239 0, /* tp_print */
240 0, /* tp_getattr */
241 0, /* tp_setattr */
242 0, /* tp_compare */
243 0, /* tp_repr */
244 0, /* tp_as_number */
245 0, /* tp_as_sequence */
246 0, /* tp_as_mapping */
247 0, /* tp_hash */
248 0, /* tp_call */
249 0, /* tp_str */
250 0, /* tp_getattro */
251 0, /* tp_setattro */
252 0, /* tp_as_buffer */
253 Py_TPFLAGS_DEFAULT, /* tp_flags */
254 PendingCall_tp_doc, /* tp_doc */
255 0, /* tp_traverse */
256 0, /* tp_clear */
257 0, /* tp_richcompare */
258 0, /* tp_weaklistoffset */
259 0, /* tp_iter */
260 0, /* tp_iternext */
261 PendingCall_tp_methods, /* tp_methods */
262 0, /* tp_members */
263 0, /* tp_getset */
264 0, /* tp_base */
265 0, /* tp_dict */
266 0, /* tp_descr_get */
267 0, /* tp_descr_set */
268 0, /* tp_dictoffset */
269 0, /* tp_init */
270 0, /* tp_alloc */
271 /* deliberately not callable! Use PendingCall_ConsumeDBusPendingCall */
272 0, /* tp_new */
275 static inline int
276 init_pending_call (void)
278 if (PyType_Ready (&PendingCallType) < 0) return 0;
279 return 1;
282 static inline int
283 insert_pending_call (PyObject *this_module)
285 if (PyModule_AddObject (this_module, "PendingCall",
286 (PyObject *)&PendingCallType) < 0) return 0;
287 return 1;
290 /* vim:set ft=c cino< sw=4 sts=4 et: */