Skip to content

Commit

Permalink
Write a test for child process cleanup on VM crash
Browse files Browse the repository at this point in the history
TODO: Needs to be tested on win32
  • Loading branch information
adamwight committed Feb 22, 2025
1 parent d756e8e commit 8efd4f5
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 1 deletion.
33 changes: 33 additions & 0 deletions erts/emulator/test/port_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@
stream_small/1,
t_binary/1,
t_exit/1,
terminate_children/1,
tps_16_bytes/1,
tps_1K/1,
unregister_name/1,
Expand Down Expand Up @@ -178,6 +179,7 @@ all() ->
mix_up_ports, otp_5112, otp_5119,
exit_status_multi_scheduling_block, ports, spawn_driver,
spawn_executable, close_deaf_port, unregister_name,
terminate_children,
port_setget_data,
parallelism_option,
mon_port_invalid_type,
Expand Down Expand Up @@ -1699,6 +1701,37 @@ spawn_executable(Config) when is_list(Config) ->
end,
ok.


terminate_children(Config) when is_list(Config) ->
DataDir = proplists:get_value(data_dir, Config),
PrivDir = proplists:get_value(priv_dir, Config),
FileWhileAlive = os:find_executable("file_while_alive", DataDir),
TmpFile = filename:join(PrivDir, "file_while_alive_file"),
% TODO: Is this guaranteed to find the erl under test?
Port = open_port({spawn_executable, os:find_executable("erl")}, [
{args, [
"-noshell",
"-eval",
"Port_1 = open_port(" ++
"{spawn_executable, \"" ++ FileWhileAlive ++ "\"}," ++
"[{args, [\"" ++ TmpFile ++ "\"]}, use_stdio, in])," ++
"receive {Port_1, {data, \"waiting.\"}} -> ok " ++
"after 10000 -> error(no_child) end," ++
"halt()."
]},
exit_status, in
]),
true = is_port(Port),
receive
{Port, {exit_status, 0}} -> ok;
{Port, {exit_status, 1}} -> ?assert(false)
after 10000 -> ?assert(false) end,
% TODO: receive "removed." message rather than sleep.
receive after 1000 -> ok end,
{error, enoent} = file:read_file(TmpFile),
ok.


unregister_name(Config) when is_list(Config) ->
Cmd = case os:getenv("WSLENV") of
false -> "sleep 5";
Expand Down
8 changes: 7 additions & 1 deletion erts/emulator/test/port_SUITE_data/Makefile.src
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ LD = @LD@
CFLAGS = @CFLAGS@ -I@erl_include@ @DEFS@
CROSSLDFLAGS = @CROSSLDFLAGS@

PROGS = port_test@exe@ echo_args@exe@ dead_port@exe@
PROGS = port_test@exe@ echo_args@exe@ dead_port@exe@ file_while_alive@exe@
DRIVERS = echo_drv@dll@ exit_drv@dll@ failure_drv@dll@ sleep_failure_drv@dll@

all: $(PROGS) $(DRIVERS) port_test.@EMULATOR@
Expand All @@ -26,6 +26,12 @@ dead_port@exe@: dead_port@obj@
dead_port@obj@: dead_port.c
$(CC) -c -o dead_port@obj@ $(CFLAGS) dead_port.c

file_while_alive@exe@: file_while_alive@obj@
$(LD) $(CROSSLDFLAGS) -o file_while_alive file_while_alive@obj@ @LIBS@

file_while_alive@obj@: file_while_alive.c
$(CC) -c -o file_while_alive@obj@ $(CFLAGS) file_while_alive.c

port_test.@EMULATOR@: port_test.erl
@erl_name@ -compile port_test

Expand Down
86 changes: 86 additions & 0 deletions erts/emulator/test/port_SUITE_data/file_while_alive.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Test utility which waits for SIGTERM and responds by deleting a temp file
* path.
*/
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>

#ifndef __WIN32__
#include <unistd.h>

#define O_BINARY 0
#define _setmode(fd, mode)
#endif

#ifdef __WIN32__
#include "windows.h"
#include "winbase.h"
#endif

extern int errno;

const char* MSG_WAITING = "waiting.";
int ERL_FD = 1;

char* tmp_file;

static void handle_sigterm(int sig);
static void delay(unsigned ms);
static void write_erl(const char* buf, unsigned size);

int main(int argc, char *argv[]) {
if (argc != 2)
exit(1);

tmp_file = argv[1];

struct sigaction sa;
sa.sa_handler = &handle_sigterm;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (sigaction(SIGTERM, &sa, 0) == -1) {
fprintf(stderr, "Failed to set signal handler: %d\n", errno);
exit(1);
}

_setmode(ERL_FD, O_BINARY);

write_erl(MSG_WAITING, sizeof(MSG_WAITING));
/* Wait 60s for SIGTERM. */
delay(60000);

fprintf(stderr, "Failure to stop child process.\n");

return 1;
}

static void
write_erl(const char* buf, unsigned size) {
if (size != write(ERL_FD, buf, size)) {
fprintf(stderr, "Failed to write message to erlang (%d): %s\n", errno, buf);
exit(1);
}
}

static void
handle_sigterm(int sig) {
unlink(tmp_file);
/* TODO: Ideally we send a message to the grandparent process here. */
}

static void
delay(unsigned ms)
{
#ifdef __WIN32__
Sleep(ms);
#else
struct timeval t;
t.tv_sec = ms/1000;
t.tv_usec = (ms % 1000) * 1000;

select(0, NULL, NULL, NULL, &t);
#endif
}

0 comments on commit 8efd4f5

Please sign in to comment.