summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2013-11-30 22:47:17 +0100
committerwm4 <wm4@nowhere>2013-11-30 22:47:26 +0100
commit9d8573cfe85fa1810ec6a18c584e920b47da1dad (patch)
tree729de7d7c13b8e7908c7bd77e42dd010cda7bb2e
parent95cfe58e3db9d939abe7a9a26116c1d576eed60b (diff)
downloadmpv-9d8573cfe85fa1810ec6a18c584e920b47da1dad.tar.bz2
mpv-9d8573cfe85fa1810ec6a18c584e920b47da1dad.tar.xz
Prevent creating zombies
The "run" input command does fork+exec to spawn child processes. But it doesn't cleanup the child processes, so they are left as zombies until mpv terminates. Leaving zombie processes around is not very nice, so employ a simple trick to let pid 1 take care of this: we fork twice, and when the first fork exits, the second fork becomes orphaned and becomes pid 1's child. It becomes pid 1's responsibility to cleanup the process. The advantage is that we don't need extra logic to cleanup the spawned process, which could have an arbitrary lifetime. This is e.g. described here: http://yarchive.net/comp/zombie_process.html Also use _exit() instead of exit(). It's not really sane to run cleanup handlers (atexit() etc.) inside a forked process.
-rw-r--r--mpvcore/player/command.c17
1 files changed, 14 insertions, 3 deletions
diff --git a/mpvcore/player/command.c b/mpvcore/player/command.c
index 129c0aa58a..ae1ba3ca31 100644
--- a/mpvcore/player/command.c
+++ b/mpvcore/player/command.c
@@ -23,6 +23,8 @@
#include <stdbool.h>
#include <assert.h>
#include <time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
#include <libavutil/avstring.h>
#include <libavutil/common.h>
@@ -3028,10 +3030,19 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
char *args[MP_CMD_MAX_ARGS + 1] = {0};
for (int n = 0; n < cmd->nargs; n++)
args[n] = cmd->args[n].v.s;
- if (!fork()) {
- execvp(args[0], args);
- exit(0);
+ pid_t child = fork();
+ if (child == 0) {
+ // Fork twice; the second process will be made child of pid 1 as
+ // soon as the first process exists, and we don't have to care
+ // about having to wait for the second process to terminate.
+ if (fork() == 0) {
+ execvp(args[0], args);
+ _exit(0);
+ }
+ _exit(0);
}
+ int st;
+ while (child != -1 && waitpid(child, &st, 0) < 0 && errno == EINTR) {}
#endif
break;
}