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.c116
1 files changed, 105 insertions, 11 deletions
diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c
index 25b91e3..26ebde0 100644
--- a/Modules/_io/bufferedio.c
+++ b/Modules/_io/bufferedio.c
@@ -197,6 +197,7 @@ typedef struct {
int detached;
int readable;
int writable;
+ int deallocating;
/* True if this is a vanilla Buffered object (rather than a user derived
class) *and* the raw stream is a vanilla FileIO object. */
@@ -260,6 +261,7 @@ typedef struct {
/* These macros protect the buffered object against concurrent operations. */
#ifdef WITH_THREAD
+
static int
_enter_buffered_busy(buffered *self)
{
@@ -359,6 +361,7 @@ _enter_buffered_busy(buffered *self)
static void
buffered_dealloc(buffered *self)
{
+ self->deallocating = 1;
if (self->ok && _PyIOBase_finalize((PyObject *) self) < 0)
return;
_PyObject_GC_UNTRACK(self);
@@ -399,6 +402,23 @@ buffered_clear(buffered *self)
return 0;
}
+/* Because this can call arbitrary code, it shouldn't be called when
+ the refcount is 0 (that is, not directly from tp_dealloc unless
+ the refcount has been temporarily re-incremented). */
+static PyObject *
+buffered_dealloc_warn(buffered *self, PyObject *source)
+{
+ if (self->ok && self->raw) {
+ PyObject *r;
+ r = PyObject_CallMethod(self->raw, "_dealloc_warn", "O", source);
+ if (r)
+ Py_DECREF(r);
+ else
+ PyErr_Clear();
+ }
+ Py_RETURN_NONE;
+}
+
/*
* _BufferedIOMixin methods
* This is not a class, just a collection of methods that will be reused
@@ -453,6 +473,14 @@ buffered_close(buffered *self, PyObject *args)
Py_INCREF(res);
goto end;
}
+
+ if (self->deallocating) {
+ PyObject *r = buffered_dealloc_warn(self, (PyObject *) self);
+ if (r)
+ Py_DECREF(r);
+ else
+ PyErr_Clear();
+ }
/* flush() will most probably re-take the lock, so drop it first */
LEAVE_BUFFERED(self)
res = PyObject_CallMethodObjArgs((PyObject *)self, _PyIO_str_flush, NULL);
@@ -541,6 +569,15 @@ buffered_isatty(buffered *self, PyObject *args)
return PyObject_CallMethodObjArgs(self->raw, _PyIO_str_isatty, NULL);
}
+/* Serialization */
+
+static PyObject *
+buffered_getstate(buffered *self, PyObject *args)
+{
+ PyErr_Format(PyExc_TypeError,
+ "cannot serialize '%s' object", Py_TYPE(self)->tp_name);
+ return NULL;
+}
/* Forward decls */
static PyObject *
@@ -597,7 +634,8 @@ _buffered_raw_tell(buffered *self)
if (n < 0) {
if (!PyErr_Occurred())
PyErr_Format(PyExc_IOError,
- "Raw stream returned invalid position %zd", n);
+ "Raw stream returned invalid position %" PY_PRIdOFF,
+ (PY_OFF_T_COMPAT)n);
return -1;
}
self->abs_pos = n;
@@ -629,7 +667,8 @@ _buffered_raw_seek(buffered *self, Py_off_t target, int whence)
if (n < 0) {
if (!PyErr_Occurred())
PyErr_Format(PyExc_IOError,
- "Raw stream returned invalid position %zd", n);
+ "Raw stream returned invalid position %" PY_PRIdOFF,
+ (PY_OFF_T_COMPAT)n);
return -1;
}
self->abs_pos = n;
@@ -675,6 +714,39 @@ _buffered_init(buffered *self)
return 0;
}
+/* Return 1 if an EnvironmentError with errno == EINTR is set (and then
+ clears the error indicator), 0 otherwise.
+ Should only be called when PyErr_Occurred() is true.
+*/
+static int
+_trap_eintr(void)
+{
+ static PyObject *eintr_int = NULL;
+ PyObject *typ, *val, *tb;
+ PyEnvironmentErrorObject *env_err;
+
+ if (eintr_int == NULL) {
+ eintr_int = PyLong_FromLong(EINTR);
+ assert(eintr_int != NULL);
+ }
+ if (!PyErr_ExceptionMatches(PyExc_EnvironmentError))
+ return 0;
+ PyErr_Fetch(&typ, &val, &tb);
+ PyErr_NormalizeException(&typ, &val, &tb);
+ env_err = (PyEnvironmentErrorObject *) val;
+ assert(env_err != NULL);
+ if (env_err->myerrno != NULL &&
+ PyObject_RichCompareBool(env_err->myerrno, eintr_int, Py_EQ) > 0) {
+ Py_DECREF(typ);
+ Py_DECREF(val);
+ Py_XDECREF(tb);
+ return 1;
+ }
+ /* This silences any error set by PyObject_RichCompareBool() */
+ PyErr_Restore(typ, val, tb);
+ return 0;
+}
+
/*
* Shared methods and wrappers
*/
@@ -1230,7 +1302,14 @@ _bufferedreader_raw_read(buffered *self, char *start, Py_ssize_t len)
memobj = PyMemoryView_FromBuffer(&buf);
if (memobj == NULL)
return -1;
- res = PyObject_CallMethodObjArgs(self->raw, _PyIO_str_readinto, memobj, NULL);
+ /* NOTE: PyErr_SetFromErrno() calls PyErr_CheckSignals() when EINTR
+ occurs so we needn't do it ourselves.
+ We then retry reading, ignoring the signal if no handler has
+ raised (see issue #10956).
+ */
+ do {
+ res = PyObject_CallMethodObjArgs(self->raw, _PyIO_str_readinto, memobj, NULL);
+ } while (res == NULL && _trap_eintr());
Py_DECREF(memobj);
if (res == NULL)
return -1;
@@ -1488,6 +1567,8 @@ static PyMethodDef bufferedreader_methods[] = {
{"writable", (PyCFunction)buffered_writable, METH_NOARGS},
{"fileno", (PyCFunction)buffered_fileno, METH_NOARGS},
{"isatty", (PyCFunction)buffered_isatty, METH_NOARGS},
+ {"_dealloc_warn", (PyCFunction)buffered_dealloc_warn, METH_O},
+ {"__getstate__", (PyCFunction)buffered_getstate, METH_NOARGS},
{"read", (PyCFunction)buffered_read, METH_VARARGS},
{"peek", (PyCFunction)buffered_peek, METH_VARARGS},
@@ -1637,7 +1718,14 @@ _bufferedwriter_raw_write(buffered *self, char *start, Py_ssize_t len)
memobj = PyMemoryView_FromBuffer(&buf);
if (memobj == NULL)
return -1;
- res = PyObject_CallMethodObjArgs(self->raw, _PyIO_str_write, memobj, NULL);
+ /* NOTE: PyErr_SetFromErrno() calls PyErr_CheckSignals() when EINTR
+ occurs so we needn't do it ourselves.
+ We then retry writing, ignoring the signal if no handler has
+ raised (see issue #10956).
+ */
+ do {
+ res = PyObject_CallMethodObjArgs(self->raw, _PyIO_str_write, memobj, NULL);
+ } while (res == NULL && _trap_eintr());
Py_DECREF(memobj);
if (res == NULL)
return -1;
@@ -1723,7 +1811,8 @@ bufferedwriter_write(buffered *self, PyObject *args)
{
PyObject *res = NULL;
Py_buffer buf;
- Py_ssize_t written, avail, remaining, n;
+ Py_ssize_t written, avail, remaining;
+ Py_off_t offset;
CHECK_INITIALIZED(self)
if (!PyArg_ParseTuple(args, "y*:write", &buf)) {
@@ -1801,18 +1890,18 @@ bufferedwriter_write(buffered *self, PyObject *args)
the raw stream by itself).
Fixes issue #6629.
*/
- n = RAW_OFFSET(self);
- if (n != 0) {
- if (_buffered_raw_seek(self, -n, 1) < 0)
+ offset = RAW_OFFSET(self);
+ if (offset != 0) {
+ if (_buffered_raw_seek(self, -offset, 1) < 0)
goto error;
- self->raw_pos -= n;
+ self->raw_pos -= offset;
}
/* Then write buf itself. At this point the buffer has been emptied. */
remaining = buf.len;
written = 0;
while (remaining > self->buffer_size) {
- n = _bufferedwriter_raw_write(
+ Py_ssize_t n = _bufferedwriter_raw_write(
self, (char *) buf.buf + written, buf.len - written);
if (n == -1) {
Py_ssize_t *w = _buffered_check_blocking_error();
@@ -1872,6 +1961,8 @@ static PyMethodDef bufferedwriter_methods[] = {
{"writable", (PyCFunction)buffered_writable, METH_NOARGS},
{"fileno", (PyCFunction)buffered_fileno, METH_NOARGS},
{"isatty", (PyCFunction)buffered_isatty, METH_NOARGS},
+ {"_dealloc_warn", (PyCFunction)buffered_dealloc_warn, METH_O},
+ {"__getstate__", (PyCFunction)buffered_getstate, METH_NOARGS},
{"write", (PyCFunction)bufferedwriter_write, METH_VARARGS},
{"truncate", (PyCFunction)buffered_truncate, METH_VARARGS},
@@ -2137,6 +2228,8 @@ static PyMethodDef bufferedrwpair_methods[] = {
{"close", (PyCFunction)bufferedrwpair_close, METH_NOARGS},
{"isatty", (PyCFunction)bufferedrwpair_isatty, METH_NOARGS},
+ {"__getstate__", (PyCFunction)buffered_getstate, METH_NOARGS},
+
{NULL, NULL}
};
@@ -2256,6 +2349,8 @@ static PyMethodDef bufferedrandom_methods[] = {
{"writable", (PyCFunction)buffered_writable, METH_NOARGS},
{"fileno", (PyCFunction)buffered_fileno, METH_NOARGS},
{"isatty", (PyCFunction)buffered_isatty, METH_NOARGS},
+ {"_dealloc_warn", (PyCFunction)buffered_dealloc_warn, METH_O},
+ {"__getstate__", (PyCFunction)buffered_getstate, METH_NOARGS},
{"flush", (PyCFunction)buffered_flush, METH_NOARGS},
@@ -2325,4 +2420,3 @@ PyTypeObject PyBufferedRandom_Type = {
0, /* tp_alloc */
PyType_GenericNew, /* tp_new */
};
-