1%% -*- erlang-indent-level: 2 -*- 2%% 3%% Licensed under the Apache License, Version 2.0 (the "License"); 4%% you may not use this file except in compliance with the License. 5%% You may obtain a copy of the License at 6%% 7%% http://www.apache.org/licenses/LICENSE-2.0 8%% 9%% Unless required by applicable law or agreed to in writing, software 10%% distributed under the License is distributed on an "AS IS" BASIS, 11%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12%% See the License for the specific language governing permissions and 13%% limitations under the License. 14 15-module(dialyzer_worker). 16 17-export([launch/4]). 18 19-export_type([worker/0]). 20 21-opaque worker() :: pid(). 22 23-type mode() :: dialyzer_coordinator:mode(). 24-type coordinator() :: dialyzer_coordinator:coordinator(). 25-type init_data() :: dialyzer_coordinator:init_data(). 26-type job() :: dialyzer_coordinator:job(). 27 28-record(state, { 29 mode :: mode(), 30 job :: job(), 31 coordinator :: coordinator(), 32 init_data :: init_data(), 33 depends_on = [] :: list() 34 }). 35 36-include("dialyzer.hrl"). 37 38%% -define(DEBUG, true). 39 40-ifdef(DEBUG). 41-define(debug(X__, Y__), io:format(X__, Y__)). 42-else. 43-define(debug(X__, Y__), ok). 44-endif. 45 46%%-------------------------------------------------------------------- 47 48-spec launch(mode(), job(), init_data(), coordinator()) -> worker(). 49 50launch(Mode, Job, InitData, Coordinator) -> 51 State = #state{mode = Mode, 52 job = Job, 53 init_data = InitData, 54 coordinator = Coordinator}, 55 spawn_link(fun() -> init(State) end). 56 57%%-------------------------------------------------------------------- 58 59init(#state{job = SCC, mode = Mode, init_data = InitData, 60 coordinator = Coordinator} = State) when 61 Mode =:= 'typesig'; Mode =:= 'dataflow' -> 62 DependsOnSCCs = dialyzer_succ_typings:find_depends_on(SCC, InitData), 63 ?debug("~w: Deps ~p: ~p\n", [self(), SCC, DependsOnSCCs]), 64 Pids = dialyzer_coordinator:sccs_to_pids(DependsOnSCCs, Coordinator), 65 ?debug("~w: PidsDeps ~p\n", [self(), Pids]), 66 DependsOn = [{Pid, erlang:monitor(process, Pid)} || Pid <- Pids], 67 loop(updating, State#state{depends_on = DependsOn}); 68init(#state{mode = Mode} = State) when 69 Mode =:= 'compile'; Mode =:= 'warnings' -> 70 loop(running, State). 71 72loop(updating, #state{mode = Mode} = State) when 73 Mode =:= 'typesig'; Mode =:= 'dataflow' -> 74 ?debug("~w: Update: ~p\n", [self(), State#state.job]), 75 NextStatus = 76 case waits_more_success_typings(State) of 77 true -> waiting; 78 false -> running 79 end, 80 loop(NextStatus, State); 81loop(waiting, #state{mode = Mode} = State) when 82 Mode =:= 'typesig'; Mode =:= 'dataflow' -> 83 ?debug("~w: Wait: ~p\n", [self(), State#state.job]), 84 NewState = wait_for_success_typings(State), 85 loop(updating, NewState); 86loop(running, #state{mode = 'compile'} = State) -> 87 request_activation(State), 88 ?debug("Compile: ~s\n",[State#state.job]), 89 Result = 90 case start_compilation(State) of 91 {ok, EstimatedSize, Data} -> 92 Label = ask_coordinator_for_label(EstimatedSize, State), 93 continue_compilation(Label, Data); 94 {error, _Reason} = Error -> 95 Error 96 end, 97 report_to_coordinator(Result, State); 98loop(running, #state{mode = 'warnings'} = State) -> 99 request_activation(State), 100 ?debug("Warning: ~s\n",[State#state.job]), 101 Result = collect_warnings(State), 102 report_to_coordinator(Result, State); 103loop(running, #state{mode = Mode} = State) when 104 Mode =:= 'typesig'; Mode =:= 'dataflow' -> 105 request_activation(State), 106 ?debug("~w: Run: ~p\n", [self(), State#state.job]), 107 NotFixpoint = do_work(State), 108 report_to_coordinator(NotFixpoint, State). 109 110waits_more_success_typings(#state{depends_on = Depends}) -> 111 Depends =/= []. 112 113wait_for_success_typings(#state{depends_on = DependsOn} = State) -> 114 receive 115 {'DOWN', Ref, process, Pid, _Info} -> 116 ?debug("~w: ~p got DOWN: ~p\n", [self(), State#state.job, Pid]), 117 State#state{depends_on = DependsOn -- [{Pid, Ref}]} 118 after 119 5000 -> 120 ?debug("~w: Still Waiting ~p:\n ~p\n", [self(), State#state.job, DependsOn]), 121 State 122 end. 123 124request_activation(#state{coordinator = Coordinator}) -> 125 dialyzer_coordinator:request_activation(Coordinator). 126 127do_work(#state{mode = Mode, job = Job, init_data = InitData}) -> 128 case Mode of 129 typesig -> dialyzer_succ_typings:find_succ_types_for_scc(Job, InitData); 130 dataflow -> dialyzer_succ_typings:refine_one_module(Job, InitData) 131 end. 132 133report_to_coordinator(Result, #state{job = Job, coordinator = Coordinator}) -> 134 ?debug("~w: Done: ~p\n",[self(), Job]), 135 dialyzer_coordinator:job_done(Job, Result, Coordinator). 136 137start_compilation(#state{job = Job, init_data = InitData}) -> 138 dialyzer_analysis_callgraph:start_compilation(Job, InitData). 139 140ask_coordinator_for_label(EstimatedSize, #state{coordinator = Coordinator}) -> 141 dialyzer_coordinator:get_next_label(EstimatedSize, Coordinator). 142 143continue_compilation(Label, Data) -> 144 dialyzer_analysis_callgraph:continue_compilation(Label, Data). 145 146collect_warnings(#state{job = Job, init_data = InitData}) -> 147 dialyzer_succ_typings:collect_warnings(Job, InitData). 148