Skip to content

Commit db003e4

Browse files
committed
Do not send close_pipe cmd if we have received eof
1 parent 3cb5df8 commit db003e4

File tree

2 files changed

+55
-15
lines changed

2 files changed

+55
-15
lines changed

lib/ex_cmd/process.ex

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -796,6 +796,7 @@ defmodule ExCmd.Process do
796796

797797
@spec handle_command(recv_commands, binary, State.t()) :: {:noreply, State.t()}
798798
defp handle_command(tag, bin, state) when tag in [:output_eof, :output] do
799+
# Only stdout supported for now
799800
pipe_name = :stdout
800801

801802
with {:ok, operation_name} <- Operations.match_pending_operation(state, pipe_name),
@@ -810,6 +811,18 @@ defmodule ExCmd.Process do
810811
end
811812

812813
:ok = GenServer.reply(from, ret)
814+
815+
# Mark pipe as EOF to skip redundant close commands
816+
state =
817+
if ret == :eof do
818+
{:ok, pipe} = State.pipe(state, pipe_name)
819+
{:ok, eof_pipe} = Pipe.mark_eof(pipe)
820+
{:ok, state} = State.put_pipe(state, pipe_name, eof_pipe)
821+
state
822+
else
823+
state
824+
end
825+
813826
{:noreply, state}
814827
else
815828
{:error, _error} ->

lib/ex_cmd/process/pipe.ex

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ defmodule ExCmd.Process.Pipe do
1010
port: port | nil,
1111
monitor_ref: reference() | nil,
1212
owner: pid | nil,
13-
status: :open | :closed
13+
status: :open | :closed | :eof
1414
}
1515

1616
defstruct [:name, :port, :monitor_ref, :owner, status: :init]
@@ -37,14 +37,19 @@ defmodule ExCmd.Process.Pipe do
3737
end
3838

3939
@spec open?(t) :: boolean()
40-
def open?(pipe), do: pipe.status == :open
40+
def open?(pipe), do: pipe.status in [:open, :eof]
4141

42-
@spec read(t, non_neg_integer, pid) :: {:error, :eagain} | {:error, term}
42+
@spec read(t, non_neg_integer, pid) :: :eof | {:error, :eagain} | {:error, term}
4343
def read(pipe, size, caller) do
44-
if caller != pipe.owner do
45-
{:error, :pipe_closed_or_invalid_caller}
46-
else
47-
{:error, :eagain} = Proto.read(pipe.port, size)
44+
cond do
45+
caller != pipe.owner ->
46+
{:error, :pipe_closed_or_invalid_caller}
47+
48+
pipe.status == :eof ->
49+
:eof
50+
51+
true ->
52+
{:error, :eagain} = Proto.read(pipe.port, size)
4853
end
4954
end
5055

@@ -59,20 +64,30 @@ defmodule ExCmd.Process.Pipe do
5964

6065
@spec close(t, pid) :: {:ok, t} | {:error, :pipe_closed_or_invalid_caller}
6166
def close(%Pipe{} = pipe, caller) do
62-
if caller != pipe.owner do
63-
{:error, :pipe_closed_or_invalid_caller}
64-
else
65-
Process.demonitor(pipe.monitor_ref, [:flush])
66-
:ok = Proto.close(pipe.port, pipe.name)
67-
pipe = %Pipe{pipe | status: :closed, monitor_ref: nil, owner: nil}
67+
cond do
68+
caller != pipe.owner ->
69+
{:error, :pipe_closed_or_invalid_caller}
6870

69-
{:ok, pipe}
71+
pipe.status in [:closed, :eof] ->
72+
# Already closed/eof - skip protocol command
73+
if pipe.monitor_ref do
74+
Process.demonitor(pipe.monitor_ref, [:flush])
75+
end
76+
77+
pipe = %Pipe{pipe | status: :closed, monitor_ref: nil, owner: nil}
78+
{:ok, pipe}
79+
80+
true ->
81+
Process.demonitor(pipe.monitor_ref, [:flush])
82+
:ok = Proto.close(pipe.port, pipe.name)
83+
pipe = %Pipe{pipe | status: :closed, monitor_ref: nil, owner: nil}
84+
{:ok, pipe}
7085
end
7186
end
7287

7388
@spec set_owner(t, pid) :: {:ok, t} | {:error, :closed}
7489
def set_owner(%Pipe{} = pipe, new_owner) do
75-
if pipe.status == :open do
90+
if open?(pipe) do
7691
ref = Process.monitor(new_owner)
7792
Process.demonitor(pipe.monitor_ref, [:flush])
7893
pipe = %Pipe{pipe | owner: new_owner, monitor_ref: ref}
@@ -82,4 +97,16 @@ defmodule ExCmd.Process.Pipe do
8297
{:error, :closed}
8398
end
8499
end
100+
101+
@doc """
102+
Mark pipe as EOF when remote side closes.
103+
104+
Only used for stdout/stderr. `:eof` means remote closed,
105+
`:closed` means we explicitly closed.
106+
"""
107+
@spec mark_eof(t) :: {:ok, t}
108+
def mark_eof(%Pipe{} = pipe) do
109+
pipe = %Pipe{pipe | status: :eof}
110+
{:ok, pipe}
111+
end
85112
end

0 commit comments

Comments
 (0)