A client that leaves before its reply arrives, which used to kill the daemon
Eight hit-and-run connections against the robustness daemon, then a check that the session is still there and still knows what it installed. Fails with 'killed by SIGPIPE' against the parent of this commit.
This commit is contained in:
parent
fbafee4957
commit
4074eb7b7f
@ -3044,11 +3044,66 @@ let () =
|
||||
| None -> fail "(probe-one) produced no value")
|
||||
| Some r -> fail "(probe-one): %s %s" (status r) (message r)
|
||||
| None -> ());
|
||||
(try
|
||||
ignore (Wire.send c "(:op \"close\")");
|
||||
ignore (Wire.recv c)
|
||||
with _ -> ());
|
||||
(try Unix.close c with Unix.Unix_error _ -> ())
|
||||
(* ── An editor that leaves before its reply does ──────────────── *)
|
||||
(* The failure this closes was a flake in test_emacs, and it read like
|
||||
nothing it was: two break-loop checks failing, and then
|
||||
"cannot reconnect: Connection refused" on a socket path that plainly
|
||||
existed. A write into a socket whose reader has gone is SIGPIPE, and
|
||||
SIGPIPE's default action is to kill the process — which in the merged
|
||||
build is the program, the compiler and the listener at once, and
|
||||
leaves the socket file behind for the next client to be refused on.
|
||||
It is not a contrived shape: reconnecting tears the old connection
|
||||
down, and a reply already on its way out lands in the gap.
|
||||
|
||||
[c] is closed first because the daemon serves one connection at a
|
||||
time: a second one would sit in the backlog until this one ended, and
|
||||
then there would be no reply in flight to lose. *)
|
||||
(try Unix.close c with Unix.Unix_error _ -> ());
|
||||
let hit_and_run () =
|
||||
let s = Unix.socket Unix.PF_UNIX Unix.SOCK_STREAM 0 in
|
||||
(try
|
||||
Unix.connect s (Unix.ADDR_UNIX rsock);
|
||||
Wire.send s "(:op \"describe\")"
|
||||
with Unix.Unix_error _ -> ());
|
||||
(try Unix.close s with Unix.Unix_error _ -> ())
|
||||
in
|
||||
(* Several, because the first write into a freshly closed socket can
|
||||
still land in a buffer nobody will read; the second is the one that
|
||||
is refused. *)
|
||||
for _ = 1 to 8 do hit_and_run (); ignore (Unix.select [] [] [] 0.02) done;
|
||||
let c =
|
||||
match Unix.waitpid [ Unix.WNOHANG ] rpid with
|
||||
| 0, _ -> Some (connect rsock)
|
||||
| _, st ->
|
||||
(* Named rather than numbered, because [WSIGNALED] carries OCaml's
|
||||
own signal numbering and "signal -8" would send the next reader
|
||||
to the wrong page. SIGPIPE is the answer this check expects to
|
||||
see when it fails. *)
|
||||
fail "an editor that left before its reply did ended the daemon (%s)"
|
||||
(match st with
|
||||
| Unix.WEXITED n -> Printf.sprintf "exit %d" n
|
||||
| Unix.WSIGNALED n when n = Sys.sigpipe -> "killed by SIGPIPE"
|
||||
| Unix.WSIGNALED n -> Printf.sprintf "signal %d" n
|
||||
| Unix.WSTOPPED n -> Printf.sprintf "stopped on %d" n);
|
||||
None
|
||||
| exception Unix.Unix_error _ -> Some (connect rsock)
|
||||
in
|
||||
(match c with
|
||||
| None -> ()
|
||||
| Some c ->
|
||||
(match Wire.parse (Wire.send c "(:op \"describe\")"; Wire.recv c) with
|
||||
| r ->
|
||||
if not (contains_sub (Form.to_string (Option.get (Wire.field r "fns")))
|
||||
"probe-one")
|
||||
then fail "the session lost track of the program after a client left"
|
||||
| exception e ->
|
||||
fail "the session did not answer after a client left: %s"
|
||||
(Printexc.to_string e));
|
||||
(try
|
||||
ignore (Wire.send c "(:op \"close\")");
|
||||
ignore (Wire.recv c)
|
||||
with _ -> ());
|
||||
(try Unix.close c with Unix.Unix_error _ -> ()))
|
||||
end;
|
||||
(try Unix.kill rpid Sys.sigkill with Unix.Unix_error _ -> ());
|
||||
(try ignore (Unix.waitpid [] rpid) with Unix.Unix_error _ -> ());
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user