Copyright (C) 2000-2012 |
GNU Info (python2.1-ext.info)Thin IceThin Ice -------- There are a few situations where seemingly harmless use of a borrowed reference can lead to problems. These all have to do with implicit invocations of the interpreter, which can cause the owner of a reference to dispose of it. The first and most important case to know about is using `Py_DECREF()' on an unrelated object while borrowing a reference to a list item. For instance: bug(PyObject *list) { PyObject *item = PyList_GetItem(list, 0); PyList_SetItem(list, 1, PyInt_FromLong(0L)); PyObject_Print(item, stdout, 0); /* BUG! */ } This function first borrows a reference to `list[0]', then replaces `list[1]' with the value `0', and finally prints the borrowed reference. Looks harmless, right? But it's not! Let's follow the control flow into `PyList_SetItem()'. The list owns references to all its items, so when item 1 is replaced, it has to dispose of the original item 1. Now let's suppose the original item 1 was an instance of a user-defined class, and let's further suppose that the class defined a `__del__()' method. If this class instance has a reference count of 1, disposing of it will call its `__del__()' method. Since it is written in Python, the `__del__()' method can execute arbitrary Python code. Could it perhaps do something to invalidate the reference to `item' in `bug()'? You bet! Assuming that the list passed into `bug()' is accessible to the `__del__()' method, it could execute a statement to the effect of `del list[0]', and assuming this was the last reference to that object, it would free the memory associated with it, thereby invalidating `item'. The solution, once you know the source of the problem, is easy: temporarily increment the reference count. The correct version of the function reads: no_bug(PyObject *list) { PyObject *item = PyList_GetItem(list, 0); Py_INCREF(item); PyList_SetItem(list, 1, PyInt_FromLong(0L)); PyObject_Print(item, stdout, 0); Py_DECREF(item); } This is a true story. An older version of Python contained variants of this bug and someone spent a considerable amount of time in a C debugger to figure out why his `__del__()' methods would fail... The second case of problems with a borrowed reference is a variant involving threads. Normally, multiple threads in the Python interpreter can't get in each other's way, because there is a global lock protecting Python's entire object space. However, it is possible to temporarily release this lock using the macro `Py_BEGIN_ALLOW_THREADS', and to re-acquire it using `Py_END_ALLOW_THREADS'. This is common around blocking I/O calls, to let other threads use the CPU while waiting for the I/O to complete. Obviously, the following function has the same problem as the previous one: bug(PyObject *list) { PyObject *item = PyList_GetItem(list, 0); Py_BEGIN_ALLOW_THREADS ...some blocking I/O call... Py_END_ALLOW_THREADS PyObject_Print(item, stdout, 0); /* BUG! */ } automatically generated by info2www version 1.2.2.9 |