Skip to content

Commit aa51b1c

Browse files
committed
Use separate process for code loading in parallel compiler
1 parent 7d76b18 commit aa51b1c

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
@@ -37,7 +37,15 @@ defmodule Kernel.ErrorHandler do
3737
:erlang.garbage_collect(self())
3838

3939
receive do
40-
{^ref, value} -> value
40+
{^ref, {:loading, pid}} ->
41+
ref = :erlang.monitor(:process, pid)
42+
43+
receive do
44+
{:DOWN, ^ref, _, _, _} -> :found
45+
end
46+
47+
{^ref, value} ->
48+
value
4149
end
4250
end
4351
end

lib/elixir/lib/kernel/parallel_compiler.ex

+61-30
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ defmodule Kernel.ParallelCompiler do
324324
Code.prepend_path(path)
325325

326326
Enum.flat_map(result, fn
327-
{{:module, module}, binary} when is_binary(binary) ->
327+
{{:module, module}, {binary, _}} when is_binary(binary) ->
328328
full_path = Path.join(path, Atom.to_string(module) <> ".beam")
329329
File.write!(full_path, binary)
330330
if timestamp, do: File.touch!(full_path, timestamp)
@@ -336,7 +336,7 @@ defmodule Kernel.ParallelCompiler do
336336
end
337337

338338
defp write_module_binaries(result, _output, _timestamp) do
339-
for {{:module, module}, binary} when is_binary(binary) <- result, do: module
339+
for {{:module, module}, {binary, _}} when is_binary(binary) <- result, do: module
340340
end
341341

342342
## Verification
@@ -350,7 +350,7 @@ defmodule Kernel.ParallelCompiler do
350350

351351
defp maybe_check_modules(result, runtime_modules, state) do
352352
compiled_modules =
353-
for {{:module, module}, binary} when is_binary(binary) <- result,
353+
for {{:module, module}, {binary, _}} when is_binary(binary) <- result,
354354
do: module
355355

356356
profile(
@@ -566,7 +566,7 @@ defmodule Kernel.ParallelCompiler do
566566
end
567567

568568
defp count_modules(result) do
569-
Enum.count(result, &match?({{:module, _}, binary} when is_binary(binary), &1))
569+
Enum.count(result, &match?({{:module, _}, {binary, _}} when is_binary(binary), &1))
570570
end
571571

572572
defp each_cycle_return({kind, modules, warnings}), do: {kind, modules, warnings}
@@ -645,19 +645,28 @@ defmodule Kernel.ParallelCompiler do
645645
send(child, {ref, load?})
646646
spawn_workers(queue, spawned, waiting, files, result, warnings, errors, state)
647647

648+
{{:module_loaded, module}, _ref, _type, _pid, _reason} ->
649+
result =
650+
Map.update!(result, {:module, module}, fn {binary, _loader} -> {binary, true} end)
651+
652+
spawn_workers(queue, spawned, waiting, files, result, warnings, errors, state)
653+
648654
{:module_available, child, ref, file, module, binary, loaded?} ->
649655
state.each_module.(file, module, binary)
650656

651-
available =
657+
{available, load_status} =
652658
case Map.get(result, {:module, module}) do
659+
# We prefer to load in the client, if possible,
660+
# to avoid locking the compilation server.
661+
[_ | _] = pids when loaded? ->
662+
{Enum.map(pids, &{&1, :found}), loaded?}
663+
653664
[_ | _] = pids ->
654-
# We prefer to load in the client, if possible,
655-
# to avoid locking the compilation server.
656-
loaded? or load_module(module, binary, state)
657-
Enum.map(pids, &{&1, :found})
665+
pid = load_module(module, binary, state.dest)
666+
{Enum.map(pids, &{&1, {:loading, pid}}), pid}
658667

659668
_ ->
660-
[]
669+
{[], loaded?}
661670
end
662671

663672
# Release the module loader which is waiting for an ack
@@ -668,7 +677,7 @@ defmodule Kernel.ParallelCompiler do
668677
spawned,
669678
waiting,
670679
files,
671-
Map.put(result, {:module, module}, binary),
680+
Map.put(result, {:module, module}, {binary, load_status}),
672681
warnings,
673682
errors,
674683
state
@@ -688,8 +697,8 @@ defmodule Kernel.ParallelCompiler do
688697
{waiting, files, result} =
689698
if not is_list(available_or_pending) or on in defining do
690699
# If what we are waiting on was defined but not loaded, we do it now.
691-
load_pending(kind, on, result, state)
692-
send(child_pid, {ref, :found})
700+
{reply, result} = load_pending(kind, on, result, state)
701+
send(child_pid, {ref, reply})
693702
{waiting, files, result}
694703
else
695704
waiting = Map.put(waiting, child_pid, {kind, ref, file_pid, on, defining, deadlock})
@@ -784,27 +793,49 @@ defmodule Kernel.ParallelCompiler do
784793
end
785794

786795
defp load_pending(kind, module, result, state) do
787-
with true <- kind in [:module, :struct],
788-
%{{:module, ^module} => binary} when is_binary(binary) <- result,
789-
false <- :erlang.module_loaded(module) do
790-
load_module(module, binary, state)
796+
case result do
797+
%{{:module, ^module} => {binary, load_status}}
798+
when kind in [:module, :struct] and is_binary(binary) ->
799+
case load_status do
800+
true ->
801+
{:found, result}
802+
803+
false ->
804+
pid = load_module(module, binary, state.dest)
805+
result = Map.put(result, {:module, module}, {binary, pid})
806+
{{:loading, pid}, result}
807+
808+
pid when is_pid(pid) ->
809+
{{:loading, pid}, result}
810+
end
811+
812+
_ ->
813+
{:found, result}
791814
end
792815
end
793816

794-
defp load_module(module, binary, state) do
795-
beam_location =
796-
case state.dest do
797-
nil ->
798-
[]
799-
800-
dest ->
801-
:filename.join(
802-
:elixir_utils.characters_to_list(dest),
803-
Atom.to_charlist(module) ++ ~c".beam"
804-
)
805-
end
817+
defp load_module(module, binary, dest) do
818+
{pid, _ref} =
819+
:erlang.spawn_opt(
820+
fn ->
821+
beam_location =
822+
case dest do
823+
nil ->
824+
[]
825+
826+
dest ->
827+
:filename.join(
828+
:elixir_utils.characters_to_list(dest),
829+
Atom.to_charlist(module) ++ ~c".beam"
830+
)
831+
end
832+
833+
:code.load_binary(module, beam_location, binary)
834+
end,
835+
monitor: [tag: {:module_loaded, module}]
836+
)
806837

807-
:code.load_binary(module, beam_location, binary)
838+
pid
808839
end
809840

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

0 commit comments

Comments
 (0)