mirror of
https://github.com/python/cpython.git
synced 2025-10-17 20:28:43 +00:00
Issue #12213: Fix a buffering bug with interleaved reads and writes that
could appear on BufferedRandom streams.
This commit is contained in:
parent
a370fcf3b2
commit
e05565ec5a
3 changed files with 104 additions and 62 deletions
|
@ -751,6 +751,28 @@ _trap_eintr(void)
|
|||
* Shared methods and wrappers
|
||||
*/
|
||||
|
||||
static PyObject *
|
||||
buffered_flush_and_rewind_unlocked(buffered *self)
|
||||
{
|
||||
PyObject *res;
|
||||
|
||||
res = _bufferedwriter_flush_unlocked(self, 0);
|
||||
if (res == NULL)
|
||||
return NULL;
|
||||
Py_DECREF(res);
|
||||
|
||||
if (self->readable) {
|
||||
/* Rewind the raw stream so that its position corresponds to
|
||||
the current logical position. */
|
||||
Py_off_t n;
|
||||
n = _buffered_raw_seek(self, -RAW_OFFSET(self), 1);
|
||||
_bufferedreader_reset_buf(self);
|
||||
if (n == -1)
|
||||
return NULL;
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
buffered_flush(buffered *self, PyObject *args)
|
||||
{
|
||||
|
@ -761,16 +783,7 @@ buffered_flush(buffered *self, PyObject *args)
|
|||
|
||||
if (!ENTER_BUFFERED(self))
|
||||
return NULL;
|
||||
res = _bufferedwriter_flush_unlocked(self, 0);
|
||||
if (res != NULL && self->readable) {
|
||||
/* Rewind the raw stream so that its position corresponds to
|
||||
the current logical position. */
|
||||
Py_off_t n;
|
||||
n = _buffered_raw_seek(self, -RAW_OFFSET(self), 1);
|
||||
if (n == -1)
|
||||
Py_CLEAR(res);
|
||||
_bufferedreader_reset_buf(self);
|
||||
}
|
||||
res = buffered_flush_and_rewind_unlocked(self);
|
||||
LEAVE_BUFFERED(self)
|
||||
|
||||
return res;
|
||||
|
@ -791,7 +804,7 @@ buffered_peek(buffered *self, PyObject *args)
|
|||
return NULL;
|
||||
|
||||
if (self->writable) {
|
||||
res = _bufferedwriter_flush_unlocked(self, 1);
|
||||
res = buffered_flush_and_rewind_unlocked(self);
|
||||
if (res == NULL)
|
||||
goto end;
|
||||
Py_CLEAR(res);
|
||||
|
@ -826,19 +839,18 @@ buffered_read(buffered *self, PyObject *args)
|
|||
if (!ENTER_BUFFERED(self))
|
||||
return NULL;
|
||||
res = _bufferedreader_read_all(self);
|
||||
LEAVE_BUFFERED(self)
|
||||
}
|
||||
else {
|
||||
res = _bufferedreader_read_fast(self, n);
|
||||
if (res == Py_None) {
|
||||
Py_DECREF(res);
|
||||
if (!ENTER_BUFFERED(self))
|
||||
return NULL;
|
||||
res = _bufferedreader_read_generic(self, n);
|
||||
LEAVE_BUFFERED(self)
|
||||
}
|
||||
if (res != Py_None)
|
||||
return res;
|
||||
Py_DECREF(res);
|
||||
if (!ENTER_BUFFERED(self))
|
||||
return NULL;
|
||||
res = _bufferedreader_read_generic(self, n);
|
||||
}
|
||||
|
||||
LEAVE_BUFFERED(self)
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -864,13 +876,6 @@ buffered_read1(buffered *self, PyObject *args)
|
|||
if (!ENTER_BUFFERED(self))
|
||||
return NULL;
|
||||
|
||||
if (self->writable) {
|
||||
res = _bufferedwriter_flush_unlocked(self, 1);
|
||||
if (res == NULL)
|
||||
goto end;
|
||||
Py_CLEAR(res);
|
||||
}
|
||||
|
||||
/* Return up to n bytes. If at least one byte is buffered, we
|
||||
only return buffered bytes. Otherwise, we do one raw read. */
|
||||
|
||||
|
@ -890,6 +895,13 @@ buffered_read1(buffered *self, PyObject *args)
|
|||
goto end;
|
||||
}
|
||||
|
||||
if (self->writable) {
|
||||
res = buffered_flush_and_rewind_unlocked(self);
|
||||
if (res == NULL)
|
||||
goto end;
|
||||
Py_DECREF(res);
|
||||
}
|
||||
|
||||
/* Fill the buffer from the raw stream, and copy it to the result. */
|
||||
_bufferedreader_reset_buf(self);
|
||||
r = _bufferedreader_fill_buffer(self);
|
||||
|
@ -912,24 +924,10 @@ end:
|
|||
static PyObject *
|
||||
buffered_readinto(buffered *self, PyObject *args)
|
||||
{
|
||||
PyObject *res = NULL;
|
||||
|
||||
CHECK_INITIALIZED(self)
|
||||
|
||||
/* TODO: use raw.readinto() instead! */
|
||||
if (self->writable) {
|
||||
if (!ENTER_BUFFERED(self))
|
||||
return NULL;
|
||||
res = _bufferedwriter_flush_unlocked(self, 0);
|
||||
LEAVE_BUFFERED(self)
|
||||
if (res == NULL)
|
||||
goto end;
|
||||
Py_DECREF(res);
|
||||
}
|
||||
res = bufferediobase_readinto((PyObject *)self, args);
|
||||
|
||||
end:
|
||||
return res;
|
||||
/* TODO: use raw.readinto() (or a direct copy from our buffer) instead! */
|
||||
return bufferediobase_readinto((PyObject *)self, args);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
|
@ -967,12 +965,6 @@ _buffered_readline(buffered *self, Py_ssize_t limit)
|
|||
goto end_unlocked;
|
||||
|
||||
/* Now we try to get some more from the raw stream */
|
||||
if (self->writable) {
|
||||
res = _bufferedwriter_flush_unlocked(self, 1);
|
||||
if (res == NULL)
|
||||
goto end;
|
||||
Py_CLEAR(res);
|
||||
}
|
||||
chunks = PyList_New(0);
|
||||
if (chunks == NULL)
|
||||
goto end;
|
||||
|
@ -986,9 +978,16 @@ _buffered_readline(buffered *self, Py_ssize_t limit)
|
|||
}
|
||||
Py_CLEAR(res);
|
||||
written += n;
|
||||
self->pos += n;
|
||||
if (limit >= 0)
|
||||
limit -= n;
|
||||
}
|
||||
if (self->writable) {
|
||||
PyObject *r = buffered_flush_and_rewind_unlocked(self);
|
||||
if (r == NULL)
|
||||
goto end;
|
||||
Py_DECREF(r);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
_bufferedreader_reset_buf(self);
|
||||
|
@ -1157,20 +1156,11 @@ buffered_truncate(buffered *self, PyObject *args)
|
|||
return NULL;
|
||||
|
||||
if (self->writable) {
|
||||
res = _bufferedwriter_flush_unlocked(self, 0);
|
||||
res = buffered_flush_and_rewind_unlocked(self);
|
||||
if (res == NULL)
|
||||
goto end;
|
||||
Py_CLEAR(res);
|
||||
}
|
||||
if (self->readable) {
|
||||
if (pos == Py_None) {
|
||||
/* Rewind the raw stream so that its position corresponds to
|
||||
the current logical position. */
|
||||
if (_buffered_raw_seek(self, -RAW_OFFSET(self), 1) == -1)
|
||||
goto end;
|
||||
}
|
||||
_bufferedreader_reset_buf(self);
|
||||
}
|
||||
res = PyObject_CallMethodObjArgs(self->raw, _PyIO_str_truncate, pos, NULL);
|
||||
if (res == NULL)
|
||||
goto end;
|
||||
|
@ -1367,17 +1357,18 @@ _bufferedreader_read_all(buffered *self)
|
|||
Py_DECREF(chunks);
|
||||
return NULL;
|
||||
}
|
||||
self->pos += current_size;
|
||||
}
|
||||
_bufferedreader_reset_buf(self);
|
||||
/* We're going past the buffer's bounds, flush it */
|
||||
if (self->writable) {
|
||||
res = _bufferedwriter_flush_unlocked(self, 1);
|
||||
res = buffered_flush_and_rewind_unlocked(self);
|
||||
if (res == NULL) {
|
||||
Py_DECREF(chunks);
|
||||
return NULL;
|
||||
}
|
||||
Py_CLEAR(res);
|
||||
}
|
||||
_bufferedreader_reset_buf(self);
|
||||
while (1) {
|
||||
if (data) {
|
||||
if (PyList_Append(chunks, data) < 0) {
|
||||
|
@ -1460,6 +1451,14 @@ _bufferedreader_read_generic(buffered *self, Py_ssize_t n)
|
|||
memcpy(out, self->buffer + self->pos, current_size);
|
||||
remaining -= current_size;
|
||||
written += current_size;
|
||||
self->pos += current_size;
|
||||
}
|
||||
/* Flush the write buffer if necessary */
|
||||
if (self->writable) {
|
||||
PyObject *r = buffered_flush_and_rewind_unlocked(self);
|
||||
if (r == NULL)
|
||||
goto error;
|
||||
Py_DECREF(r);
|
||||
}
|
||||
_bufferedreader_reset_buf(self);
|
||||
while (remaining > 0) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue