summaryrefslogtreecommitdiff
path: root/Modules/posixmodule.c
diff options
context:
space:
mode:
Diffstat (limited to 'Modules/posixmodule.c')
-rw-r--r--Modules/posixmodule.c184
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