Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions erts/emulator/sys/unix/erl_child_setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ static ssize_t write_all(int fd, const char *buff, size_t size) {
return pos;
}

static void kill_all_children(void);
static int forker_hash_init(void);

static int max_files = -1;
Expand Down Expand Up @@ -571,6 +572,7 @@ main(int argc, char *argv[])
tcsetattr(0,TCSANOW,&initial_tty_mode);
}
DEBUG_PRINT("erl_child_setup failed to read from uds: %d, %d", res, errno);
kill_all_children();
_exit(0);
}

Expand All @@ -579,6 +581,7 @@ main(int argc, char *argv[])
if (isatty(0) && isatty(1)) {
tcsetattr(0,TCSANOW,&initial_tty_mode);
}
kill_all_children();
_exit(0);
}
/* Since we use unix domain sockets and send the entire data in
Expand Down Expand Up @@ -662,6 +665,21 @@ main(int argc, char *argv[])
return 1;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be unreachable.

Actually, I'm uncertain about some of the ABORT exits: my thinking is that these indicate pathological corner cases where we can no longer trust the internal tracking nor child pipes, so it would be useless or even risky to try "atexit"-like cleanup behaviors. This is supported by documentation for glibc abort:

Up until glibc 2.26, if the abort() function caused process termination, all open streams were closed and flushed (as with fclose(3)). However, in some cases this could result in deadlocks and data corruption. Therefore, starting with glibc 2.27, abort() terminates the process without flushing streams. POSIX.1 permits either possible behavior, saying that abort() "may include an attempt to effect fclose() on all open streams".

That's how I feel, too.

}

static void kill_child(pid_t os_pid) {
if (os_pid > 0 && kill(os_pid, SIGTERM) != 0) {
DEBUG_PRINT("error killing process %d: %d", os_pid, errno);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should always work, but if it doesn't because of eg. a race condition between the child dying on its own and killing it, continue with trying to kill the other children.

}
}

static void fun_kill_foreach(ErtsSysExitStatus *es, void *unused) {
kill_child(es->os_pid);
}

static void kill_all_children(void) {
DEBUG_PRINT("cleaning up by killing all %d child processes", forker_hash->nobjs);
hash_foreach(forker_hash, (HFOREACH_FUN)fun_kill_foreach, NULL);
}

static int fcmp(void *a, void *b)
{
ErtsSysExitStatus *sa = a;
Expand Down
5 changes: 5 additions & 0 deletions erts/preloaded/src/erlang.erl
Original file line number Diff line number Diff line change
Expand Up @@ -7475,6 +7475,11 @@ reported to the owning process using signals of the form

The maximum number of ports that can be open at the same time can be configured
by passing command-line flag [`+Q`](erl_cmd.md#max_ports) to [erl](erl_cmd.md).

When the VM shuts down, spawned executables are sent `SIGTERM` on unix. The
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"unix" -> "POSIX"? Hopefully this can be reimplemented for win32 in later work, anyway. Is it okay to have divergent behavior on the platforms?

child may still outlive the VM if it traps the signal. Note that any processes
started under a shell using `spawn` will not terminate unless they respond to
stdin or stdout being closed.
""".
-doc #{ category => ports }.
-spec open_port(PortName, PortSettings) -> port() when
Expand Down
Loading