Skip to content

Commit f8e27c0

Browse files
committed
Fixed a deadlock issue in the bufferedIO module when using fork in Python
1 parent 58d305c commit f8e27c0

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
@@ -737,6 +737,8 @@ static PyObject *
737737
_bufferedreader_read_generic(buffered *self, Py_ssize_t);
738738
static Py_ssize_t
739739
_bufferedreader_raw_read(buffered *self, char *start, Py_ssize_t len);
740+
static int
741+
_buffered_init(buffered *self);
740742

741743
/*
742744
* Helpers
@@ -825,6 +827,60 @@ _buffered_raw_seek(buffered *self, Py_off_t target, int whence)
825827
return n;
826828
}
827829

830+
#ifdef HAVE_FORK
831+
832+
static PyObject*
833+
buffered_after_fork_child_impl(PyObject *self, PyObject *Py_UNUSED(ignored))
834+
{
835+
buffered *buf = (buffered *)self;
836+
if (_buffered_init(buf) < 0) {
837+
PyErr_SetString(PyExc_RuntimeError, "Failed to initialize buffer after fork");
838+
return NULL;
839+
}
840+
_bufferedreader_reset_buf(buf);
841+
Py_RETURN_NONE;
842+
}
843+
844+
static PyMethodDef buffered_fork_methods[] = {
845+
{"_after_fork_child", buffered_after_fork_child_impl, METH_NOARGS, NULL},
846+
{NULL, NULL}
847+
};
848+
849+
static int
850+
buffered_register_at_fork(buffered *self)
851+
{
852+
PyInterpreterState *interp = PyThreadState_Get()->interp;
853+
if (!interp) {
854+
PyErr_SetString(PyExc_RuntimeError, "Failed to get interpreter state");
855+
return -1;
856+
}
857+
858+
// Only register the fork handlers once
859+
if (!interp->after_forkers_child) {
860+
interp->after_forkers_child = PyList_New(0);
861+
if (!interp->after_forkers_child) {
862+
return -1;
863+
}
864+
}
865+
866+
/* Create method objects */
867+
PyObject *after_child = PyCFunction_New(&buffered_fork_methods[0], (PyObject *)self);
868+
if (!after_child) {
869+
return -1;
870+
}
871+
872+
/* Append callbacks to the lists */
873+
int status = 0;
874+
if (PyList_Append(interp->after_forkers_child, after_child) < 0) {
875+
status = -1;
876+
}
877+
878+
Py_DECREF(after_child);
879+
return status;
880+
}
881+
882+
#endif /* HAVE_FORK */
883+
828884
static int
829885
_buffered_init(buffered *self)
830886
{
@@ -859,6 +915,16 @@ _buffered_init(buffered *self)
859915
self->buffer_mask = 0;
860916
if (_buffered_raw_tell(self) == -1)
861917
PyErr_Clear();
918+
919+
#ifdef HAVE_FORK
920+
/* Register fork handlers */
921+
if (buffered_register_at_fork(self) < 0) {
922+
PyThread_free_lock(self->lock);
923+
self->lock = NULL;
924+
return -1;
925+
}
926+
#endif /* HAVE_FORK */
927+
862928
return 0;
863929
}
864930

0 commit comments

Comments
 (0)