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 library is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation; either version 2.1 of the License, or
10 * (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include "dbus_bindings-internal.h"
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
);
43 PyDoc_STRVAR(PendingCall_cancel__doc__
,
45 "Cancel this pending call. Its reply will be ignored and the associated\n"
46 "reply handler will never be called.\n");
48 PendingCall_cancel(PendingCall
*self
, PyObject
*unused UNUSED
)
50 Py_BEGIN_ALLOW_THREADS
51 dbus_pending_call_cancel(self
->pc
);
56 PyDoc_STRVAR(PendingCall_block__doc__
,
58 "Block until this pending call has completed and the associated\n"
59 "reply handler has been called.\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.\n");
64 PendingCall_block(PendingCall
*self
, PyObject
*unused UNUSED
)
66 Py_BEGIN_ALLOW_THREADS
67 dbus_pending_call_block(self
->pc
);
73 _pending_call_notify_function(DBusPendingCall
*pc
,
76 PyGILState_STATE gil
= PyGILState_Ensure();
77 /* BEGIN CRITICAL SECTION
78 * While holding the GIL, make sure the callback only gets called once
79 * by deleting it from the 1-item list that's held by libdbus.
81 PyObject
*handler
= PyList_GetItem(list
, 0);
88 if (handler
== Py_None
) {
89 /* We've already called (and thrown away) the callback */
92 Py_INCREF(handler
); /* previously borrowed from the list, now owned */
93 Py_INCREF(Py_None
); /* take a ref so SetItem can steal it */
94 PyList_SetItem(list
, 0, Py_None
);
95 /* END CRITICAL SECTION */
97 msg
= dbus_pending_call_steal_reply(pc
);
100 /* omg, what happened here? the notify should only get called
101 * when we have a reply */
102 PyErr_Warn(PyExc_UserWarning
, "D-Bus notify function was called "
103 "for an incomplete pending call (shouldn't happen)");
105 PyObject
*msg_obj
= DBusPyMessage_ConsumeDBusMessage(msg
);
108 PyObject
*ret
= PyObject_CallFunctionObjArgs(handler
, msg_obj
, NULL
);
116 /* else OOM has happened - not a lot we can do about that,
117 * except possibly making it fatal (FIXME?) */
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");
131 PendingCall_get_completed(PendingCall
*self
, PyObject
*unused UNUSED
)
135 Py_BEGIN_ALLOW_THREADS
136 ret
= dbus_pending_call_get_completed(self
->pc
);
138 return PyBool_FromLong(ret
);
141 /* Steals the reference to the pending call. */
143 DBusPyPendingCall_ConsumeDBusPendingCall(DBusPendingCall
*pc
,
147 PyObject
*list
= PyList_New(1);
148 PendingCall
*self
= PyObject_New(PendingCall
, &PendingCallType
);
150 if (!list
|| !self
) {
153 Py_BEGIN_ALLOW_THREADS
154 dbus_pending_call_cancel(pc
);
155 dbus_pending_call_unref(pc
);
160 /* INCREF because SET_ITEM steals a ref */
162 PyList_SET_ITEM(list
, 0, callable
);
164 /* INCREF so we can give a ref to set_notify and still have one */
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
);
175 /* DECREF twice - one for the INCREF and one for the allocation */
179 Py_BEGIN_ALLOW_THREADS
180 dbus_pending_call_cancel(pc
);
181 dbus_pending_call_unref(pc
);
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
);
208 return (PyObject
*)self
;
212 PendingCall_tp_dealloc (PendingCall
*self
)
215 Py_BEGIN_ALLOW_THREADS
216 dbus_pending_call_unref(self
->pc
);
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",
238 (destructor
)PendingCall_tp_dealloc
, /* tp_dealloc */
244 0, /* tp_as_number */
245 0, /* tp_as_sequence */
246 0, /* tp_as_mapping */
252 0, /* tp_as_buffer */
253 Py_TPFLAGS_DEFAULT
, /* tp_flags */
254 PendingCall_tp_doc
, /* tp_doc */
257 0, /* tp_richcompare */
258 0, /* tp_weaklistoffset */
261 PendingCall_tp_methods
, /* tp_methods */
266 0, /* tp_descr_get */
267 0, /* tp_descr_set */
268 0, /* tp_dictoffset */
271 /* deliberately not callable! Use PendingCall_ConsumeDBusPendingCall */
276 dbus_py_init_pending_call (void)
278 if (PyType_Ready (&PendingCallType
) < 0) return 0;
283 dbus_py_insert_pending_call (PyObject
*this_module
)
285 if (PyModule_AddObject (this_module
, "PendingCall",
286 (PyObject
*)&PendingCallType
) < 0) return 0;
290 /* vim:set ft=c cino< sw=4 sts=4 et: */