Skip to content

Commit 6d0eaa5

Browse files
authored
tools: rewrite v timeout, support killing the child process on timeout by default (#24367)
1 parent f8c35b4 commit 6d0eaa5

1 file changed

Lines changed: 51 additions & 23 deletions

File tree

‎cmd/tools/vtimeout.v‎

Lines changed: 51 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,71 @@
11
import os
22
import time
33
import flag
4+
import strconv
45

56
struct Context {
67
mut:
7-
show_help bool
8-
cmd_line_opts []string
9-
full_cmd string
10-
timeout f64
8+
timeout f64
9+
cmd_args []string
1110
}
1211

1312
fn main() {
14-
mut ctx := Context{}
15-
args := arguments()
16-
mut fp := flag.new_flag_parser(args#[1..])
13+
mut fp := flag.new_flag_parser(os.args[1..])
1714
fp.application('v timeout')
18-
fp.version('0.0.1')
15+
fp.version('0.0.2')
1916
fp.description('Run a command with a time limit. Example: `v timeout 0.3 v run examples/hello_world.v`')
2017
fp.arguments_description('timeout_in_seconds CMD [ARGS]')
2118
fp.skip_executable()
2219
fp.limit_free_args_to_at_least(2)!
23-
ctx.show_help = fp.bool('help', `h`, false, 'Show this help screen.')
24-
if ctx.show_help {
20+
21+
if fp.bool('help', `h`, false, 'Show this help screen.') {
2522
println(fp.usage())
2623
exit(0)
2724
}
28-
ctx.cmd_line_opts = fp.finalize() or {
29-
eprintln('> error: ${err}')
25+
26+
args := fp.finalize() or {
27+
eprintln('Argument error: ${err}')
3028
exit(125) // mimic the exit codes of `timeout` in coreutils
3129
}
32-
ctx.timeout = ctx.cmd_line_opts[0].f64()
33-
ctx.cmd_line_opts = ctx.cmd_line_opts#[1..]
34-
ctx.full_cmd = ctx.cmd_line_opts.join(' ')
35-
spawn fn (ctx Context) {
36-
tperiod := time.Duration(i64(ctx.timeout * time.second))
37-
time.sleep(tperiod)
38-
// eprintln('> error: timeout of ${tperiod.seconds():5.3f}s reached, before command finished; command was: `${ctx.full_cmd}`')
39-
exit(124)
40-
}(ctx)
41-
ecode := os.system(ctx.full_cmd)
42-
exit(ecode)
30+
31+
ctx := Context{
32+
timeout: strconv.atof64(args[0]) or {
33+
eprintln('Invalid timeout: ${args[0]}')
34+
exit(125)
35+
}
36+
cmd_args: args[1..].clone()
37+
}
38+
39+
mut p := os.new_process(ctx.cmd_args[0])
40+
p.set_args(ctx.cmd_args[1..])
41+
p.run()
42+
if p.err != '' {
43+
eprintln('Cannot execute: ${ctx.cmd_args.join(' ')}')
44+
exit(if os.exists(ctx.cmd_args[0]) { 126 } else { 127 })
45+
}
46+
47+
child_exit := chan int{}
48+
49+
spawn fn (mut p os.Process, ch chan int) {
50+
p.wait()
51+
ch <- p.code
52+
ch.close()
53+
}(mut p, child_exit)
54+
55+
mut exit_code := 0
56+
select {
57+
i64(ctx.timeout * time.second) {
58+
p.signal_term()
59+
time.sleep(2 * time.millisecond)
60+
if p.is_alive() {
61+
p.signal_kill()
62+
}
63+
p.wait()
64+
exit_code = 124 // timeout
65+
}
66+
code := <-child_exit {
67+
exit_code = code
68+
}
69+
}
70+
exit(exit_code)
4371
}

0 commit comments

Comments
 (0)