Skip to content

Commit e46e1de

Browse files
committed
Fixed a deadlock issue in the bufferedIO module when using fork in Python
1 parent aeb9b65 commit e46e1de

1 file changed

Lines changed: 66 additions & 0 deletions

File tree

Modules/_io/bufferedio.c

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,8 @@ static PyObject *
731731
_bufferedreader_read_generic(buffered *self, Py_ssize_t);
732732
static Py_ssize_t
733733
_bufferedreader_raw_read(buffered *self, char *start, Py_ssize_t len);
734+
static int
735+
_buffered_init(buffered *self);
734736

735737
/*
736738
* Helpers
@@ -819,6 +821,60 @@ _buffered_raw_seek(buffered *self, Py_off_t target, int whence)
819821
return n;
820822
}
821823

824+
#ifdef HAVE_FORK
825+
826+
static PyObject*
827+
buffered_after_fork_child_impl(PyObject *self, PyObject *Py_UNUSED(ignored))
828+
{
829+
buffered *buf = (buffered *)self;
830+
if (_buffered_init(buf) < 0) {
831+
PyErr_SetString(PyExc_RuntimeError, "Failed to initialize buffer after fork");
832+
return NULL;
833+
}
834+
_bufferedreader_reset_buf(buf);
835+
Py_RETURN_NONE;
836+
}
837+
838+
static PyMethodDef buffered_fork_methods[] = {
839+
{"_after_fork_child", buffered_after_fork_child_impl, METH_NOARGS, NULL},
840+
{NULL, NULL}
841+
};
842+
843+
static int
844+
buffered_register_at_fork(buffered *self)
845+
{
846+
PyInterpreterState *interp = PyThreadState_Get()->interp;
847+
if (!interp) {
848+
PyErr_SetString(PyExc_RuntimeError, "Failed to get interpreter state");
849+
return -1;
850+
}
851+
852+
// Only register the fork handlers once
853+
if (!interp->after_forkers_child) {
854+
interp->after_forkers_child = PyList_New(0);
855+
if (!interp->after_forkers_child) {
856+
return -1;
857+
}
858+
}
859+
860+
/* Create method objects */
861+
PyObject *after_child = PyCFunction_New(&buffered_fork_methods[0], (PyObject *)self);
862+
if (!after_child) {
863+
return -1;
864+
}
865+
866+
/* Append callbacks to the lists */
867+
int status = 0;
868+
if (PyList_Append(interp->after_forkers_child, after_child) < 0) {
869+
status = -1;
870+
}
871+
872+
Py_DECREF(after_child);
873+
return status;
874+
}
875+
876+
#endif /* HAVE_FORK */
877+
822878
static int
823879
_buffered_init(buffered *self)
824880
{
@@ -853,6 +909,16 @@ _buffered_init(buffered *self)
853909
self->buffer_mask = 0;
854910
if (_buffered_raw_tell(self) == -1)
855911
PyErr_Clear();
912+
913+
#ifdef HAVE_FORK
914+
/* Register fork handlers */
915+
if (buffered_register_at_fork(self) < 0) {
916+
PyThread_free_lock(self->lock);
917+
self->lock = NULL;
918+
return -1;
919+
}
920+
#endif /* HAVE_FORK */
921+
856922
return 0;
857923
}
858924

0 commit comments

Comments
 (0)