_dbus_bindings/conn-impl.h: Be more careful that referenced Connections always have...
[dbus-python-phuang.git] / _dbus_bindings / conn-impl.h
blob4396a0419f0aa57fe5d835ca77ff66973aa7e41d
1 /* Implementation of the _dbus_bindings Connection type, a Python wrapper
2 * for DBusConnection. See also conn-methods-impl.h.
4 * Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/>
6 * Licensed under the Academic Free License version 2.1
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 /* Connection definition ============================================ */
28 PyDoc_STRVAR(Connection_tp_doc,
29 "A D-Bus connection.\n\n"
30 "Connection(address: str, mainloop=None) -> Connection\n"
33 typedef struct Connection {
34 PyObject_HEAD
35 DBusConnection *conn;
36 /* A list of filter callbacks. */
37 PyObject *filters;
38 /* A dict mapping object paths to one of:
39 * - tuples (unregister_callback or None, message_callback)
40 * - None (meaning unregistration from libdbus is in progress and nobody
41 * should touch this entry til we're finished)
43 PyObject *object_paths;
45 PyObject *weaklist;
46 } Connection;
48 static PyTypeObject ConnectionType;
50 static inline int Connection_Check(PyObject *o)
52 return PyObject_TypeCheck(o, &ConnectionType);
55 /* Helpers ========================================================== */
57 static PyObject *Connection_ExistingFromDBusConnection(DBusConnection *);
58 static PyObject *Connection_GetObjectPathHandlers(Connection *, PyObject *);
59 static DBusHandlerResult Connection_HandleMessage(Connection *, Message *,
60 PyObject *);
62 static void
63 _object_path_unregister(DBusConnection *conn, void *user_data)
65 PyGILState_STATE gil = PyGILState_Ensure();
66 PyObject *tuple = NULL;
67 Connection *conn_obj = NULL;
68 PyObject *callable;
70 conn_obj = (Connection *)Connection_ExistingFromDBusConnection(conn);
71 if (!conn_obj) goto out;
73 DBG("Connection at %p unregistering object path %s",
74 conn_obj, PyString_AS_STRING((PyObject *)user_data));
75 tuple = Connection_GetObjectPathHandlers(conn_obj, (PyObject *)user_data);
76 if (!tuple) goto out;
77 if (tuple == Py_None) goto out;
79 DBG("%s", "... yes we have handlers for that object path");
81 /* 0'th item is the unregisterer (if that's a word) */
82 callable = PyTuple_GetItem(tuple, 0);
83 if (callable && callable != Py_None) {
84 DBG("%s", "... and we even have an unregisterer");
85 /* any return from the unregisterer is ignored */
86 Py_XDECREF(PyObject_CallFunctionObjArgs(callable, conn_obj, NULL));
88 out:
89 Py_XDECREF(conn_obj);
90 Py_XDECREF(tuple);
91 /* the user_data (a Python str) is no longer ref'd by the DBusConnection */
92 Py_XDECREF((PyObject *)user_data);
93 if (PyErr_Occurred()) {
94 PyErr_Print();
96 PyGILState_Release(gil);
99 static DBusHandlerResult
100 _object_path_message(DBusConnection *conn, DBusMessage *message,
101 void *user_data)
103 DBusHandlerResult ret;
104 PyGILState_STATE gil = PyGILState_Ensure();
105 Connection *conn_obj = NULL;
106 PyObject *tuple = NULL;
107 Message *msg_obj;
108 PyObject *callable; /* borrowed */
110 dbus_message_ref(message);
111 msg_obj = (Message *)Message_ConsumeDBusMessage(message);
112 if (!msg_obj) {
113 ret = DBUS_HANDLER_RESULT_NEED_MEMORY;
114 goto out;
117 conn_obj = (Connection *)Connection_ExistingFromDBusConnection(conn);
118 if (!conn_obj) {
119 ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
120 goto out;
123 DBG("Connection at %p messaging object path %s",
124 conn_obj, PyString_AS_STRING((PyObject *)user_data));
125 DBG_DUMP_MESSAGE(message);
126 tuple = Connection_GetObjectPathHandlers(conn_obj, (PyObject *)user_data);
127 if (!tuple || tuple == Py_None) {
128 ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
129 goto out;
132 DBG("%s", "... yes we have handlers for that object path");
134 /* 1st item (0-based) is the message callback */
135 callable = PyTuple_GetItem(tuple, 1);
136 if (!callable) {
137 DBG("%s", "... error getting message handler from tuple");
138 ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
140 else if (callable == Py_None) {
141 /* there was actually no handler after all */
142 DBG("%s", "... but those handlers don't do messages");
143 ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
145 else {
146 DBG("%s", "... and we have a message handler for that object path");
147 ret = Connection_HandleMessage(conn_obj, msg_obj, callable);
150 out:
151 Py_XDECREF(msg_obj);
152 Py_XDECREF(conn_obj);
153 Py_XDECREF(tuple);
154 if (PyErr_Occurred()) {
155 PyErr_Print();
157 PyGILState_Release(gil);
158 return ret;
161 static const DBusObjectPathVTable _object_path_vtable = {
162 _object_path_unregister,
163 _object_path_message,
166 static DBusHandlerResult
167 _filter_message(DBusConnection *conn, DBusMessage *message, void *user_data)
169 DBusHandlerResult ret;
170 PyGILState_STATE gil = PyGILState_Ensure();
171 Connection *conn_obj = NULL;
172 PyObject *callable = NULL;
173 Message *msg_obj;
174 #ifndef DBUS_PYTHON_DISABLE_CHECKS
175 int i, size;
176 #endif
178 dbus_message_ref(message);
179 msg_obj = (Message *)Message_ConsumeDBusMessage(message);
180 if (!msg_obj) {
181 DBG("%s", "OOM while trying to construct Message");
182 ret = DBUS_HANDLER_RESULT_NEED_MEMORY;
183 goto out;
186 conn_obj = (Connection *)Connection_ExistingFromDBusConnection(conn);
187 if (!conn_obj) {
188 DBG("%s", "failed to traverse DBusConnection -> Connection weakref");
189 ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
190 goto out;
193 /* The user_data is a pointer to a Python object. To avoid
194 * cross-library reference cycles, the DBusConnection isn't allowed
195 * to reference it. However, as long as the Connection is still
196 * alive, its ->filters list owns a reference to the same Python
197 * object, so the object should also still be alive.
199 * To ensure that this works, be careful whenever manipulating the
200 * filters list! (always put things in the list *before* giving
201 * them to libdbus, etc.)
203 #ifdef DBUS_PYTHON_DISABLE_CHECKS
204 callable = (PyObject *)user_data;
205 #else
206 size = PyList_GET_SIZE(conn_obj->filters);
207 for (i = 0; i < size; i++) {
208 callable = PyList_GET_ITEM(conn_obj->filters, i);
209 if (callable == user_data) {
210 Py_INCREF(callable);
212 else {
213 callable = NULL;
217 if (!callable) {
218 DBG("... filter %p has vanished from ->filters, so not calling it",
219 user_data);
220 ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
221 goto out;
223 #endif
225 ret = Connection_HandleMessage(conn_obj, msg_obj, callable);
226 out:
227 Py_XDECREF(msg_obj);
228 Py_XDECREF(conn_obj);
229 Py_XDECREF(callable);
230 PyGILState_Release(gil);
231 return ret;
234 /* D-Bus Connection user data slot, containing an owned reference to either
235 * the Connection, or a weakref to the Connection.
237 static dbus_int32_t _connection_python_slot;
239 /* C API for main-loop hooks ======================================== */
241 /* Return a borrowed reference to the DBusConnection which underlies this
242 * Connection. */
243 static DBusConnection *
244 Connection_BorrowDBusConnection(PyObject *self)
246 DBusConnection *dbc;
248 if (!Connection_Check(self)) {
249 PyErr_SetString(PyExc_TypeError, "A dbus.Connection is required");
250 return NULL;
252 dbc = ((Connection *)self)->conn;
253 if (!dbc) {
254 PyErr_SetString(PyExc_RuntimeError, "Connection is in an invalid "
255 "state: no DBusConnection");
256 return NULL;
258 return dbc;
261 /* Internal C API =================================================== */
263 /* Pass a message through a handler. */
264 static DBusHandlerResult
265 Connection_HandleMessage(Connection *conn, Message *msg, PyObject *callable)
267 PyObject *obj = PyObject_CallFunctionObjArgs(callable, conn, msg,
268 NULL);
269 if (obj == Py_None) {
270 DBG("%p: OK, handler %p returned None", conn, callable);
271 Py_DECREF(obj);
272 return DBUS_HANDLER_RESULT_HANDLED;
274 else if (obj == Py_NotImplemented) {
275 DBG("%p: handler %p returned NotImplemented, continuing",
276 conn, callable);
277 Py_DECREF(obj);
278 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
280 else if (!obj) {
281 if (PyErr_ExceptionMatches(PyExc_MemoryError)) {
282 DBG_EXC("%p: handler %p caused OOM", conn, callable);
283 PyErr_Clear();
284 return DBUS_HANDLER_RESULT_NEED_MEMORY;
286 DBG_EXC("%p: handler %p raised exception", conn, callable);
287 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
289 else {
290 long i = PyInt_AsLong(obj);
291 DBG("%p: handler %p returned %ld", conn, callable, i);
292 Py_DECREF(obj);
293 if (i == -1 && PyErr_Occurred()) {
294 PyErr_SetString(PyExc_TypeError, "Return from D-Bus message "
295 "handler callback should be None, "
296 "NotImplemented or integer");
297 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
299 else if (i == DBUS_HANDLER_RESULT_HANDLED ||
300 i == DBUS_HANDLER_RESULT_NOT_YET_HANDLED ||
301 i == DBUS_HANDLER_RESULT_NEED_MEMORY) {
302 return i;
304 else {
305 PyErr_Format(PyExc_ValueError, "Integer return from "
306 "D-Bus message handler callback should "
307 "be a DBUS_HANDLER_RESULT_... constant, "
308 "not %d", (int)i);
309 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
314 /* On KeyError or if unregistration is in progress, return None. */
315 static PyObject *
316 Connection_GetObjectPathHandlers(Connection *self, PyObject *path)
318 PyObject *callbacks = PyDict_GetItem(self->object_paths, path);
319 if (!callbacks) {
320 if (PyErr_ExceptionMatches(PyExc_KeyError)) {
321 PyErr_Clear();
322 Py_RETURN_NONE;
325 Py_INCREF(callbacks);
326 return callbacks;
329 /* Return a new reference to a Python Connection or subclass corresponding
330 * to the DBusConnection conn. For use in callbacks.
332 * Raises AssertionError if the DBusConnection does not have a Connection.
334 static PyObject *
335 Connection_ExistingFromDBusConnection(DBusConnection *conn)
337 PyObject *self, *ref;
339 Py_BEGIN_ALLOW_THREADS
340 ref = (PyObject *)dbus_connection_get_data(conn,
341 _connection_python_slot);
342 Py_END_ALLOW_THREADS
343 if (ref) {
344 self = PyWeakref_GetObject(ref); /* still a borrowed ref */
345 if (self && self != Py_None && Connection_Check(self)) {
346 Py_INCREF(self);
347 return self;
351 PyErr_SetString(PyExc_AssertionError,
352 "D-Bus connection does not have a Connection "
353 "instance associated with it");
354 return NULL;
357 /* Return a new reference to a Python Connection or subclass (given by cls)
358 * corresponding to the DBusConnection conn, which must have been newly
359 * created. For use by the Connection and Bus constructors.
361 * Raises AssertionError if the DBusConnection already has a Connection.
363 static PyObject *
364 Connection_NewConsumingDBusConnection(PyTypeObject *cls,
365 DBusConnection *conn,
366 PyObject *mainloop)
368 Connection *self = NULL;
369 PyObject *ref;
370 dbus_bool_t ok;
372 Py_BEGIN_ALLOW_THREADS
373 ref = (PyObject *)dbus_connection_get_data(conn,
374 _connection_python_slot);
375 Py_END_ALLOW_THREADS
376 if (ref) {
377 self = (Connection *)PyWeakref_GetObject(ref);
378 ref = NULL;
379 if (self && (PyObject *)self != Py_None) {
380 self = NULL;
381 PyErr_SetString(PyExc_AssertionError,
382 "Newly created D-Bus connection already has a "
383 "Connection instance associated with it");
384 return NULL;
387 ref = NULL;
389 if (!mainloop || mainloop == Py_None) {
390 mainloop = default_main_loop;
391 if (!mainloop || mainloop == Py_None) {
392 PyErr_SetString(PyExc_ValueError,
393 "D-Bus connections must be attached to a main "
394 "loop by passing mainloop=... to the constructor "
395 "or calling dbus.Bus.set_default_main_loop(...)");
396 goto err;
399 /* Make sure there's a ref to the main loop (in case someone changes the
400 * default) */
401 Py_INCREF(mainloop);
403 DBG("Constructing Connection from DBusConnection at %p", conn);
405 self = (Connection *)(cls->tp_alloc(cls, 0));
406 if (!self) goto err;
408 self->conn = NULL;
409 self->filters = PyList_New(0);
410 if (!self->filters) goto err;
411 self->object_paths = PyDict_New();
412 if (!self->object_paths) goto err;
414 ref = PyWeakref_NewRef((PyObject *)self, NULL);
415 if (!ref) goto err;
417 Py_BEGIN_ALLOW_THREADS
418 ok = dbus_connection_set_data(conn, _connection_python_slot,
419 (void *)ref,
420 (DBusFreeFunction)Glue_TakeGILAndXDecref);
421 Py_END_ALLOW_THREADS
423 if (!ok) {
424 PyErr_NoMemory();
425 goto err;
428 self->conn = conn;
430 if (!dbus_python_set_up_connection((PyObject *)self, mainloop)) {
431 goto err;
434 Py_DECREF(mainloop);
436 return (PyObject *)self;
438 err:
439 DBG("Failed to construct Connection from DBusConnection at %p", conn);
440 Py_XDECREF(mainloop);
441 Py_XDECREF(self);
442 Py_XDECREF(ref);
443 if (conn) {
444 Py_BEGIN_ALLOW_THREADS
445 dbus_connection_close(conn);
446 dbus_connection_unref(conn);
447 Py_END_ALLOW_THREADS
449 return NULL;
452 /* Connection type-methods ========================================== */
454 /* "Constructor" (the real constructor is Connection_NewFromDBusConnection,
455 * to which this delegates). */
456 static PyObject *
457 Connection_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs)
459 DBusConnection *conn;
460 const char *address;
461 DBusError error;
462 PyObject *self, *mainloop = NULL;
463 static char *argnames[] = {"address", "mainloop", NULL};
465 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|O", argnames,
466 &address, &mainloop)) {
467 return NULL;
470 dbus_error_init(&error);
472 /* We always open a private connection (at the libdbus level). Sharing
473 * is done in Python, to keep things simple. */
474 Py_BEGIN_ALLOW_THREADS
475 conn = dbus_connection_open_private(address, &error);
476 Py_END_ALLOW_THREADS
478 if (!conn) {
479 DBusException_ConsumeError(&error);
480 return NULL;
482 self = Connection_NewConsumingDBusConnection(cls, conn, mainloop);
484 return self;
487 /* Destructor */
488 static void Connection_tp_dealloc(Connection *self)
490 DBusConnection *conn = self->conn;
491 PyObject *filters = self->filters;
492 PyObject *object_paths = self->object_paths;
494 DBG("Deallocating Connection at %p (DBusConnection at %p)", self, conn);
496 self->filters = NULL;
497 Py_XDECREF(filters);
498 self->object_paths = NULL;
499 Py_XDECREF(object_paths);
501 /* make sure to do this last to preserve the invariant that
502 * self->conn is always non-NULL for any referenced Connection
503 * (until the filters and object paths were freed, we might have been
504 * in a reference cycle!)
506 self->conn = NULL;
507 if (conn) {
508 Py_BEGIN_ALLOW_THREADS
509 dbus_connection_close(conn);
510 Py_END_ALLOW_THREADS
512 dbus_connection_unref(conn);
515 (self->ob_type->tp_free)((PyObject *)self);
518 /* Connection_tp_methods */
519 #include "conn-methods-impl.h"
521 /* Connection type object =========================================== */
523 static PyTypeObject ConnectionType = {
524 PyObject_HEAD_INIT(NULL)
525 0, /*ob_size*/
526 "_dbus_bindings.Connection", /*tp_name*/
527 sizeof(Connection), /*tp_basicsize*/
528 0, /*tp_itemsize*/
529 /* methods */
530 (destructor)Connection_tp_dealloc,
531 0, /*tp_print*/
532 0, /*tp_getattr*/
533 0, /*tp_setattr*/
534 0, /*tp_compare*/
535 0, /*tp_repr*/
536 0, /*tp_as_number*/
537 0, /*tp_as_sequence*/
538 0, /*tp_as_mapping*/
539 0, /*tp_hash*/
540 0, /*tp_call*/
541 0, /*tp_str*/
542 0, /*tp_getattro*/
543 0, /*tp_setattro*/
544 0, /*tp_as_buffer*/
545 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_WEAKREFS | Py_TPFLAGS_BASETYPE,
546 Connection_tp_doc, /*tp_doc*/
547 0, /*tp_traverse*/
548 0, /*tp_clear*/
549 0, /*tp_richcompare*/
550 offsetof(Connection, weaklist), /*tp_weaklistoffset*/
551 0, /*tp_iter*/
552 0, /*tp_iternext*/
553 Connection_tp_methods, /*tp_methods*/
554 0, /*tp_members*/
555 0, /*tp_getset*/
556 0, /*tp_base*/
557 0, /*tp_dict*/
558 0, /*tp_descr_get*/
559 0, /*tp_descr_set*/
560 0, /*tp_dictoffset*/
561 0, /*tp_init*/
562 0, /*tp_alloc*/
563 Connection_tp_new, /*tp_new*/
564 0, /*tp_free*/
565 0, /*tp_is_gc*/
568 static inline dbus_bool_t
569 init_conn_types(void)
571 default_main_loop = NULL;
573 /* Get a slot to store our weakref on DBus Connections */
574 _connection_python_slot = -1;
575 if (!dbus_connection_allocate_data_slot(&_connection_python_slot))
576 return FALSE;
577 if (PyType_Ready(&ConnectionType) < 0)
578 return FALSE;
579 return TRUE;
582 static inline dbus_bool_t
583 insert_conn_types(PyObject *this_module)
585 if (PyModule_AddObject(this_module, "Connection",
586 (PyObject *)&ConnectionType) < 0) return FALSE;
587 return TRUE;
590 /* vim:set ft=c cino< sw=4 sts=4 et: */