1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
open! Import
include Async_intf
module Unix = struct
let kill_no_err pid =
try Unix.kill pid 9
with Unix.Unix_error (e, s1, s2) ->
[%log.warn
"Killing process with pid %d failed with error (%s, %s, %s)" pid
(Unix.error_message e) s1 s2]
(** [Exit] is a stack of PIDs that will be killed [at_exit]. *)
module Exit = struct
let proc_list = ref []
let m = Mutex.create ()
let add pid =
Mutex.lock m;
proc_list := pid :: !proc_list;
Mutex.unlock m
let remove pid =
Mutex.lock m;
proc_list := List.filter (fun pid' -> pid <> pid') !proc_list;
Mutex.unlock m
let () = at_exit @@ fun () -> List.iter kill_no_err !proc_list
end
type outcome = [ `Success | `Cancelled | `Failure of string ]
[@@deriving irmin]
type status = [ `Running | `Success | `Cancelled | `Failure of string ]
[@@deriving irmin]
type t = { pid : int; mutable status : status }
let async f =
Stdlib.flush_all ();
match Lwt_unix.fork () with
| 0 ->
Lwt_main.Exit_hooks.remove_all ();
Lwt_main.abandon_yielded_and_paused ();
(try f ()
with e ->
[%log.err
"Unhandled exception in child process %s" (Printexc.to_string e)]);
Unix.kill (Unix.getpid ()) 9;
assert false
| pid ->
Exit.add pid;
{ pid; status = `Running }
let status_of_process_outcome = function
| Lwt_unix.WSIGNALED x when x = Sys.sigkill ->
`Success
| Lwt_unix.WSIGNALED n -> `Failure (Fmt.str "Signaled %d" n)
| Lwt_unix.WEXITED n -> `Failure (Fmt.str "Exited %d" n)
| Lwt_unix.WSTOPPED n -> `Failure (Fmt.str "Stopped %d" n)
let cancel t =
match t.status with
| `Running ->
let pid, _ = Unix.waitpid [ Unix.WNOHANG ] t.pid in
if pid = 0 then (
kill_no_err t.pid;
Exit.remove t.pid;
t.status <- `Cancelled;
true)
else false
| _ -> false
let status t =
match t.status with
| `Running ->
let pid, status = Unix.waitpid [ Unix.WNOHANG ] t.pid in
if pid = 0 then `Running
else
let s = status_of_process_outcome status in
Exit.remove pid;
t.status <- s;
s
| #outcome as s -> s
let await t =
match t.status with
| `Running ->
let+ pid, status = Lwt_unix.waitpid [] t.pid in
let s = status_of_process_outcome status in
Exit.remove pid;
t.status <- s;
s
| #outcome as s -> Lwt.return s
end