Kill child process and close pty on exit

- Adds kill() method to LibuvUtils.
- Adds close() method to Pipe.
This commit is contained in:
Leroy Hopson 2021-07-22 12:17:23 +07:00
parent 55b0a0577d
commit c81da3820b
No known key found for this signature in database
GPG key ID: D2747312A6DB51AA
10 changed files with 53 additions and 9 deletions

View file

@ -88,5 +88,4 @@ func _input(event):
func _on_PTY_exited(exit_code: int, signum: int): func _on_PTY_exited(exit_code: int, signum: int):
print("exit_code: ", exit_code, " signum: ", signum)
emit_signal("exited", exit_code, signum) emit_signal("exited", exit_code, signum)

View file

@ -9,6 +9,8 @@ void LibuvUtils::_register_methods() {
register_method("get_os_environ", &LibuvUtils::get_os_environ); register_method("get_os_environ", &LibuvUtils::get_os_environ);
register_method("get_os_release", &LibuvUtils::get_os_release); register_method("get_os_release", &LibuvUtils::get_os_release);
register_method("get_cwd", &LibuvUtils::get_cwd); register_method("get_cwd", &LibuvUtils::get_cwd);
register_method("kill", &LibuvUtils::kill);
} }
LibuvUtils::LibuvUtils() {} LibuvUtils::LibuvUtils() {}
@ -61,6 +63,10 @@ String LibuvUtils::get_cwd() {
return result; return result;
} }
godot_error LibuvUtils::kill(int pid, int signum) {
RETURN_UV_ERR(uv_kill(pid, signum));
}
godot_error LibuvUtils::translate_uv_errno(int uv_err) { godot_error LibuvUtils::translate_uv_errno(int uv_err) {
if (uv_err >= 0) if (uv_err >= 0)
return GODOT_OK; return GODOT_OK;

View file

@ -9,7 +9,9 @@
String(uv_strerror(uv_err))); String(uv_strerror(uv_err)));
#define RETURN_UV_ERR(uv_err) \ #define RETURN_UV_ERR(uv_err) \
UV_ERR_PRINT(uv_err); \ if (uv_err < 0) { \
UV_ERR_PRINT(uv_err); \
} \
return LibuvUtils::translate_uv_errno(uv_err); return LibuvUtils::translate_uv_errno(uv_err);
#define RETURN_IF_UV_ERR(uv_err) \ #define RETURN_IF_UV_ERR(uv_err) \
@ -34,6 +36,8 @@ public:
String get_os_release(); String get_os_release();
String get_cwd(); String get_cwd();
godot_error kill(int pid, int signum);
public: public:
static godot_error translate_uv_errno(int uv_err); static godot_error translate_uv_errno(int uv_err);
}; };

View file

@ -31,7 +31,7 @@ void Pipe::_register_methods() {
} }
Pipe::Pipe() {} Pipe::Pipe() {}
Pipe::~Pipe() {} Pipe::~Pipe() { close(); }
void Pipe::_init() {} void Pipe::_init() {}
@ -39,6 +39,8 @@ void _poll_connection();
void _read_cb(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf); void _read_cb(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf);
void _close_cb(uv_handle_t *handle);
void _write_cb(uv_write_t *req, int status); void _write_cb(uv_write_t *req, int status);
void _alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf); void _alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf);
@ -56,6 +58,11 @@ godot_error Pipe::open(int fd, bool ipc = false) {
return GODOT_OK; return GODOT_OK;
} }
void Pipe::close() {
uv_close((uv_handle_t *)&handle, NULL);
uv_run(uv_default_loop(), UV_RUN_NOWAIT);
}
godot_error Pipe::write(String p_data) { godot_error Pipe::write(String p_data) {
char *s = p_data.alloc_c_string(); char *s = p_data.alloc_c_string();
ULONG len = strlen(s); ULONG len = strlen(s);
@ -88,10 +95,10 @@ void _read_cb(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf) {
switch (nread) { switch (nread) {
case UV_EOF: case UV_EOF:
// Normal after shell exits. // Normal after shell exits.
return;
case UV_EIO: case UV_EIO:
// Can happen when the process exits. // Can happen when the process exits.
// As long as PTY has caught it, we should be fine. // As long as PTY has caught it, we should be fine.
uv_read_stop(handle);
return; return;
default: default:
UV_ERR_PRINT(nread); UV_ERR_PRINT(nread);

View file

@ -23,6 +23,7 @@ public:
void _init(); void _init();
godot_error open(int fd, bool ipc); godot_error open(int fd, bool ipc);
void close();
int get_status(); int get_status();
godot_error write(String p_data); godot_error write(String p_data);

View file

@ -25,5 +25,9 @@ static func get_windows_build_number() -> int:
assert(false, "Not implemented.") assert(false, "Not implemented.")
return 0 return 0
static func kill(pid: int, signum: int):
if pid > 1:
return LibuvUtils.new().kill(pid, signum)
static func new(): static func new():
assert(false, "Abstract sealed (i.e. static) class should not be instantiated.") assert(false, "Abstract sealed (i.e. static) class should not be instantiated.")

View file

@ -212,8 +212,11 @@ func destroy() -> void:
# Kill the pty. # Kill the pty.
# sigint: The signal to send. By default this is SIGHUP. # sigint: The signal to send. By default this is SIGHUP.
# This is not supported on Windows. # This is not supported on Windows.
func kill(sigint: int = Signal.SIGHUP) -> void: func kill(signum: int = Signal.SIGHUP) -> void:
pass if _pipe:
_pipe.close()
if pid > 0:
LibuvUtils.kill(pid, signum)
func fork( func fork(

View file

@ -42,7 +42,6 @@ func _resize(cols: int, rows: int) -> void:
func _fork_thread(args): func _fork_thread(args):
var result = preload("./pty_unix.gdns").new().callv("fork", args) var result = preload("./pty_unix.gdns").new().callv("fork", args)
print(result)
return result return result
@ -88,6 +87,8 @@ func fork(
status = STATUS_ERROR status = STATUS_ERROR
return FAILED return FAILED
pid = result[1].pid
_pipe = Pipe.new() _pipe = Pipe.new()
_pipe.open(_fd) _pipe.open(_fd)
@ -101,12 +102,20 @@ func open(cols: int = DEFAULT_COLS, rows: int = DEFAULT_ROWS) -> Array:
return PTYUnix.new().open(cols, rows) return PTYUnix.new().open(cols, rows)
func _exit_tree():
_exit_cb = null
if pid > 1:
LibuvUtils.kill(pid, Signal.SIGHUP)
func _on_pipe_data_received(data): func _on_pipe_data_received(data):
emit_signal("data_received", data) emit_signal("data_received", data)
func _on_exit(exit_code: int, signum: int) -> void: func _on_exit(exit_code: int, signum: int) -> void:
emit_signal("exited", exit_code, signum) if is_instance_valid(self):
pid = -1
emit_signal("exited", exit_code, signum)
func _sanitize_env(env: Dictionary) -> Dictionary: func _sanitize_env(env: Dictionary) -> Dictionary:

View file

@ -20,5 +20,4 @@ script = ExtResource( 1 )
__meta__ = { __meta__ = {
"_edit_use_anchors_": false "_edit_use_anchors_": false
} }
[connection signal="key_pressed" from="Terminal" to="." method="_on_Terminal_key_pressed"] [connection signal="key_pressed" from="Terminal" to="." method="_on_Terminal_key_pressed"]

View file

@ -1,5 +1,7 @@
extends "res://addons/gut/test.gd" extends "res://addons/gut/test.gd"
const LibuvUtils := preload("res://addons/godot_xterm/nodes/pty/libuv_utils.gd")
var pty: GDXterm.PTYUnix var pty: GDXterm.PTYUnix
var helper: Helper var helper: Helper
@ -51,6 +53,16 @@ func test_open_pty_has_correct_win_size():
assert_eq(winsize.rows, rows) assert_eq(winsize.rows, rows)
func test_closes_pty_on_exit():
var num_pts = helper._get_pts().size()
pty.fork("sleep", ["1000"])
remove_child(pty)
pty.free()
yield(yield_for(1), YIELD)
var new_num_pts = helper._get_pts().size()
assert_eq(new_num_pts, num_pts)
class Helper: class Helper:
static func _get_pts() -> Array: static func _get_pts() -> Array:
assert(false, "Abstract method") assert(false, "Abstract method")