diff options
Diffstat (limited to 'Modules/posixmodule.c')
| -rw-r--r-- | Modules/posixmodule.c | 184 |
1 files changed, 154 insertions, 30 deletions
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 5c739180ea..be8a66dd50 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -25,6 +25,7 @@ #define PY_SSIZE_T_CLEAN #include "Python.h" +#include "pythread.h" #include "structmember.h" #ifndef MS_WINDOWS #include "posixmodule.h" @@ -394,6 +395,95 @@ static int win32_can_symlink = 0; #define MODNAME "posix" #endif + +#ifdef HAVE_FORK +static void +run_at_forkers(PyObject *lst, int reverse) +{ + Py_ssize_t i; + PyObject *cpy; + + if (lst != NULL) { + assert(PyList_CheckExact(lst)); + + /* Use a list copy in case register_at_fork() is called from + * one of the callbacks. + */ + cpy = PyList_GetSlice(lst, 0, PyList_GET_SIZE(lst)); + if (cpy == NULL) + PyErr_WriteUnraisable(lst); + else { + if (reverse) + PyList_Reverse(cpy); + for (i = 0; i < PyList_GET_SIZE(cpy); i++) { + PyObject *func, *res; + func = PyList_GET_ITEM(cpy, i); + res = PyObject_CallObject(func, NULL); + if (res == NULL) + PyErr_WriteUnraisable(func); + else + Py_DECREF(res); + } + Py_DECREF(cpy); + } + } +} + +void +PyOS_BeforeFork(void) +{ + run_at_forkers(PyThreadState_Get()->interp->before_forkers, 1); + + _PyImport_AcquireLock(); +} + +void +PyOS_AfterFork_Parent(void) +{ + if (_PyImport_ReleaseLock() <= 0) + Py_FatalError("failed releasing import lock after fork"); + + run_at_forkers(PyThreadState_Get()->interp->after_forkers_parent, 0); +} + +void +PyOS_AfterFork_Child(void) +{ +#ifdef WITH_THREAD + /* PyThread_ReInitTLS() must be called early, to make sure that the TLS API + * can be called safely. */ + PyThread_ReInitTLS(); + _PyGILState_Reinit(); + PyEval_ReInitThreads(); + _PyImport_ReInitLock(); +#endif + _PySignal_AfterFork(); + + run_at_forkers(PyThreadState_Get()->interp->after_forkers_child, 0); +} + +static int +register_at_forker(PyObject **lst, PyObject *func) +{ + if (*lst == NULL) { + *lst = PyList_New(0); + if (*lst == NULL) + return -1; + } + return PyList_Append(*lst, func); +} +#endif + +/* Legacy wrapper */ +void +PyOS_AfterFork(void) +{ +#ifdef HAVE_FORK + PyOS_AfterFork_Child(); +#endif +} + + #ifdef MS_WINDOWS /* defined in fileutils.c */ PyAPI_FUNC(void) _Py_time_t_to_FILE_TIME(time_t, int, FILETIME *); @@ -5218,6 +5308,57 @@ os_spawnve_impl(PyObject *module, int mode, path_t *path, PyObject *argv, #endif /* HAVE_SPAWNV */ +#ifdef HAVE_FORK +/*[clinic input] +os.register_at_fork + + func: object + Function or callable + / + when: str + 'before', 'child' or 'parent' + +Register a callable object to be called when forking. + +'before' callbacks are called in reverse order before forking. +'child' callbacks are called in order after forking, in the child process. +'parent' callbacks are called in order after forking, in the parent process. + +[clinic start generated code]*/ + +static PyObject * +os_register_at_fork_impl(PyObject *module, PyObject *func, const char *when) +/*[clinic end generated code: output=8943be81a644750c input=5fc05efa4d42eb84]*/ +{ + PyInterpreterState *interp; + PyObject **lst; + + if (!PyCallable_Check(func)) { + PyErr_Format(PyExc_TypeError, + "expected callable object, got %R", Py_TYPE(func)); + return NULL; + } + interp = PyThreadState_Get()->interp; + + if (!strcmp(when, "before")) + lst = &interp->before_forkers; + else if (!strcmp(when, "child")) + lst = &interp->after_forkers_child; + else if (!strcmp(when, "parent")) + lst = &interp->after_forkers_parent; + else { + PyErr_Format(PyExc_ValueError, "unexpected value for `when`: '%s'", + when); + return NULL; + } + if (register_at_forker(lst, func)) + return NULL; + else + Py_RETURN_NONE; +} +#endif /* HAVE_FORK */ + + #ifdef HAVE_FORK1 /*[clinic input] os.fork1 @@ -5232,24 +5373,18 @@ os_fork1_impl(PyObject *module) /*[clinic end generated code: output=0de8e67ce2a310bc input=12db02167893926e]*/ { pid_t pid; - int result = 0; - _PyImport_AcquireLock(); + + PyOS_BeforeFork(); pid = fork1(); if (pid == 0) { /* child: this clobbers and resets the import lock. */ - PyOS_AfterFork(); + PyOS_AfterFork_Child(); } else { /* parent: release the import lock. */ - result = _PyImport_ReleaseLock(); + PyOS_AfterFork_Parent(); } if (pid == -1) return posix_error(); - if (result < 0) { - /* Don't clobber the OSError if the fork failed. */ - PyErr_SetString(PyExc_RuntimeError, - "not holding the import lock"); - return NULL; - } return PyLong_FromPid(pid); } #endif /* HAVE_FORK1 */ @@ -5269,24 +5404,18 @@ os_fork_impl(PyObject *module) /*[clinic end generated code: output=3626c81f98985d49 input=13c956413110eeaa]*/ { pid_t pid; - int result = 0; - _PyImport_AcquireLock(); + + PyOS_BeforeFork(); pid = fork(); if (pid == 0) { /* child: this clobbers and resets the import lock. */ - PyOS_AfterFork(); + PyOS_AfterFork_Child(); } else { /* parent: release the import lock. */ - result = _PyImport_ReleaseLock(); + PyOS_AfterFork_Parent(); } if (pid == -1) return posix_error(); - if (result < 0) { - /* Don't clobber the OSError if the fork failed. */ - PyErr_SetString(PyExc_RuntimeError, - "not holding the import lock"); - return NULL; - } return PyLong_FromPid(pid); } #endif /* HAVE_FORK */ @@ -5868,26 +5997,20 @@ static PyObject * os_forkpty_impl(PyObject *module) /*[clinic end generated code: output=60d0a5c7512e4087 input=f1f7f4bae3966010]*/ { - int master_fd = -1, result = 0; + int master_fd = -1; pid_t pid; - _PyImport_AcquireLock(); + PyOS_BeforeFork(); pid = forkpty(&master_fd, NULL, NULL, NULL); if (pid == 0) { /* child: this clobbers and resets the import lock. */ - PyOS_AfterFork(); + PyOS_AfterFork_Child(); } else { /* parent: release the import lock. */ - result = _PyImport_ReleaseLock(); + PyOS_AfterFork_Parent(); } if (pid == -1) return posix_error(); - if (result < 0) { - /* Don't clobber the OSError if the fork failed. */ - PyErr_SetString(PyExc_RuntimeError, - "not holding the import lock"); - return NULL; - } return Py_BuildValue("(Ni)", PyLong_FromPid(pid), master_fd); } #endif /* HAVE_FORKPTY */ @@ -12265,6 +12388,7 @@ static PyMethodDef posix_methods[] = { OS_SPAWNVE_METHODDEF OS_FORK1_METHODDEF OS_FORK_METHODDEF + OS_REGISTER_AT_FORK_METHODDEF OS_SCHED_GET_PRIORITY_MAX_METHODDEF OS_SCHED_GET_PRIORITY_MIN_METHODDEF OS_SCHED_GETPARAM_METHODDEF |
