safety of Py_CLEAR and self
I was wondering today how I could convince myself that a sequence of Py_CLEAR() calls in a tp_clear function was safe. Take for example a really trivial sequence of code on frame_clear(): Py_CLEAR(f->f_exc_type); Py_CLEAR(f->f_exc_value); Py_CLEAR(f->f_exc_traceback); Py_CLEAR(f->f_trace); We use Py_CLEAR() so that multiple calls to frame_clear() are safe. The first time this runs it sets exc_type to NULL before calling DECREF. This guarantees that a subsequent frame_clear() or frame_dealloc() won't attempt to DECREF it a second time. I think I understand why that's desireable and why it works. The primary risk is that via DECREF we may execute an arbitrary amount of Python code via weakref callbacks, finalizers, and code in other threads that gets resumed while the callbacks and finalizers are running. My question, specifically, then: Why it is safe to assume that f doesn't point to trash after a particular call to Py_CLEAR()? Any particular call to Py_CLEAR() could break the cycle that the object is involved in an lead to a call to frame_dealloc(). The frame could get returned to an obmalloc pool, returned to the OS, or just re-used by another object before we get back to Py_CLEAR(). It seems like such behavior would be exceedingly unlikely, but I can't convince myself that it is impossible. Which is it: improbable or impossible? If it is only improbable, should we figure out how to write code that is safe against such an improbable event? Jeremy
Looking for where tp_clear() is being called, the only caller is line 713 in gmodule.c, which explicitly surrounds the call with an INCREF/DECREF pair. Perhaps that's the clue you're looking for? --Guido On 2/12/07, Jeremy Hylton <jeremy@alum.mit.edu> wrote:
I was wondering today how I could convince myself that a sequence of Py_CLEAR() calls in a tp_clear function was safe. Take for example a really trivial sequence of code on frame_clear():
Py_CLEAR(f->f_exc_type); Py_CLEAR(f->f_exc_value); Py_CLEAR(f->f_exc_traceback); Py_CLEAR(f->f_trace);
We use Py_CLEAR() so that multiple calls to frame_clear() are safe. The first time this runs it sets exc_type to NULL before calling DECREF. This guarantees that a subsequent frame_clear() or frame_dealloc() won't attempt to DECREF it a second time. I think I understand why that's desireable and why it works. The primary risk is that via DECREF we may execute an arbitrary amount of Python code via weakref callbacks, finalizers, and code in other threads that gets resumed while the callbacks and finalizers are running.
My question, specifically, then: Why it is safe to assume that f doesn't point to trash after a particular call to Py_CLEAR()? Any particular call to Py_CLEAR() could break the cycle that the object is involved in an lead to a call to frame_dealloc(). The frame could get returned to an obmalloc pool, returned to the OS, or just re-used by another object before we get back to Py_CLEAR(). It seems like such behavior would be exceedingly unlikely, but I can't convince myself that it is impossible. Which is it: improbable or impossible? If it is only improbable, should we figure out how to write code that is safe against such an improbable event?
Jeremy _______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/guido%40python.org
-- --Guido van Rossum (home page: http://www.python.org/~guido/)
On 2/12/07, Guido van Rossum <guido@python.org> wrote:
Looking for where tp_clear() is being called, the only caller is line 713 in gmodule.c, which explicitly surrounds the call with an INCREF/DECREF pair. Perhaps that's the clue you're looking for?
Yes, of course. The INCREF guarantees that the object can't be collected until the tp_clear() returns. Thanks. Jeremy
--Guido
On 2/12/07, Jeremy Hylton <jeremy@alum.mit.edu> wrote:
I was wondering today how I could convince myself that a sequence of Py_CLEAR() calls in a tp_clear function was safe. Take for example a really trivial sequence of code on frame_clear():
Py_CLEAR(f->f_exc_type); Py_CLEAR(f->f_exc_value); Py_CLEAR(f->f_exc_traceback); Py_CLEAR(f->f_trace);
We use Py_CLEAR() so that multiple calls to frame_clear() are safe. The first time this runs it sets exc_type to NULL before calling DECREF. This guarantees that a subsequent frame_clear() or frame_dealloc() won't attempt to DECREF it a second time. I think I understand why that's desireable and why it works. The primary risk is that via DECREF we may execute an arbitrary amount of Python code via weakref callbacks, finalizers, and code in other threads that gets resumed while the callbacks and finalizers are running.
My question, specifically, then: Why it is safe to assume that f doesn't point to trash after a particular call to Py_CLEAR()? Any particular call to Py_CLEAR() could break the cycle that the object is involved in an lead to a call to frame_dealloc(). The frame could get returned to an obmalloc pool, returned to the OS, or just re-used by another object before we get back to Py_CLEAR(). It seems like such behavior would be exceedingly unlikely, but I can't convince myself that it is impossible. Which is it: improbable or impossible? If it is only improbable, should we figure out how to write code that is safe against such an improbable event?
Jeremy _______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/guido%40python.org
-- --Guido van Rossum (home page: http://www.python.org/~guido/)
[Jeremy Hylton]
I was wondering today how I could convince myself that a sequence of Py_CLEAR() calls in a tp_clear function was safe. Take for example a really trivial sequence of code on frame_clear():
Py_CLEAR(f->f_exc_type); Py_CLEAR(f->f_exc_value); Py_CLEAR(f->f_exc_traceback); Py_CLEAR(f->f_trace);
We use Py_CLEAR() so that multiple calls to frame_clear() are safe. The first time this runs it sets exc_type to NULL before calling DECREF. This guarantees that a subsequent frame_clear() or frame_dealloc() won't attempt to DECREF it a second time. I think I understand why that's desireable and why it works. The primary risk is that via DECREF we may execute an arbitrary amount of Python code via weakref callbacks, finalizers, and code in other threads that gets resumed while the callbacks and finalizers are running.
My question, specifically, then: Why it is safe to assume that f doesn't point to trash after a particular call to Py_CLEAR()? Any particular call to Py_CLEAR() could break the cycle that the object is involved in an lead to a call to frame_dealloc(). The frame could get returned to an obmalloc pool, returned to the OS, or just re-used by another object before we get back to Py_CLEAR(). It seems like such behavior would be exceedingly unlikely, but I can't convince myself that it is impossible. Which is it: improbable or impossible? If it is only improbable, should we figure out how to write code that is safe against such an improbable event?
As Guido pointed out, tp_clear is called by gc from only one place, which sticks an incref/decref pair around the call so that the refcount on `f` can't fall to 0 while frame_clear() is active. That doesn't mean frame_clear is always safe to call, it only means that gc's use of the tp_clear slot is safe. Nobody else "should be" calling frame_clear (and it so happens nothing else in the core does), but it's something to be dimly aware of. For example, IIRC, some of ZODB's C code internally invokes its XXX_clear() functions directly, as part of removing persistent object state (unrelated to object deallocation). Determining whether those kinds of uses are safe requires case-by-case analysis.
participants (3)
-
Guido van Rossum
-
Jeremy Hylton
-
Tim Peters