Skip to content

Commit f7c336f

Browse files
committed
WIP: Forward parent death to descendant processes (unix)
If the forker's connection to the parent BEAM is broken or closed, react by killing all spawned children. When a spawned port is closed, kill the associated OS process. A concise demonstration of the problem being solved is to run the following command with and without the patch, then kill the BEAM. Without the patch, the "sleep" process will continue: erl -noshell -eval 'os:cmd("sleep 60")' TODO: * Needs a decision made between killing the process or process group. * Separate patch for win32
1 parent 68cf02a commit f7c336f

File tree

2 files changed

+27
-0
lines changed

2 files changed

+27
-0
lines changed

erts/emulator/sys/unix/erl_child_setup.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,8 @@ static ssize_t write_all(int fd, const char *buff, size_t size) {
183183
return pos;
184184
}
185185

186+
static void kill_child(pid_t os_pid);
187+
static void kill_all_children(void);
186188
static int forker_hash_init(void);
187189

188190
static int max_files = -1;
@@ -571,6 +573,7 @@ main(int argc, char *argv[])
571573
tcsetattr(0,TCSANOW,&initial_tty_mode);
572574
}
573575
DEBUG_PRINT("erl_child_setup failed to read from uds: %d, %d", res, errno);
576+
kill_all_children();
574577
_exit(0);
575578
}
576579

@@ -579,6 +582,7 @@ main(int argc, char *argv[])
579582
if (isatty(0) && isatty(1)) {
580583
tcsetattr(0,TCSANOW,&initial_tty_mode);
581584
}
585+
kill_all_children();
582586
_exit(0);
583587
}
584588
/* Since we use unix domain sockets and send the entire data in
@@ -621,6 +625,7 @@ main(int argc, char *argv[])
621625
est.os_pid = proto.u.stop.os_pid;
622626
es = hash_remove(forker_hash, &est);
623627
if (es) {
628+
kill_child(es->os_pid);
624629
free(es);
625630
}
626631
} else {
@@ -660,6 +665,23 @@ main(int argc, char *argv[])
660665
return 1;
661666
}
662667

668+
/* Kill child process groups on VM termination so they don't become orphaned. */
669+
670+
static void kill_child(pid_t os_pid) {
671+
if (os_pid > 0 && kill(os_pid, SIGTERM) != 0) {
672+
DEBUG_PRINT("error killing process %d: %d", os_pid, errno);
673+
}
674+
}
675+
676+
static void fun_kill_foreach(ErtsSysExitStatus *es, void *unused) {
677+
kill_child(es->os_pid);
678+
}
679+
680+
static void kill_all_children(void) {
681+
DEBUG_PRINT("cleaning up by killing all %d child processes", forker_hash->nobjs);
682+
hash_foreach(forker_hash, (HFOREACH_FUN)fun_kill_foreach, NULL);
683+
}
684+
663685
static int fcmp(void *a, void *b)
664686
{
665687
ErtsSysExitStatus *sa = a;

erts/preloaded/src/erlang.erl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7475,6 +7475,11 @@ reported to the owning process using signals of the form
74757475

74767476
The maximum number of ports that can be open at the same time can be configured
74777477
by passing command-line flag [`+Q`](erl_cmd.md#max_ports) to [erl](erl_cmd.md).
7478+
7479+
When a port is closed or the VM shuts down, spawned executables are sent a
7480+
`SIGTERM` on unix. The child may still outlive the VM if it traps the signal.
7481+
Note that any processes started under a shell using `spawn` will not terminate
7482+
unless they respond to stdin or stdout being closed.
74787483
""".
74797484
-doc #{ category => ports }.
74807485
-spec open_port(PortName, PortSettings) -> port() when

0 commit comments

Comments
 (0)