summaryrefslogtreecommitdiffstats
path: root/Modules/_io/bufferedio.c
diff options
context:
space:
mode:
Diffstat (limited to 'Modules/_io/bufferedio.c')
-rw-r--r--Modules/_io/bufferedio.c148
1 files changed, 116 insertions, 32 deletions
diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c
index ea0302b..365bb85 100644
--- a/Modules/_io/bufferedio.c
+++ b/Modules/_io/bufferedio.c
@@ -52,7 +52,6 @@ bufferediobase_readinto(PyObject *self, PyObject *args)
Py_buffer buf;
Py_ssize_t len;
PyObject *data;
- _Py_IDENTIFIER(read);
if (!PyArg_ParseTuple(args, "w*:readinto", &buf)) {
return NULL;
@@ -92,7 +91,9 @@ bufferediobase_readinto(PyObject *self, PyObject *args)
static PyObject *
bufferediobase_unsupported(const char *message)
{
- PyErr_SetString(IO_STATE->unsupported_operation, message);
+ _PyIO_State *state = IO_STATE();
+ if (state != NULL)
+ PyErr_SetString(state->unsupported_operation, message);
return NULL;
}
@@ -190,7 +191,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 +211,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 +232,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. */
@@ -288,14 +300,35 @@ typedef struct {
static int
_enter_buffered_busy(buffered *self)
{
+ int relax_locking;
+ PyLockStatus st;
if (self->owner == PyThread_get_thread_ident()) {
PyErr_Format(PyExc_RuntimeError,
"reentrant call inside %R", self);
return 0;
}
+ relax_locking = (_Py_Finalizing != NULL);
Py_BEGIN_ALLOW_THREADS
- PyThread_acquire_lock(self->lock, 1);
+ if (!relax_locking)
+ st = PyThread_acquire_lock(self->lock, 1);
+ else {
+ /* When finalizing, we don't want a deadlock to happen with daemon
+ * threads abruptly shut down while they owned the lock.
+ * Therefore, only wait for a grace period (1 s.).
+ * Note that non-daemon threads have already exited here, so this
+ * shouldn't affect carefully written threaded I/O code.
+ */
+ st = PyThread_acquire_lock_timed(self->lock, 1e6, 0);
+ }
Py_END_ALLOW_THREADS
+ if (relax_locking && st != PY_LOCK_ACQUIRED) {
+ PyObject *msgobj = PyUnicode_FromFormat(
+ "could not acquire lock for %A at interpreter "
+ "shutdown, possibly due to daemon threads",
+ (PyObject *) self);
+ char *msg = PyUnicode_AsUTF8(msgobj);
+ Py_FatalError(msg);
+ }
return 1;
}
@@ -384,8 +417,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 +461,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 +539,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);
@@ -527,20 +558,14 @@ buffered_close(buffered *self, PyObject *args)
res = PyObject_CallMethodObjArgs(self->raw, _PyIO_str_close, NULL);
+ if (self->buffer) {
+ PyMem_Free(self->buffer);
+ self->buffer = NULL;
+ }
+
if (exc != NULL) {
- if (res != NULL) {
- Py_CLEAR(res);
- PyErr_Restore(exc, val, tb);
- }
- else {
- PyObject *val2;
- Py_DECREF(exc);
- Py_XDECREF(tb);
- PyErr_Fetch(&exc, &val2, &tb);
- PyErr_NormalizeException(&exc, &val2, &tb);
- PyException_SetContext(val2, val);
- PyErr_Restore(exc, val2, tb);
- }
+ _PyErr_ChainExceptions(exc, val, tb);
+ Py_CLEAR(res);
}
end:
@@ -658,6 +683,11 @@ static void
_set_BlockingIOError(char *msg, Py_ssize_t written)
{
PyObject *err;
+#ifdef Py_DEBUG
+ /* in debug mode, PyEval_EvalFrameEx() fails with an assertion error
+ if an exception is set when it is called */
+ PyErr_Clear();
+#endif
err = PyObject_CallFunction(PyExc_BlockingIOError, "isn",
errno, msg, written);
if (err)
@@ -859,6 +889,8 @@ buffered_peek(buffered *self, PyObject *args)
PyObject *res = NULL;
CHECK_INITIALIZED(self)
+ CHECK_CLOSED(self, "peek of closed file")
+
if (!PyArg_ParseTuple(args, "|n:peek", &n)) {
return NULL;
}
@@ -933,6 +965,9 @@ buffered_read1(buffered *self, PyObject *args)
"read length must be positive");
return NULL;
}
+
+ CHECK_CLOSED(self, "read of closed file")
+
if (n == 0)
return PyBytes_FromStringAndSize(NULL, 0);
@@ -1350,7 +1385,7 @@ buffered_repr(buffered *self)
nameobj = _PyObject_GetAttrId((PyObject *) self, &PyId_name);
if (nameobj == NULL) {
- if (PyErr_ExceptionMatches(PyExc_AttributeError))
+ if (PyErr_ExceptionMatches(PyExc_Exception))
PyErr_Clear();
else
return NULL;
@@ -1739,6 +1774,7 @@ static PyMethodDef bufferedreader_methods[] = {
static PyMemberDef bufferedreader_members[] = {
{"raw", T_OBJECT, offsetof(buffered, raw), READONLY},
+ {"_finalizing", T_BOOL, offsetof(buffered, finalizing), 0},
{NULL}
};
@@ -1771,7 +1807,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 */
@@ -1790,6 +1826,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 */
};
@@ -2120,6 +2166,7 @@ static PyMethodDef bufferedwriter_methods[] = {
static PyMemberDef bufferedwriter_members[] = {
{"raw", T_OBJECT, offsetof(buffered, raw), READONLY},
+ {"_finalizing", T_BOOL, offsetof(buffered, finalizing), 0},
{NULL}
};
@@ -2152,7 +2199,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 */
@@ -2171,6 +2218,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 */
};
@@ -2334,12 +2391,18 @@ bufferedrwpair_writable(rwpair *self, PyObject *args)
static PyObject *
bufferedrwpair_close(rwpair *self, PyObject *args)
{
+ PyObject *exc = NULL, *val, *tb;
PyObject *ret = _forward_call(self->writer, &PyId_close, args);
if (ret == NULL)
- return NULL;
- Py_DECREF(ret);
-
- return _forward_call(self->reader, &PyId_close, args);
+ PyErr_Fetch(&exc, &val, &tb);
+ else
+ Py_DECREF(ret);
+ ret = _forward_call(self->reader, &PyId_close, args);
+ if (exc != NULL) {
+ _PyErr_ChainExceptions(exc, val, tb);
+ Py_CLEAR(ret);
+ }
+ return ret;
}
static PyObject *
@@ -2413,7 +2476,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 */
@@ -2432,6 +2495,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 */
};
@@ -2519,6 +2592,7 @@ static PyMethodDef bufferedrandom_methods[] = {
static PyMemberDef bufferedrandom_members[] = {
{"raw", T_OBJECT, offsetof(buffered, raw), READONLY},
+ {"_finalizing", T_BOOL, offsetof(buffered, finalizing), 0},
{NULL}
};
@@ -2551,7 +2625,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 */
@@ -2570,4 +2644,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 */
};