summaryrefslogtreecommitdiffstats
path: root/Modules/_io
diff options
context:
space:
mode:
authorAntoine Pitrou <solipsis@pitrou.net>2013-07-30 17:59:21 (GMT)
committerAntoine Pitrou <solipsis@pitrou.net>2013-07-30 17:59:21 (GMT)
commit796564c27b8f2e32b9fbc034bbdda75f9507ca43 (patch)
tree52db4985f7fe10db48703103a156ff3fd2011d8d /Modules/_io
parentc5d95b17acfdf2d01569bd32d67cadbd5f5cd4a3 (diff)
downloadcpython-796564c27b8f2e32b9fbc034bbdda75f9507ca43.zip
cpython-796564c27b8f2e32b9fbc034bbdda75f9507ca43.tar.gz
cpython-796564c27b8f2e32b9fbc034bbdda75f9507ca43.tar.bz2
Issue #18112: PEP 442 implementation (safe object finalization).
Diffstat (limited to 'Modules/_io')
-rw-r--r--Modules/_io/bufferedio.c74
-rw-r--r--Modules/_io/fileio.c24
-rw-r--r--Modules/_io/iobase.c92
-rw-r--r--Modules/_io/textio.c37
4 files changed, 163 insertions, 64 deletions
diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c
index 6a885cf..6fe5d58 100644
--- a/Modules/_io/bufferedio.c
+++ b/Modules/_io/bufferedio.c
@@ -190,7 +190,8 @@ PyTypeObject PyBufferedIOBase_Type = {
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
+ | Py_TPFLAGS_HAVE_FINALIZE, /*tp_flags*/
bufferediobase_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
@@ -209,6 +210,16 @@ PyTypeObject PyBufferedIOBase_Type = {
0, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+ 0, /* tp_del */
+ 0, /* tp_version_tag */
+ 0, /* tp_finalize */
};
@@ -220,7 +231,7 @@ typedef struct {
int detached;
int readable;
int writable;
- int deallocating;
+ char finalizing;
/* True if this is a vanilla Buffered object (rather than a user derived
class) *and* the raw stream is a vanilla FileIO object. */
@@ -384,8 +395,8 @@ _enter_buffered_busy(buffered *self)
static void
buffered_dealloc(buffered *self)
{
- self->deallocating = 1;
- if (self->ok && _PyIOBase_finalize((PyObject *) self) < 0)
+ self->finalizing = 1;
+ if (_PyIOBase_finalize((PyObject *) self) < 0)
return;
_PyObject_GC_UNTRACK(self);
self->ok = 0;
@@ -428,8 +439,6 @@ buffered_traverse(buffered *self, visitproc visit, void *arg)
static int
buffered_clear(buffered *self)
{
- if (self->ok && _PyIOBase_finalize((PyObject *) self) < 0)
- return -1;
self->ok = 0;
Py_CLEAR(self->raw);
Py_CLEAR(self->dict);
@@ -508,7 +517,7 @@ buffered_close(buffered *self, PyObject *args)
goto end;
}
- if (self->deallocating) {
+ if (self->finalizing) {
PyObject *r = buffered_dealloc_warn(self, (PyObject *) self);
if (r)
Py_DECREF(r);
@@ -1749,6 +1758,7 @@ static PyMethodDef bufferedreader_methods[] = {
static PyMemberDef bufferedreader_members[] = {
{"raw", T_OBJECT, offsetof(buffered, raw), READONLY},
+ {"_finalizing", T_BOOL, offsetof(buffered, finalizing), 0},
{NULL}
};
@@ -1781,7 +1791,7 @@ PyTypeObject PyBufferedReader_Type = {
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
- | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+ | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, /*tp_flags*/
bufferedreader_doc, /* tp_doc */
(traverseproc)buffered_traverse, /* tp_traverse */
(inquiry)buffered_clear, /* tp_clear */
@@ -1800,6 +1810,16 @@ PyTypeObject PyBufferedReader_Type = {
(initproc)bufferedreader_init, /* tp_init */
0, /* tp_alloc */
PyType_GenericNew, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+ 0, /* tp_del */
+ 0, /* tp_version_tag */
+ 0, /* tp_finalize */
};
@@ -2130,6 +2150,7 @@ static PyMethodDef bufferedwriter_methods[] = {
static PyMemberDef bufferedwriter_members[] = {
{"raw", T_OBJECT, offsetof(buffered, raw), READONLY},
+ {"_finalizing", T_BOOL, offsetof(buffered, finalizing), 0},
{NULL}
};
@@ -2162,7 +2183,7 @@ PyTypeObject PyBufferedWriter_Type = {
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
- | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+ | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, /*tp_flags*/
bufferedwriter_doc, /* tp_doc */
(traverseproc)buffered_traverse, /* tp_traverse */
(inquiry)buffered_clear, /* tp_clear */
@@ -2181,6 +2202,16 @@ PyTypeObject PyBufferedWriter_Type = {
(initproc)bufferedwriter_init, /* tp_init */
0, /* tp_alloc */
PyType_GenericNew, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+ 0, /* tp_del */
+ 0, /* tp_version_tag */
+ 0, /* tp_finalize */
};
@@ -2416,7 +2447,7 @@ PyTypeObject PyBufferedRWPair_Type = {
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
- | Py_TPFLAGS_HAVE_GC, /* tp_flags */
+ | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, /* tp_flags */
bufferedrwpair_doc, /* tp_doc */
(traverseproc)bufferedrwpair_traverse, /* tp_traverse */
(inquiry)bufferedrwpair_clear, /* tp_clear */
@@ -2435,6 +2466,16 @@ PyTypeObject PyBufferedRWPair_Type = {
(initproc)bufferedrwpair_init, /* tp_init */
0, /* tp_alloc */
PyType_GenericNew, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+ 0, /* tp_del */
+ 0, /* tp_version_tag */
+ 0, /* tp_finalize */
};
@@ -2522,6 +2563,7 @@ static PyMethodDef bufferedrandom_methods[] = {
static PyMemberDef bufferedrandom_members[] = {
{"raw", T_OBJECT, offsetof(buffered, raw), READONLY},
+ {"_finalizing", T_BOOL, offsetof(buffered, finalizing), 0},
{NULL}
};
@@ -2554,7 +2596,7 @@ PyTypeObject PyBufferedRandom_Type = {
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
- | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+ | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, /*tp_flags*/
bufferedrandom_doc, /* tp_doc */
(traverseproc)buffered_traverse, /* tp_traverse */
(inquiry)buffered_clear, /* tp_clear */
@@ -2573,4 +2615,14 @@ PyTypeObject PyBufferedRandom_Type = {
(initproc)bufferedrandom_init, /* tp_init */
0, /* tp_alloc */
PyType_GenericNew, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+ 0, /* tp_del */
+ 0, /* tp_version_tag */
+ 0, /* tp_finalize */
};
diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c
index 5280991..e88ae87 100644
--- a/Modules/_io/fileio.c
+++ b/Modules/_io/fileio.c
@@ -51,7 +51,7 @@ typedef struct {
unsigned int writable : 1;
signed int seekable : 2; /* -1 means unknown */
unsigned int closefd : 1;
- unsigned int deallocating: 1;
+ char finalizing;
PyObject *weakreflist;
PyObject *dict;
} fileio;
@@ -128,7 +128,7 @@ fileio_close(fileio *self)
self->fd = -1;
Py_RETURN_NONE;
}
- if (self->deallocating) {
+ if (self->finalizing) {
PyObject *r = fileio_dealloc_warn(self, (PyObject *) self);
if (r)
Py_DECREF(r);
@@ -447,7 +447,7 @@ fileio_clear(fileio *self)
static void
fileio_dealloc(fileio *self)
{
- self->deallocating = 1;
+ self->finalizing = 1;
if (_PyIOBase_finalize((PyObject *) self) < 0)
return;
_PyObject_GC_UNTRACK(self);
@@ -1182,6 +1182,11 @@ static PyGetSetDef fileio_getsetlist[] = {
{NULL},
};
+static PyMemberDef fileio_members[] = {
+ {"_finalizing", T_BOOL, offsetof(fileio, finalizing), 0},
+ {NULL}
+};
+
PyTypeObject PyFileIO_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"_io.FileIO",
@@ -1203,7 +1208,7 @@ PyTypeObject PyFileIO_Type = {
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
- | Py_TPFLAGS_HAVE_GC, /* tp_flags */
+ | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, /* tp_flags */
fileio_doc, /* tp_doc */
(traverseproc)fileio_traverse, /* tp_traverse */
(inquiry)fileio_clear, /* tp_clear */
@@ -1212,7 +1217,7 @@ PyTypeObject PyFileIO_Type = {
0, /* tp_iter */
0, /* tp_iternext */
fileio_methods, /* tp_methods */
- 0, /* tp_members */
+ fileio_members, /* tp_members */
fileio_getsetlist, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
@@ -1223,4 +1228,13 @@ PyTypeObject PyFileIO_Type = {
PyType_GenericAlloc, /* tp_alloc */
fileio_new, /* tp_new */
PyObject_GC_Del, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+ 0, /* tp_del */
+ 0, /* tp_version_tag */
+ 0, /* tp_finalize */
};
diff --git a/Modules/_io/iobase.c b/Modules/_io/iobase.c
index e38473a..a4f2c0e 100644
--- a/Modules/_io/iobase.c
+++ b/Modules/_io/iobase.c
@@ -196,21 +196,17 @@ iobase_close(PyObject *self, PyObject *args)
/* Finalization and garbage collection support */
-int
-_PyIOBase_finalize(PyObject *self)
+static void
+iobase_finalize(PyObject *self)
{
PyObject *res;
- PyObject *tp, *v, *tb;
- int closed = 1;
- int is_zombie;
+ PyObject *error_type, *error_value, *error_traceback;
+ int closed;
+ _Py_IDENTIFIER(_finalizing);
+
+ /* Save the current exception, if any. */
+ PyErr_Fetch(&error_type, &error_value, &error_traceback);
- /* If _PyIOBase_finalize() is called from a destructor, we need to
- resurrect the object as calling close() can invoke arbitrary code. */
- is_zombie = (Py_REFCNT(self) == 0);
- if (is_zombie) {
- ++Py_REFCNT(self);
- }
- PyErr_Fetch(&tp, &v, &tb);
/* If `closed` doesn't exist or can't be evaluated as bool, then the
object is probably in an unusable state, so ignore. */
res = PyObject_GetAttr(self, _PyIO_str_closed);
@@ -223,6 +219,10 @@ _PyIOBase_finalize(PyObject *self)
PyErr_Clear();
}
if (closed == 0) {
+ /* Signal close() that it was called as part of the object
+ finalization process. */
+ if (_PyObject_SetAttrId(self, &PyId__finalizing, Py_True))
+ PyErr_Clear();
res = PyObject_CallMethodObjArgs((PyObject *) self, _PyIO_str_close,
NULL);
/* Silencing I/O errors is bad, but printing spurious tracebacks is
@@ -233,31 +233,25 @@ _PyIOBase_finalize(PyObject *self)
else
Py_DECREF(res);
}
- PyErr_Restore(tp, v, tb);
- if (is_zombie) {
- if (--Py_REFCNT(self) != 0) {
- /* The object lives again. The following code is taken from
- slot_tp_del in typeobject.c. */
- Py_ssize_t refcnt = Py_REFCNT(self);
- _Py_NewReference(self);
- Py_REFCNT(self) = refcnt;
- /* If Py_REF_DEBUG, _Py_NewReference bumped _Py_RefTotal, so
- * we need to undo that. */
- _Py_DEC_REFTOTAL;
- /* If Py_TRACE_REFS, _Py_NewReference re-added self to the object
- * chain, so no more to do there.
- * If COUNT_ALLOCS, the original decref bumped tp_frees, and
- * _Py_NewReference bumped tp_allocs: both of those need to be
- * undone.
- */
-#ifdef COUNT_ALLOCS
- --Py_TYPE(self)->tp_frees;
- --Py_TYPE(self)->tp_allocs;
-#endif
- return -1;
- }
+
+ /* Restore the saved exception. */
+ PyErr_Restore(error_type, error_value, error_traceback);
+}
+
+int
+_PyIOBase_finalize(PyObject *self)
+{
+ int is_zombie;
+
+ /* If _PyIOBase_finalize() is called from a destructor, we need to
+ resurrect the object as calling close() can invoke arbitrary code. */
+ is_zombie = (Py_REFCNT(self) == 0);
+ if (is_zombie)
+ return PyObject_CallFinalizerFromDealloc(self);
+ else {
+ PyObject_CallFinalizer(self);
+ return 0;
}
- return 0;
}
static int
@@ -270,8 +264,6 @@ iobase_traverse(iobase *self, visitproc visit, void *arg)
static int
iobase_clear(iobase *self)
{
- if (_PyIOBase_finalize((PyObject *) self) < 0)
- return -1;
Py_CLEAR(self->dict);
return 0;
}
@@ -741,7 +733,7 @@ PyTypeObject PyIOBase_Type = {
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
- | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+ | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, /*tp_flags*/
iobase_doc, /* tp_doc */
(traverseproc)iobase_traverse, /* tp_traverse */
(inquiry)iobase_clear, /* tp_clear */
@@ -760,6 +752,16 @@ PyTypeObject PyIOBase_Type = {
0, /* tp_init */
0, /* tp_alloc */
PyType_GenericNew, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+ 0, /* tp_del */
+ 0, /* tp_version_tag */
+ (destructor)iobase_finalize, /* tp_finalize */
};
@@ -905,7 +907,7 @@ PyTypeObject PyRawIOBase_Type = {
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_FINALIZE, /*tp_flags*/
rawiobase_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
@@ -924,4 +926,14 @@ PyTypeObject PyRawIOBase_Type = {
0, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+ 0, /* tp_del */
+ 0, /* tp_version_tag */
+ 0, /* tp_finalize */
};
diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c
index 283411b..5d2438d 100644
--- a/Modules/_io/textio.c
+++ b/Modules/_io/textio.c
@@ -173,7 +173,8 @@ PyTypeObject PyTextIOBase_Type = {
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
+ | Py_TPFLAGS_HAVE_FINALIZE, /*tp_flags*/
textiobase_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
@@ -192,6 +193,16 @@ PyTypeObject PyTextIOBase_Type = {
0, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+ 0, /* tp_del */
+ 0, /* tp_version_tag */
+ 0, /* tp_finalize */
};
@@ -691,7 +702,7 @@ typedef struct
char seekable;
char has_read1;
char telling;
- char deallocating;
+ char finalizing;
/* Specialized encoding func (see below) */
encodefunc_t encodefunc;
/* Whether or not it's the start of the stream */
@@ -1112,8 +1123,6 @@ textiowrapper_init(textio *self, PyObject *args, PyObject *kwds)
static int
_textiowrapper_clear(textio *self)
{
- if (self->ok && _PyIOBase_finalize((PyObject *) self) < 0)
- return -1;
self->ok = 0;
Py_CLEAR(self->buffer);
Py_CLEAR(self->encoding);
@@ -1131,9 +1140,10 @@ _textiowrapper_clear(textio *self)
static void
textiowrapper_dealloc(textio *self)
{
- self->deallocating = 1;
- if (_textiowrapper_clear(self) < 0)
+ self->finalizing = 1;
+ if (_PyIOBase_finalize((PyObject *) self) < 0)
return;
+ _textiowrapper_clear(self);
_PyObject_GC_UNTRACK(self);
if (self->weakreflist != NULL)
PyObject_ClearWeakRefs((PyObject *)self);
@@ -2573,7 +2583,7 @@ textiowrapper_close(textio *self, PyObject *args)
}
else {
PyObject *exc = NULL, *val, *tb;
- if (self->deallocating) {
+ if (self->finalizing) {
res = _PyObject_CallMethodId(self->buffer, &PyId__dealloc_warn, "O", self);
if (res)
Py_DECREF(res);
@@ -2734,6 +2744,7 @@ static PyMemberDef textiowrapper_members[] = {
{"encoding", T_OBJECT, offsetof(textio, encoding), READONLY},
{"buffer", T_OBJECT, offsetof(textio, buffer), READONLY},
{"line_buffering", T_BOOL, offsetof(textio, line_buffering), READONLY},
+ {"_finalizing", T_BOOL, offsetof(textio, finalizing), 0},
{NULL}
};
@@ -2770,7 +2781,7 @@ PyTypeObject PyTextIOWrapper_Type = {
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
- | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+ | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, /*tp_flags*/
textiowrapper_doc, /* tp_doc */
(traverseproc)textiowrapper_traverse, /* tp_traverse */
(inquiry)textiowrapper_clear, /* tp_clear */
@@ -2789,4 +2800,14 @@ PyTypeObject PyTextIOWrapper_Type = {
(initproc)textiowrapper_init, /* tp_init */
0, /* tp_alloc */
PyType_GenericNew, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+ 0, /* tp_del */
+ 0, /* tp_version_tag */
+ 0, /* tp_finalize */
};