@@ -198,7 +198,7 @@ defmodule Kernel.ParallelCompiler do
198198 { :ok , [ atom ] , [ warning ] | info ( ) }
199199 | { :error , [ error ] | [ Code . diagnostic ( :error ) ] , [ warning ] | info ( ) }
200200 def compile_to_path ( files , path , options \\ [ ] ) when is_binary ( path ) and is_list ( options ) do
201- spawn_workers ( files , { :compile , path } , options )
201+ spawn_workers ( files , { :compile , path } , Keyword . put ( options , :dest , path ) )
202202 end
203203
204204 @ doc """
@@ -338,8 +338,11 @@ defmodule Kernel.ParallelCompiler do
338338 end
339339
340340 defp write_module_binaries ( result , { :compile , path } , timestamp ) do
341+ File . mkdir_p! ( path )
342+ Code . prepend_path ( path )
343+
341344 Enum . flat_map ( result , fn
342- { { :module , module } , binary } when is_binary ( binary ) ->
345+ { { :module , module } , { binary , _ } } when is_binary ( binary ) ->
343346 full_path = Path . join ( path , Atom . to_string ( module ) <> ".beam" )
344347 File . write! ( full_path , binary )
345348 if timestamp , do: File . touch! ( full_path , timestamp )
@@ -351,7 +354,7 @@ defmodule Kernel.ParallelCompiler do
351354 end
352355
353356 defp write_module_binaries ( result , _output , _timestamp ) do
354- for { { :module , module } , binary } when is_binary ( binary ) <- result , do: module
357+ for { { :module , module } , { binary , _ } } when is_binary ( binary ) <- result , do: module
355358 end
356359
357360 ## Verification
@@ -366,7 +369,7 @@ defmodule Kernel.ParallelCompiler do
366369
367370 defp maybe_check_modules ( result , runtime_modules , state ) do
368371 compiled_modules =
369- for { { :module , module } , binary } when is_binary ( binary ) <- result ,
372+ for { { :module , module } , { binary , _ } } when is_binary ( binary ) <- result ,
370373 do: module
371374
372375 profile (
@@ -439,8 +442,8 @@ defmodule Kernel.ParallelCompiler do
439442
440443 try do
441444 case output do
442- { :compile , path } -> compile_file ( file , path , parent )
443- :compile -> compile_file ( file , dest , parent )
445+ { :compile , _ } -> compile_file ( file , dest , false , parent )
446+ :compile -> compile_file ( file , dest , true , parent )
444447 :require -> require_file ( file , parent )
445448 end
446449 catch
@@ -546,9 +549,9 @@ defmodule Kernel.ParallelCompiler do
546549 wait_for_messages ( [ ] , spawned , waiting , files , result , warnings , errors , state )
547550 end
548551
549- defp compile_file ( file , path , parent ) do
552+ defp compile_file ( file , path , force_load? , parent ) do
550553 :erlang . process_flag ( :error_handler , Kernel.ErrorHandler )
551- :erlang . put ( :elixir_compiler_dest , path )
554+ :erlang . put ( :elixir_compiler_dest , { path , force_load? } )
552555 :elixir_compiler . file ( file , & each_file ( & 1 , & 2 , parent ) )
553556 end
554557
@@ -582,7 +585,7 @@ defmodule Kernel.ParallelCompiler do
582585 end
583586
584587 defp count_modules ( result ) do
585- Enum . count ( result , & match? ( { { :module , _ } , binary } when is_binary ( binary ) , & 1 ) )
588+ Enum . count ( result , & match? ( { { :module , _ } , { binary , _ } } when is_binary ( binary ) , & 1 ) )
586589 end
587590
588591 defp each_cycle_return ( { kind , modules , warnings } ) , do: { kind , modules , warnings }
@@ -649,19 +652,37 @@ defmodule Kernel.ParallelCompiler do
649652 state
650653 )
651654
652- { :module_available , child , ref , file , module , binary } ->
653- state . each_module . ( file , module , binary )
655+ { { :module_loaded , module } , _ref , _type , _pid , _reason } ->
656+ result =
657+ Map . update! ( result , { :module , module } , fn { binary , _loader } -> { binary , true } end )
658+
659+ spawn_workers ( queue , spawned , waiting , files , result , warnings , errors , state )
654660
655- # Release the module loader which is waiting for an ack
661+ { :module_available , child , ref , file , module , binary , loaded? } ->
662+ state . each_module . ( file , module , binary )
656663 send ( child , { ref , :ack } )
657- { available , result } = update_result ( result , :module , module , binary )
664+
665+ { available , load_status } =
666+ case Map . get ( result , { :module , module } ) do
667+ [ _ | _ ] = pids when loaded? ->
668+ { Enum . map ( pids , & { & 1 , :found } ) , loaded? }
669+
670+ # When compiling files to disk, we only load the module
671+ # if other modules are waiting for it.
672+ [ _ | _ ] = pids ->
673+ pid = load_module ( module , binary , state . dest )
674+ { Enum . map ( pids , & { & 1 , { :loading , pid } } ) , pid }
675+
676+ _ ->
677+ { [ ] , loaded? }
678+ end
658679
659680 spawn_workers (
660681 available ++ queue ,
661682 spawned ,
662683 waiting ,
663684 files ,
664- result ,
685+ Map . put ( result , { :module , module } , { binary , load_status } ) ,
665686 warnings ,
666687 errors ,
667688 state
@@ -680,7 +701,9 @@ defmodule Kernel.ParallelCompiler do
680701
681702 { waiting , files , result } =
682703 if not is_list ( available_or_pending ) or on in defining do
683- send ( child_pid , { ref , :found } )
704+ # If what we are waiting on was defined but not loaded, we do it now.
705+ { reply , result } = load_pending ( kind , on , result , state )
706+ send ( child_pid , { ref , reply } )
684707 { waiting , files , result }
685708 else
686709 waiting = Map . put ( waiting , child_pid , { kind , ref , file_pid , on , defining , deadlock } )
@@ -774,6 +797,55 @@ defmodule Kernel.ParallelCompiler do
774797 { { :error , Enum . reverse ( errors , fun . ( ) ) , info } , state }
775798 end
776799
800+ defp load_pending ( kind , module , result , state ) do
801+ case result do
802+ % { { :module , ^ module } => { binary , load_status } }
803+ when kind in [ :module , :struct ] and is_binary ( binary ) ->
804+ case load_status do
805+ true ->
806+ { :found , result }
807+
808+ false ->
809+ pid = load_module ( module , binary , state . dest )
810+ result = Map . put ( result , { :module , module } , { binary , pid } )
811+ { { :loading , pid } , result }
812+
813+ pid when is_pid ( pid ) ->
814+ { { :loading , pid } , result }
815+ end
816+
817+ _ ->
818+ { :found , result }
819+ end
820+ end
821+
822+ # We load modules in a separate process to avoid blocking
823+ # the parallel compiler. We store the PID of this process and
824+ # all entries monitor it to know once the module is loaded.
825+ defp load_module ( module , binary , dest ) do
826+ { pid , _ref } =
827+ :erlang . spawn_opt (
828+ fn ->
829+ beam_location =
830+ case dest do
831+ nil ->
832+ [ ]
833+
834+ dest ->
835+ :filename . join (
836+ :elixir_utils . characters_to_list ( dest ) ,
837+ Atom . to_charlist ( module ) ++ ~c" .beam"
838+ )
839+ end
840+
841+ :code . load_binary ( module , beam_location , binary )
842+ end ,
843+ monitor: [ tag: { :module_loaded , module } ]
844+ )
845+
846+ pid
847+ end
848+
777849 defp update_result ( result , kind , module , value ) do
778850 available =
779851 case Map . get ( result , { kind , module } ) do
0 commit comments