Skip to content

Commit d929f27

Browse files
naschemeambv
authored andcommitted
wip: add defer_close logic to FileIO
If the file is wrapped by a write buffer, set defer_close to true. That ensures the underlying FileIO object is not closed until the buffer is closed (and can flush the unwritten data). This avoids data loss, as demoed by buffer_not_flushed.py.
1 parent 39bc958 commit d929f27

2 files changed

Lines changed: 22 additions & 0 deletions

File tree

‎Modules/_io/bufferedio.c‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,8 @@ _io__Buffered_closed_get_impl(buffered *self)
545545
return PyObject_GetAttr(self->raw, &_Py_ID(closed));
546546
}
547547

548+
void _PyFileIO_set_defer_close(PyObject *self, bool defer_close);
549+
548550
/*[clinic input]
549551
@critical_section
550552
_io._Buffered.close
@@ -588,6 +590,10 @@ _io__Buffered_close_impl(buffered *self)
588590
exc = PyErr_GetRaisedException();
589591
}
590592

593+
if (self->fast_closed_checks) {
594+
_PyFileIO_set_defer_close(self->raw, false);
595+
}
596+
591597
res = PyObject_CallMethodNoArgs(self->raw, &_Py_ID(close));
592598

593599
if (self->buffer) {
@@ -1972,6 +1978,9 @@ _io_BufferedWriter___init___impl(buffered *self, PyObject *raw,
19721978
Py_IS_TYPE(self, state->PyBufferedWriter_Type) &&
19731979
Py_IS_TYPE(raw, state->PyFileIO_Type)
19741980
);
1981+
if (self->fast_closed_checks) {
1982+
_PyFileIO_set_defer_close(self->raw, true);
1983+
}
19751984

19761985
self->ok = 1;
19771986
return 0;

‎Modules/_io/fileio.c‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ typedef struct {
8282
struct _Py_stat_struct *stat_atopen;
8383
PyObject *weakreflist;
8484
PyObject *dict;
85+
/* if true, we are wrapped by buffer, don't close until buffer is closed */
86+
bool defer_close;
8587
} fileio;
8688

8789
#define PyFileIO_Check(state, op) (PyObject_TypeCheck((op), state->PyFileIO_Type))
@@ -96,6 +98,13 @@ _PyFileIO_closed(PyObject *self)
9698
return (PyFileIO_CAST(self)->fd < 0);
9799
}
98100

101+
void
102+
_PyFileIO_set_defer_close(PyObject *self, bool defer_close)
103+
{
104+
PyFileIO_CAST(self)->defer_close = defer_close;
105+
}
106+
107+
99108
/* Because this can call arbitrary code, it shouldn't be called when
100109
the refcount is 0 (that is, not directly from tp_dealloc unless
101110
the refcount has been temporarily re-incremented). */
@@ -128,6 +137,9 @@ _io_FileIO__dealloc_warn_impl(fileio *self, PyObject *source)
128137
static int
129138
internal_close(fileio *self)
130139
{
140+
if (self->defer_close) {
141+
return 0;
142+
}
131143
int err = 0;
132144
int save_errno = 0;
133145
if (self->fd >= 0) {
@@ -221,6 +233,7 @@ fileio_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
221233
self->stat_atopen = NULL;
222234
self->closefd = 1;
223235
self->weakreflist = NULL;
236+
self->defer_close = false;
224237
return (PyObject *) self;
225238
}
226239

0 commit comments

Comments
 (0)