Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 22 additions & 15 deletions apps/forge/lib/forge/namespace/transform/beams.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,18 @@ defmodule Forge.Namespace.Transform.Beams do

me = self()

spawn(fn ->
all_beams
|> Task.async_stream(
&apply_and_update_progress(&1, me),
ordered: false,
timeout: :infinity
)
|> Stream.run()
end)

block_until_done(0, total_files, opts)
{_pid, monitor_ref} =
spawn_monitor(fn ->
all_beams
|> Task.async_stream(
&apply_and_update_progress(&1, me),
ordered: false,
timeout: :infinity
)
|> Stream.run()
end)

block_until_done(0, total_files, monitor_ref, opts)
end

def apply(path) do
Expand All @@ -46,17 +47,23 @@ defmodule Forge.Namespace.Transform.Beams do
defp changed?(same, same), do: false
defp changed?(_, _), do: true

defp block_until_done(same, same, opts) do
defp block_until_done(same, same, monitor_ref, opts) do
Process.demonitor(monitor_ref, [:flush])

if !opts[:no_progress] do
IO.write("\n")
end

Mix.Shell.IO.info("Finished namespacing .beam files")
end

defp block_until_done(current, max, opts) do
defp block_until_done(current, max, monitor_ref, opts) do
receive do
:progress -> :ok
:progress ->
:ok

{:DOWN, ^monitor_ref, :process, _pid, reason} ->
raise "Beam rewriting worker crashed: #{inspect(reason)}"
end

current = current + 1
Expand All @@ -68,7 +75,7 @@ defmodule Forge.Namespace.Transform.Beams do
IO.write(" Applying namespace: #{percent_complete} complete")
end

block_until_done(current, max, opts)
block_until_done(current, max, monitor_ref, opts)
end

defp apply_and_update_progress(beam_file, caller) do
Expand Down
30 changes: 30 additions & 0 deletions apps/forge/test/forge/namespace/transform/beams_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
defmodule Forge.Namespace.Transform.BeamsTest do
use ExUnit.Case, async: false
use Patch

alias Forge.Namespace.Transform.Beams

@moduletag tmp_dir: true

describe "apply_to_all/2 crash handling" do
test "raises when worker process crashes", %{tmp_dir: tmp_dir} do
# We need total_files to be > 0 in block_until_done to enter the
# receive loop
File.mkdir_p!(Path.join([tmp_dir, "lib", "fake"]))
File.write!(Path.join([tmp_dir, "lib", "fake", "Elixir.Fake.beam"]), "")

patch(Mix.Tasks.Namespace, :app_names, [:fake])

# Force a crash inside the worker. :beam_lib.chunks is the first
# remote call in apply/1, so patching it sidesteps the with clause that
# would otherwise handle a graceful error return.
patch(:beam_lib, :chunks, fn _path, _chunks ->
raise "simulated beam_lib crash"
end)

assert_raise RuntimeError, ~r/Beam rewriting worker crashed/, fn ->
Beams.apply_to_all(tmp_dir, no_progress: true)
end
end
end
end