Skip to content

Commit 85352d3

Browse files
authored
Do not use exit(msg) in parallel compiler/require (#6229)
1 parent e0bdd3e commit 85352d3

File tree

2 files changed

+93
-71
lines changed

2 files changed

+93
-71
lines changed

lib/elixir/lib/kernel/parallel_compiler.ex

Lines changed: 54 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -110,17 +110,21 @@ defmodule Kernel.ParallelCompiler do
110110
:erlang.put(:elixir_compiler_file, file)
111111
:erlang.process_flag(:error_handler, Kernel.ErrorHandler)
112112

113-
exit(try do
114-
_ = if output do
115-
:elixir_compiler.file_to_path(file, output)
116-
else
117-
:elixir_compiler.file(file, Keyword.get(options, :dest))
113+
result =
114+
try do
115+
_ = if output do
116+
:elixir_compiler.file_to_path(file, output)
117+
else
118+
:elixir_compiler.file(file, Keyword.get(options, :dest))
119+
end
120+
:ok
121+
catch
122+
kind, reason ->
123+
{kind, reason, System.stacktrace}
118124
end
119-
{:shutdown, file}
120-
catch
121-
kind, reason ->
122-
{:failure, kind, reason, System.stacktrace}
123-
end)
125+
126+
send(parent, {:file_compiled, self(), file, result})
127+
exit(:shutdown)
124128
end
125129

126130
timeout = Keyword.get(options, :long_compilation_threshold, 10) * 1_000
@@ -226,26 +230,52 @@ defmodule Kernel.ParallelCompiler do
226230
end
227231
spawn_compilers(state)
228232

229-
{:DOWN, _down_ref, :process, down_pid, {:shutdown, file}} ->
233+
{:file_compiled, child_pid, file, :ok} ->
234+
discard_down(child_pid)
235+
230236
if callback = Keyword.get(options, :each_file) do
231237
callback.(file)
232238
end
233239

234-
cancel_waiting_timer(queued, down_pid)
240+
cancel_waiting_timer(queued, child_pid)
235241

236242
# Sometimes we may have spurious entries in the waiting
237243
# list because someone invoked try/rescue UndefinedFunctionError
238-
new_entries = List.delete(entries, down_pid)
239-
new_queued = List.keydelete(queued, down_pid, 0)
240-
new_waiting = List.keydelete(waiting, down_pid, 1)
244+
new_entries = List.delete(entries, child_pid)
245+
new_queued = List.keydelete(queued, child_pid, 0)
246+
new_waiting = List.keydelete(waiting, child_pid, 1)
241247
spawn_compilers(%{state | entries: new_entries, waiting: new_waiting, queued: new_queued})
242248

243-
{:DOWN, down_ref, :process, _down_pid, reason} ->
244-
handle_failure(down_ref, reason, queued)
249+
{:file_compiled, child_pid, file, {kind, reason, stack}} ->
250+
discard_down(child_pid)
251+
print_error(file, kind, reason, stack)
252+
terminate(queued)
253+
254+
{:DOWN, ref, :process, _pid, reason} ->
255+
handle_down(queued, ref, reason)
245256
wait_for_messages(state)
246257
end
247258
end
248259

260+
defp discard_down(pid) do
261+
receive do
262+
{:DOWN, _, :process, ^pid, _} -> :ok
263+
end
264+
end
265+
266+
defp handle_down(_queued, _ref, :normal) do
267+
:ok
268+
end
269+
defp handle_down(queued, ref, reason) do
270+
case List.keyfind(queued, ref, 1) do
271+
{_child, ^ref, file, _timer_ref} ->
272+
print_error(file, :exit, reason, [])
273+
terminate(queued)
274+
_ ->
275+
:ok
276+
end
277+
end
278+
249279
defp handle_deadlock(waiting, queued) do
250280
deadlock =
251281
for {pid, _, file, _} <- queued do
@@ -255,7 +285,7 @@ defmodule Kernel.ParallelCompiler do
255285
{_kind, ^pid, _, on, _} = List.keyfind(waiting, pid, 1)
256286
error = CompileError.exception(description: "deadlocked waiting on module #{inspect on}",
257287
file: nil, line: nil)
258-
print_failure(file, {:failure, :error, error, stacktrace})
288+
print_error(file, :error, error, stacktrace)
259289

260290
{file, on}
261291
end
@@ -279,35 +309,16 @@ defmodule Kernel.ParallelCompiler do
279309
exit({:shutdown, 1})
280310
end
281311

282-
defp handle_failure(ref, reason, queued) do
283-
if file = find_failure(ref, queued) do
284-
print_failure(file, reason)
285-
for {pid, _, _, _} <- queued do
286-
Process.exit(pid, :kill)
287-
end
288-
exit({:shutdown, 1})
289-
end
290-
end
291-
292-
defp find_failure(ref, queued) do
293-
case List.keyfind(queued, ref, 1) do
294-
{_child, ^ref, file, _timer_ref} -> file
295-
_ -> nil
312+
defp terminate(queued) do
313+
for {pid, _, _, _} <- queued do
314+
Process.exit(pid, :kill)
296315
end
316+
exit({:shutdown, 1})
297317
end
298318

299-
defp print_failure(_file, {:shutdown, _}) do
300-
:ok
301-
end
302-
303-
defp print_failure(file, {:failure, kind, reason, stacktrace}) do
304-
IO.write ["\n== Compilation error in file #{Path.relative_to_cwd(file)} ==\n",
305-
Kernel.CLI.format_error(kind, reason, stacktrace)]
306-
end
307-
308-
defp print_failure(file, reason) do
319+
defp print_error(file, kind, reason, stack) do
309320
IO.write ["\n== Compilation error in file #{Path.relative_to_cwd(file)} ==\n",
310-
Kernel.CLI.print_error(:exit, reason, [])]
321+
Kernel.CLI.format_error(kind, reason, stack)]
311322
end
312323

313324
defp cancel_waiting_timer(queued, child_pid) do

lib/elixir/lib/kernel/parallel_require.ex

Lines changed: 39 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -49,46 +49,44 @@ defmodule Kernel.ParallelRequire do
4949

5050
defp spawn_requires([file | files], waiting, callbacks, schedulers, result) do
5151
parent = self()
52+
5253
{pid, ref} = :erlang.spawn_monitor fn ->
5354
:erlang.put(:elixir_compiler_pid, parent)
5455
:erlang.put(:elixir_compiler_file, file)
5556

56-
exit(try do
57-
new = Code.require_file(file) || []
58-
{:required, Enum.map(new, &elem(&1, 0)), file}
59-
catch
60-
kind, reason ->
61-
{:failure, kind, reason, System.stacktrace}
62-
end)
57+
result =
58+
try do
59+
new = Code.require_file(file) || []
60+
{:required, Enum.map(new, &elem(&1, 0))}
61+
catch
62+
kind, reason ->
63+
{kind, reason, System.stacktrace}
64+
end
65+
66+
send(parent, {:file_required, self(), file, result})
67+
exit(:shutdown)
6368
end
6469

6570
spawn_requires(files, [{pid, ref} | waiting], callbacks, schedulers, result)
6671
end
6772

6873
defp wait_for_messages(files, waiting, callbacks, schedulers, result) do
6974
receive do
70-
{:DOWN, ref, :process, pid, status} ->
71-
tuple = {pid, ref}
72-
if tuple in waiting do
73-
waiting = List.delete(waiting, tuple)
74-
75-
case status do
76-
{:required, mods, file} ->
77-
if each_file_callback = callbacks[:each_file] do
78-
each_file_callback.(file)
79-
end
80-
81-
spawn_requires(files, waiting, callbacks, schedulers, mods ++ result)
82-
83-
{:failure, kind, reason, stacktrace} ->
84-
:erlang.raise(kind, reason, stacktrace)
85-
86-
other ->
87-
:erlang.raise(:exit, other, [])
88-
end
89-
else
90-
spawn_requires(files, waiting, callbacks, schedulers, result)
75+
{:file_required, pid, file, {:required, mods}} ->
76+
discard_down(pid)
77+
if each_file_callback = callbacks[:each_file] do
78+
each_file_callback.(file)
9179
end
80+
waiting = List.keydelete(waiting, pid, 0)
81+
spawn_requires(files, waiting, callbacks, schedulers, mods ++ result)
82+
83+
{:file_required, pid, _file, {kind, reason, stacktrace}} ->
84+
discard_down(pid)
85+
:erlang.raise(kind, reason, stacktrace)
86+
87+
{:DOWN, ref, :process, pid, reason} ->
88+
handle_down(waiting, pid, ref, reason)
89+
spawn_requires(files, waiting, callbacks, schedulers, result)
9290

9391
{:module_available, child, ref, file, module, binary} ->
9492
if each_module_callback = callbacks[:each_module] do
@@ -106,4 +104,17 @@ defmodule Kernel.ParallelRequire do
106104
spawn_requires(files, waiting, callbacks, schedulers, result)
107105
end
108106
end
107+
108+
defp discard_down(pid) do
109+
receive do
110+
{:DOWN, _, :process, ^pid, _} -> :ok
111+
end
112+
end
113+
114+
defp handle_down(waiting, pid, ref, reason) do
115+
if reason != :normal and {pid, ref} in waiting do
116+
:erlang.raise(:exit, reason, [])
117+
end
118+
:ok
119+
end
109120
end

0 commit comments

Comments
 (0)