@@ -68,17 +68,37 @@ defmodule Module.ParallelChecker do
6868 def spawn ( { pid , { checker , table } } , module , module_map , signatures , beam_location , log? ) do
6969 # Protocols may have been consolidated. So if we know their beam location,
7070 # we discard their module map on purpose and start from file.
71- info =
72- if beam_location != [ ] and Keyword . has_key? ( module_map . attributes , :__protocol__ ) do
73- List . to_string ( beam_location )
74- else
75- cache_from_module_map ( table , module_map , signatures )
76- end
71+ if beam_location != [ ] and Keyword . has_key? ( module_map . attributes , :__protocol__ ) do
72+ spawn_and_register_cache ( pid , checker , table , module , List . to_string ( beam_location ) , log? )
73+ else
74+ { mode , module_tuple } = cache_from_module_map ( table , module_map , signatures )
75+ ref = make_ref ( )
76+ spawned = spawn_checker ( ref , pid , checker , table , module , module_tuple , log? )
77+ register_cache_and_checker ( checker , mode , module , spawned , ref )
78+ :ok
79+ end
80+ end
7781
78- inner_spawn ( pid , checker , table , module , info , log? )
82+ defp spawn_checker ( ref , pid , checker , table , module , module_tuple , log? ) do
83+ spawn ( fn ->
84+ mon_ref = Process . monitor ( pid )
85+
86+ receive do
87+ { ^ ref , :check , profile } ->
88+ # Set the compiler info so we can collect warnings
89+ Process . link ( pid )
90+ :erlang . put ( :elixir_compiler_info , { pid , self ( ) } )
91+ { warnings , errors } = check_module ( module_tuple , { checker , table } , log? , profile )
92+ send ( pid , { __MODULE__ , module , warnings , errors } )
93+ send ( checker , { __MODULE__ , :checked , ref } )
94+
95+ { :DOWN , ^ mon_ref , _ , _ , _ } ->
96+ :ok
97+ end
98+ end )
7999 end
80100
81- defp inner_spawn ( pid , checker , table , module , info , log? ) do
101+ defp spawn_and_register_cache ( pid , checker , table , module , info , log? ) do
82102 ref = make_ref ( )
83103
84104 spawned =
@@ -89,56 +109,36 @@ defmodule Module.ParallelChecker do
89109 { ^ ref , :cache } ->
90110 Process . link ( pid )
91111
92- { mode , module_tuple } =
93- cond do
94- is_binary ( info ) ->
95- location =
96- case :code . which ( module ) do
97- [ _ | _ ] = path -> path
98- _ -> info
99- end
100-
101- with { :ok , binary } <- File . read ( location ) ,
102- { :ok ,
103- { _ , [ { :debug_info , { :debug_info_v1 , backend , data } } , { ~c" ExCk" , checker } ] } } <-
104- :beam_lib . chunks ( binary , [ :debug_info , ~c" ExCk" ] ) ,
105- { :ok , module_map } <- backend . debug_info ( :elixir_v1 , module , data , [ ] ) ,
106- { @ elixir_checker_version , contents } <- :erlang . binary_to_term ( checker ) do
107- { cache_chunk ( table , module , contents ) , module_map_to_module_tuple ( module_map ) }
108- else
109- _ -> { :uncached , nil }
110- end
111-
112- is_tuple ( info ) ->
113- info
112+ location =
113+ case :code . which ( module ) do
114+ [ _ | _ ] = path -> path
115+ _ -> info
114116 end
115117
116- # We only make the module available now, so they are not visible during inference
117- :ets . insert ( table , { module , mode } )
118- send ( checker , { ref , :cached } )
119-
120- receive do
121- { ^ ref , :check , profile } ->
122- # Set the compiler info so we can collect warnings
123- :erlang . put ( :elixir_compiler_info , { pid , self ( ) } )
124-
125- { warnings , errors } =
126- if module_tuple do
127- check_module ( module_tuple , { checker , table } , log? , profile )
128- else
129- { [ ] , [ ] }
130- end
131-
132- send ( pid , { __MODULE__ , module , warnings , errors } )
133- send ( checker , { __MODULE__ , :done , module } )
118+ with { :ok , binary } <- File . read ( location ) ,
119+ { :ok , { _ , [ { :debug_info , { :debug_info_v1 , backend , data } } , { ~c" ExCk" , exck } ] } } <-
120+ :beam_lib . chunks ( binary , [ :debug_info , ~c" ExCk" ] ) ,
121+ { :ok , module_map } <- backend . debug_info ( :elixir_v1 , module , data , [ ] ) ,
122+ { @ elixir_checker_version , contents } <- :erlang . binary_to_term ( exck ) do
123+ mode = cache_chunk ( table , module , contents )
124+ module_tuple = module_map_to_module_tuple ( module_map )
125+ spawned = spawn_checker ( ref , pid , checker , table , module , module_tuple , log? )
126+ :ets . insert ( table , { module , mode } )
127+ send ( checker , { __MODULE__ , :cached , module , spawned , ref } )
128+ else
129+ _ ->
130+ # Nothing to check, so we notify everyone we are done
131+ :ets . insert ( table , { module , :uncached } )
132+ send ( checker , { __MODULE__ , :cached , module , nil , ref } )
133+ send ( pid , { __MODULE__ , module , [ ] , [ ] } )
134134 end
135135
136136 { :DOWN , ^ mon_ref , _ , _ , _ } ->
137137 :ok
138138 end
139139 end )
140140
141- register ( checker , module , spawned , ref )
141+ register_cache ( checker , module , spawned , ref )
142142 :ok
143143 end
144144
@@ -192,7 +192,7 @@ defmodule Module.ParallelChecker do
192192 log? = not match? ( { _ , false } , value )
193193
194194 for { module , file } <- runtime_files do
195- inner_spawn ( self ( ) , checker , table , module , file , log? )
195+ spawn_and_register_cache ( self ( ) , checker , table , module , file , log? )
196196 end
197197
198198 count = :gen_server . call ( checker , :start , :infinity )
@@ -533,8 +533,12 @@ defmodule Module.ParallelChecker do
533533 :gen_server . call ( server , { :unlock , module , mode } , :infinity )
534534 end
535535
536- defp register ( server , module , pid , ref ) do
537- :gen_server . cast ( server , { :register , module , pid , ref } )
536+ defp register_cache_and_checker ( server , mode , module , pid , ref ) do
537+ :gen_server . cast ( server , { :register_cache_and_checker , mode , module , pid , ref } )
538+ end
539+
540+ defp register_cache ( server , module , pid , ref ) do
541+ :gen_server . cast ( server , { :register_cache , module , pid , ref } )
538542 end
539543
540544 ## Server callbacks
@@ -577,7 +581,8 @@ defmodule Module.ParallelChecker do
577581
578582 state = % {
579583 waiting: % { } ,
580- modules: [ ] ,
584+ caches: [ ] ,
585+ checkers: [ ] ,
581586 spawned: % { } ,
582587 schedulers: schedulers ,
583588 threshold: threshold ,
@@ -590,20 +595,9 @@ defmodule Module.ParallelChecker do
590595 :gen_server . enter_loop ( __MODULE__ , [ ] , state )
591596 end
592597
593- def handle_call ( :start , _from , % { modules: modules , protocols: protocols , table: table } = state ) do
598+ def handle_call ( :start , _from , % { caches: caches , protocols: protocols , table: table } = state ) do
594599 :ets . insert ( table , Enum . map ( protocols , & { & 1 , :uncached } ) )
595-
596- for { _module , pid , ref } <- modules do
597- send ( pid , { ref , :cache } )
598- end
599-
600- for { _module , _pid , ref } <- modules do
601- receive do
602- { ^ ref , :cached } -> :ok
603- end
604- end
605-
606- { :reply , length ( modules ) , run_checkers ( % { state | protocols: [ ] } ) }
600+ { :reply , length ( caches ) , run_caches ( % { state | protocols: [ ] } ) }
607601 end
608602
609603 def handle_call ( { :lock , module } , from , % { waiting: waiting } = state ) do
@@ -633,10 +627,21 @@ defmodule Module.ParallelChecker do
633627 { :noreply , state }
634628 end
635629
636- def handle_info ( { __MODULE__ , :done , module } , state ) do
637- # Unfortunately we cannot assume uniqueness because the same module
638- # may be defined by mistake several times
639- { timer , spawned } = Map . pop ( state . spawned , module )
630+ def handle_info ( { __MODULE__ , :cached , module , pid , ref } , state ) do
631+ { _nil , spawned } = Map . pop ( state . spawned , ref )
632+
633+ state =
634+ if pid do
635+ % { state | spawned: spawned , checkers: [ { module , pid , ref } | state . checkers ] }
636+ else
637+ % { state | spawned: spawned }
638+ end
639+
640+ { :noreply , run_caches ( state ) }
641+ end
642+
643+ def handle_info ( { __MODULE__ , :checked , ref } , state ) do
644+ { timer , spawned } = Map . pop ( state . spawned , ref )
640645 timer && Process . cancel_timer ( timer )
641646 { :noreply , run_checkers ( % { state | spawned: spawned } ) }
642647 end
@@ -645,11 +650,41 @@ defmodule Module.ParallelChecker do
645650 { :stop , :normal , state }
646651 end
647652
648- def handle_cast ( { :register , module , pid , ref } , % { modules: modules } = state ) do
649- { :noreply , % { state | modules: [ { module , pid , ref } | modules ] } }
653+ def handle_cast ( { :register_cache , module , pid , ref } , % { caches: caches } = state ) do
654+ { :noreply , % { state | caches: [ { module , pid , ref } | caches ] } }
655+ end
656+
657+ def handle_cast (
658+ { :register_cache_and_checker , mode , module , pid , ref } ,
659+ % { caches: caches , checkers: checkers } = state
660+ ) do
661+ { :noreply ,
662+ % { state | caches: [ { module , mode } | caches ] , checkers: [ { module , pid , ref } | checkers ] } }
663+ end
664+
665+ defp run_caches ( % { caches: [ ] , spawned: spawned } = state ) do
666+ if spawned == % { } , do: run_checkers ( state ) , else: state
667+ end
668+
669+ defp run_caches ( % { spawned: spawned , schedulers: schedulers } = state )
670+ when map_size ( spawned ) >= schedulers do
671+ state
672+ end
673+
674+ defp run_caches ( % { caches: [ cache | caches ] } = state ) do
675+ case cache do
676+ { _module , pid , ref } ->
677+ send ( pid , { ref , :cache } )
678+ spawned = Map . put ( state . spawned , ref , nil )
679+ run_caches ( % { state | caches: caches , spawned: spawned } )
680+
681+ { module , mode } ->
682+ :ets . insert ( state . table , { module , mode } )
683+ run_caches ( % { state | caches: caches } )
684+ end
650685 end
651686
652- defp run_checkers ( % { modules : [ ] } = state ) do
687+ defp run_checkers ( % { checkers : [ ] } = state ) do
653688 state
654689 end
655690
@@ -658,11 +693,11 @@ defmodule Module.ParallelChecker do
658693 state
659694 end
660695
661- defp run_checkers ( % { modules : [ { module , pid , ref } | modules ] } = state ) do
696+ defp run_checkers ( % { checkers : [ { module , pid , ref } | checkers ] } = state ) do
662697 send ( pid , { ref , :check , state . profile } )
663698 timer = :erlang . send_after ( state . threshold , self ( ) , { __MODULE__ , :timeout , module , pid } )
664- spawned = Map . put ( state . spawned , module , timer )
665- run_checkers ( % { state | modules: modules , spawned: spawned } )
699+ spawned = Map . put ( state . spawned , ref , timer )
700+ run_checkers ( % { state | checkers: checkers , spawned: spawned } )
666701 end
667702
668703 defp profile ( module , :time , fun ) do
0 commit comments