Skip to content

Commit 0bee0d6

Browse files
committed
Merge remote-tracking branch 'origin/master' into remove-skill-from-hexdocs-sidebar
2 parents 29524cd + f121922 commit 0bee0d6

2 files changed

Lines changed: 73 additions & 9 deletions

File tree

lib/membrane/core/subprocess_supervisor.ex

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,8 @@ defmodule Membrane.Core.SubprocessSupervisor do
139139
name: {__MODULE__, name},
140140
type: :supervisor,
141141
child_pid: child_pid,
142-
role: :subprocess_supervisor
142+
role: :subprocess_supervisor,
143+
pending_child_death: nil
143144
})
144145

145146
{:reply, {:ok, child_pid}, state}
@@ -221,7 +222,7 @@ defmodule Membrane.Core.SubprocessSupervisor do
221222

222223
defp do_handle_info({:EXIT, pid, reason}, state) do
223224
{data, state} = pop_in(state, [:children, pid])
224-
handle_exit(data, reason, state)
225+
state = handle_exit(data, reason, state)
225226

226227
case state do
227228
%{parent_process: :exit_requested, children: children} when children == %{} ->
@@ -245,27 +246,33 @@ defmodule Membrane.Core.SubprocessSupervisor do
245246
end
246247

247248
defp handle_exit(%{role: :subprocess_supervisor} = data, reason, state) do
248-
case Map.fetch(state.children, data.child_pid) do
249+
with :error <- Map.fetch(state.children, data.child_pid),
250+
%{pending_child_death: {child_name, death_reason}} <- data do
251+
Message.send(state.parent_component, :child_death, [child_name, death_reason])
252+
else
249253
{:ok, child_data} ->
250254
raise "Subprocess supervisor failure #{inspect(child_data.name)}, reason: #{inspect(reason)}"
251255

252-
:error ->
256+
_no_pending_death ->
253257
:ok
254258
end
259+
260+
state
255261
end
256262

257263
defp handle_exit(%{role: :component} = data, reason, state) do
258264
Process.exit(data.supervisor_pid, :shutdown)
259-
Message.send(state.parent_component, :child_death, [data.name, reason])
265+
266+
put_in(state.children[data.supervisor_pid].pending_child_death, {data.name, reason})
260267
end
261268

262-
defp handle_exit(%{role: :utility}, _reason, _state) do
263-
:ok
269+
defp handle_exit(%{role: :utility}, _reason, state) do
270+
state
264271
end
265272

266273
# Clause handling the case when child start function returns error
267274
# and we don't know its PID, but we still receive exit signal from it.
268-
defp handle_exit(nil, _reason, _state) do
269-
:ok
275+
defp handle_exit(nil, _reason, state) do
276+
state
270277
end
271278
end

test/membrane/integration/callbacks_test.exs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,4 +121,61 @@ defmodule Membrane.Integration.CallbacksTest do
121121
{:DOWN, _ref, _process, ^pipeline, _reason} -> :ok
122122
end
123123
end
124+
125+
defmodule BinWithTwoChildren do
126+
use Membrane.Bin
127+
128+
alias Membrane.Integration.CallbacksTest.PadlessElement
129+
130+
@impl true
131+
def handle_init(_ctx, _opts) do
132+
{[spec: [child(:child_a, PadlessElement), child(:child_b, PadlessElement)]], %{}}
133+
end
134+
135+
@impl true
136+
def handle_playing(ctx, state) do
137+
{[notify_parent: {:children_pids, ctx.children.child_a.pid, ctx.children.child_b.pid}],
138+
state}
139+
end
140+
end
141+
142+
defmodule BinTerminationPipeline do
143+
use Membrane.Pipeline
144+
145+
@impl true
146+
def handle_child_notification({:children_pids, a_pid, b_pid}, :bin, _ctx, state) do
147+
{[], %{state | a_pid: a_pid, b_pid: b_pid}}
148+
end
149+
150+
@impl true
151+
def handle_child_terminated(:bin, _ctx, state) do
152+
send(
153+
state.test_pid,
154+
{:terminated, Process.alive?(state.a_pid), Process.alive?(state.b_pid)}
155+
)
156+
157+
{[], state}
158+
end
159+
end
160+
161+
test "handle_child_terminated fires only after bin's subprocess_supervisor dies, ensuring bin's children are already dead" do
162+
pipeline =
163+
Testing.Pipeline.start_link_supervised!(
164+
module: BinTerminationPipeline,
165+
custom_args: %{test_pid: self(), a_pid: nil, b_pid: nil}
166+
)
167+
168+
Testing.Pipeline.execute_actions(pipeline,
169+
spec: {child(:bin, BinWithTwoChildren), group: :bin_group, crash_group_mode: :temporary}
170+
)
171+
172+
assert_pipeline_notified(pipeline, :bin, {:children_pids, _a_pid, _b_pid})
173+
174+
Testing.Pipeline.get_child_pid!(pipeline, :bin)
175+
|> Process.exit(:crash)
176+
177+
assert_receive {:terminated, false, false}, 1000
178+
179+
Testing.Pipeline.terminate(pipeline)
180+
end
124181
end

0 commit comments

Comments
 (0)