From 4728d102d94e483a4dda951eb104200c9282bd89 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Sun, 9 May 2021 21:31:04 +0300 Subject: [PATCH] os: add Process.finalise() and Process.free() methods to cleanup pipe descriptors --- cmd/tools/vwatch.v | 7 +++++-- vlib/os/process.v | 30 ++++++++++++++++++++++++++++++ vlib/os/process_test.v | 3 +++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/cmd/tools/vwatch.v b/cmd/tools/vwatch.v index 17f96023f5..d5d4a21025 100644 --- a/cmd/tools/vwatch.v +++ b/cmd/tools/vwatch.v @@ -245,6 +245,7 @@ fn (mut context Context) compilation_runner_loop() { } if !context.child_process.is_alive() { context.child_process.wait() + context.child_process.close() break } } @@ -315,18 +316,20 @@ fn (mut context Context) manager_main() { time.sleep(200 * time.millisecond) } if !(worker_process.code == 255 && worker_process.status == .exited) { + worker_process.close() break } + worker_process.close() } } fn (mut context Context) worker_main() { context.rerun_channel = chan RerunCommand{cap: 10} - os.signal(C.SIGINT, fn () { + os.signal_opt(.int, fn (_ os.Signal) { mut context := unsafe { &Context(voidptr(&ccontext)) } context.is_exiting = true context.kill_pgroup() - }) + }) or { panic(err) } go context.compilation_runner_loop() change_detection_loop(context) } diff --git a/vlib/os/process.v b/vlib/os/process.v index a9df41a1b2..8fa5e765b4 100644 --- a/vlib/os/process.v +++ b/vlib/os/process.v @@ -5,12 +5,14 @@ module os // ProcessState.stopped - the process was running, but was stopped temporarily // ProcessState.exited - the process has finished/exited // ProcessState.aborted - the process was terminated by a signal +// ProcessState.closed - the process resources like opened file descriptors were freed/discarded, final state. pub enum ProcessState { not_started running stopped exited aborted + closed } [heap] @@ -132,6 +134,34 @@ pub fn (mut p Process) wait() { return } +// close - free the OS resources associated with the process. +// Can be called multiple times, but will free the resources just once. +// This sets the process state to .closed, which is final. +pub fn (mut p Process) close() { + if p.status in [.not_started, .closed] { + return + } + p.status = .closed + $if !windows { + for i in 0 .. 3 { + if p.stdio_fd[i] != 0 { + fd_close(p.stdio_fd[i]) + } + } + } +} + +[unsafe] +pub fn (mut p Process) free() { + p.close() + unsafe { + p.filename.free() + p.err.free() + p.args.free() + p.env.free() + } +} + // // _spawn - should not be called directly, but only by p.run()/p.wait() . // It encapsulates the fork/execve mechanism that allows the diff --git a/vlib/os/process_test.v b/vlib/os/process_test.v index bdc6a46006..5301472e10 100644 --- a/vlib/os/process_test.v +++ b/vlib/os/process_test.v @@ -52,6 +52,7 @@ fn test_run() { // eprintln('polling iterations: $i') assert i < 50 + p.close() } fn test_wait() { @@ -61,6 +62,7 @@ fn test_wait() { assert p.status == .exited assert p.code == 0 assert p.pid != os.getpid() + p.close() } fn test_slurping_output() { @@ -73,6 +75,7 @@ fn test_slurping_output() { assert p.code == 0 output := p.stdout_slurp().trim_space() errors := p.stderr_slurp().trim_space() + p.close() $if trace_process_output ? { eprintln('---------------------------') eprintln('p output: "$output"')