@@ -14,6 +14,19 @@ type exec_config = Attach of string * int | Execute of string list
1414(* Raised by exec_process to indicate various unrecoverable failures. *)
1515exception Fail of string
1616
17+ (* List .events files in a directory, returning the set of filenames. *)
18+ let list_events_files dir =
19+ try
20+ Sys. readdir dir
21+ |> Array. to_list
22+ |> List. filter (fun f -> Filename. check_suffix f " .events" )
23+ with Sys_error _ -> []
24+
25+ (* Extract the PID from a ring buffer filename like "12345.events". *)
26+ let pid_of_events_file filename =
27+ try Some (int_of_string (Filename. chop_suffix filename " .events" ))
28+ with _ -> None
29+
1730let exec_process (config : runtime_events_config ) (argsl : string list ) :
1831 subprocess =
1932 if not (List. length argsl > 0 ) then
@@ -48,7 +61,12 @@ let exec_process (config : runtime_events_config) (argsl : string list) :
4861 |]
4962 (Unix. environment () )
5063 in
51- let child_pid =
64+ (* Record existing .events files so we can detect the child's new one.
65+ On Windows, Unix.create_process_env returns a process handle, not a PID,
66+ but the .events file is named with the real PID. We find the child's
67+ real PID by detecting which new .events file appeared. *)
68+ let existing_events = list_events_files dir in
69+ let child_handle =
5270 try
5371 Unix. create_process_env executable_filename (Array. of_list argsl) env
5472 Unix. stdin Unix. stdout Unix. stderr
@@ -63,14 +81,29 @@ let exec_process (config : runtime_events_config) (argsl : string list) :
6381 let timeout = 5.0 in
6482 let poll_interval = 0.05 in
6583 let deadline = Unix. gettimeofday () +. timeout in
84+ let child_pid = ref child_handle in
6685 let cursor =
6786 let last_exn = ref None in
6887 let result = ref None in
6988 while ! result = None && Unix. gettimeofday () < deadline do
70- try result := Some (Runtime_events. create_cursor (Some (dir, child_pid)))
71- with Failure _ as exn ->
72- last_exn := Some exn ;
73- Unix. sleepf poll_interval
89+ (* Find the child's real PID by detecting the new .events file.
90+ On Unix, child_handle IS the PID so this is a no-op. On Windows,
91+ we need to discover the real PID from the filesystem. *)
92+ if ! child_pid = child_handle then begin
93+ let current = list_events_files dir in
94+ let new_files = List. filter (fun f -> not (List. mem f existing_events)) current in
95+ match new_files with
96+ | [f] -> (match pid_of_events_file f with
97+ | Some pid -> child_pid := pid
98+ | None -> () )
99+ | _ -> ()
100+ end ;
101+ (try
102+ result :=
103+ Some (Runtime_events. create_cursor (Some (dir, ! child_pid)))
104+ with Failure _ as exn ->
105+ last_exn := Some exn ;
106+ Unix. sleepf poll_interval)
74107 done ;
75108 match ! result with
76109 | Some c -> c
@@ -82,19 +115,22 @@ let exec_process (config : runtime_events_config) (argsl : string list) :
82115 in
83116 failwith msg
84117 in
118+ let real_pid = ! child_pid in
85119 let alive () =
86- match Unix. waitpid [ Unix. WNOHANG ] child_pid with
120+ match Unix. waitpid [ Unix. WNOHANG ] child_handle with
87121 | 0 , _ -> true
88- | p , _ when p = child_pid -> false
122+ | p , _ when p = child_handle -> false
89123 | _ , _ -> assert false
90124 and close () =
91125 Runtime_events. free_cursor cursor;
92126 (* We need to remove the ring buffers ourselves because we told
93127 the child process not to remove them *)
94- let ring_file = Filename. concat dir (string_of_int child_pid ^ " .events" ) in
95- Unix. unlink ring_file
128+ let ring_file =
129+ Filename. concat dir (string_of_int real_pid ^ " .events" )
130+ in
131+ (try Unix. unlink ring_file with Unix. Unix_error _ -> () )
96132 in
97- { alive; cursor; close; pid = child_pid }
133+ { alive; cursor; close; pid = child_handle }
98134
99135let attach_process (dir : string ) (pid : int ) : subprocess =
100136 let cursor =
0 commit comments