Skip to content

Commit f927f5b

Browse files
committed
Use separate process for code loading in parallel compiler
1 parent 682a7ed commit f927f5b

File tree

2 files changed

+70
-31
lines changed

2 files changed

+70
-31
lines changed

lib/elixir/lib/kernel/error_handler.ex

+9-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,15 @@ defmodule Kernel.ErrorHandler do
4141
:erlang.garbage_collect(self())
4242

4343
receive do
44-
{^ref, value} -> value
44+
{^ref, {:loading, pid}} ->
45+
ref = :erlang.monitor(:process, pid)
46+
47+
receive do
48+
{:DOWN, ^ref, _, _, _} -> :found
49+
end
50+
51+
{^ref, value} ->
52+
value
4553
end
4654
end
4755
end

lib/elixir/lib/kernel/parallel_compiler.ex

+61-30
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ defmodule Kernel.ParallelCompiler do
342342
Code.prepend_path(path)
343343

344344
Enum.flat_map(result, fn
345-
{{:module, module}, binary} when is_binary(binary) ->
345+
{{:module, module}, {binary, _}} when is_binary(binary) ->
346346
full_path = Path.join(path, Atom.to_string(module) <> ".beam")
347347
File.write!(full_path, binary)
348348
if timestamp, do: File.touch!(full_path, timestamp)
@@ -354,7 +354,7 @@ defmodule Kernel.ParallelCompiler do
354354
end
355355

356356
defp write_module_binaries(result, _output, _timestamp) do
357-
for {{:module, module}, binary} when is_binary(binary) <- result, do: module
357+
for {{:module, module}, {binary, _}} when is_binary(binary) <- result, do: module
358358
end
359359

360360
## Verification
@@ -369,7 +369,7 @@ defmodule Kernel.ParallelCompiler do
369369

370370
defp maybe_check_modules(result, runtime_modules, state) do
371371
compiled_modules =
372-
for {{:module, module}, binary} when is_binary(binary) <- result,
372+
for {{:module, module}, {binary, _}} when is_binary(binary) <- result,
373373
do: module
374374

375375
profile(
@@ -585,7 +585,7 @@ defmodule Kernel.ParallelCompiler do
585585
end
586586

587587
defp count_modules(result) do
588-
Enum.count(result, &match?({{:module, _}, binary} when is_binary(binary), &1))
588+
Enum.count(result, &match?({{:module, _}, {binary, _}} when is_binary(binary), &1))
589589
end
590590

591591
defp each_cycle_return({kind, modules, warnings}), do: {kind, modules, warnings}
@@ -664,19 +664,28 @@ defmodule Kernel.ParallelCompiler do
664664
send(child, {ref, load?})
665665
spawn_workers(queue, spawned, waiting, files, result, warnings, errors, state)
666666

667+
{{:module_loaded, module}, _ref, _type, _pid, _reason} ->
668+
result =
669+
Map.update!(result, {:module, module}, fn {binary, _loader} -> {binary, true} end)
670+
671+
spawn_workers(queue, spawned, waiting, files, result, warnings, errors, state)
672+
667673
{:module_available, child, ref, file, module, binary, loaded?} ->
668674
state.each_module.(file, module, binary)
669675

670-
available =
676+
{available, load_status} =
671677
case Map.get(result, {:module, module}) do
678+
# We prefer to load in the client, if possible,
679+
# to avoid locking the compilation server.
680+
[_ | _] = pids when loaded? ->
681+
{Enum.map(pids, &{&1, :found}), loaded?}
682+
672683
[_ | _] = pids ->
673-
# We prefer to load in the client, if possible,
674-
# to avoid locking the compilation server.
675-
loaded? or load_module(module, binary, state)
676-
Enum.map(pids, &{&1, :found})
684+
pid = load_module(module, binary, state.dest)
685+
{Enum.map(pids, &{&1, {:loading, pid}}), pid}
677686

678687
_ ->
679-
[]
688+
{[], loaded?}
680689
end
681690

682691
# Release the module loader which is waiting for an ack
@@ -687,7 +696,7 @@ defmodule Kernel.ParallelCompiler do
687696
spawned,
688697
waiting,
689698
files,
690-
Map.put(result, {:module, module}, binary),
699+
Map.put(result, {:module, module}, {binary, load_status}),
691700
warnings,
692701
errors,
693702
state
@@ -707,8 +716,8 @@ defmodule Kernel.ParallelCompiler do
707716
{waiting, files, result} =
708717
if not is_list(available_or_pending) or on in defining do
709718
# If what we are waiting on was defined but not loaded, we do it now.
710-
load_pending(kind, on, result, state)
711-
send(child_pid, {ref, :found})
719+
{reply, result} = load_pending(kind, on, result, state)
720+
send(child_pid, {ref, reply})
712721
{waiting, files, result}
713722
else
714723
waiting = Map.put(waiting, child_pid, {kind, ref, file_pid, on, defining, deadlock})
@@ -803,27 +812,49 @@ defmodule Kernel.ParallelCompiler do
803812
end
804813

805814
defp load_pending(kind, module, result, state) do
806-
with true <- kind in [:module, :struct],
807-
%{{:module, ^module} => binary} when is_binary(binary) <- result,
808-
false <- :erlang.module_loaded(module) do
809-
load_module(module, binary, state)
815+
case result do
816+
%{{:module, ^module} => {binary, load_status}}
817+
when kind in [:module, :struct] and is_binary(binary) ->
818+
case load_status do
819+
true ->
820+
{:found, result}
821+
822+
false ->
823+
pid = load_module(module, binary, state.dest)
824+
result = Map.put(result, {:module, module}, {binary, pid})
825+
{{:loading, pid}, result}
826+
827+
pid when is_pid(pid) ->
828+
{{:loading, pid}, result}
829+
end
830+
831+
_ ->
832+
{:found, result}
810833
end
811834
end
812835

813-
defp load_module(module, binary, state) do
814-
beam_location =
815-
case state.dest do
816-
nil ->
817-
[]
818-
819-
dest ->
820-
:filename.join(
821-
:elixir_utils.characters_to_list(dest),
822-
Atom.to_charlist(module) ++ ~c".beam"
823-
)
824-
end
836+
defp load_module(module, binary, dest) do
837+
{pid, _ref} =
838+
:erlang.spawn_opt(
839+
fn ->
840+
beam_location =
841+
case dest do
842+
nil ->
843+
[]
844+
845+
dest ->
846+
:filename.join(
847+
:elixir_utils.characters_to_list(dest),
848+
Atom.to_charlist(module) ++ ~c".beam"
849+
)
850+
end
851+
852+
:code.load_binary(module, beam_location, binary)
853+
end,
854+
monitor: [tag: {:module_loaded, module}]
855+
)
825856

826-
:code.load_binary(module, beam_location, binary)
857+
pid
827858
end
828859

829860
defp update_result(result, kind, module, value) do

0 commit comments

Comments
 (0)