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