v2 / cmd / tools / vtimeout.v
76 lines · 66 sloc · 1.51 KB · 72f4aa9b7ec964996011c590331d980f9925ab05
Raw
1import os
2import time
3import flag
4import strconv
5
6struct Context {
7mut:
8 timeout f64
9 cmd_args []string
10}
11
12fn main() {
13 mut fp := flag.new_flag_parser(os.args[1..])
14 fp.application('v timeout')
15 fp.version('0.0.2')
16 fp.description('Run a command with a time limit. Example: `v timeout 0.3 v run examples/hello_world.v`')
17 fp.arguments_description('timeout_in_seconds CMD [ARGS]')
18 fp.skip_executable()
19 fp.limit_free_args_to_at_least(2)!
20
21 if fp.bool('help', `h`, false, 'Show this help screen.') {
22 println(fp.usage())
23 exit(0)
24 }
25
26 args := fp.finalize() or {
27 eprintln('Argument error: ${err}')
28 exit(125) // mimic the exit codes of `timeout` in coreutils
29 }
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 cmd := ctx.cmd_args[0]
40 if !os.exists(cmd) {
41 cmd = os.find_abs_path_of_executable(cmd) or { cmd }
42 }
43
44 mut p := os.new_process(cmd)
45 p.set_args(ctx.cmd_args[1..])
46 p.run()
47 if p.err != '' {
48 eprintln('Cannot execute: ${ctx.cmd_args.join(' ')}')
49 exit(if os.exists(ctx.cmd_args[0]) { 126 } else { 127 })
50 }
51
52 child_exit := chan int{}
53
54 spawn fn (mut p os.Process, ch chan int) {
55 p.wait()
56 ch <- p.code
57 ch.close()
58 }(mut p, child_exit)
59
60 mut exit_code := 0
61 select {
62 i64(ctx.timeout * time.second) {
63 p.signal_term()
64 time.sleep(2 * time.millisecond)
65 if p.is_alive() {
66 p.signal_kill()
67 }
68 p.wait()
69 exit_code = 124 // timeout
70 }
71 code := <-child_exit {
72 exit_code = code
73 }
74 }
75 exit(exit_code)
76}
77