From 15529b1392c3c3d452dcae3ce6786ff41fda9f13 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Fri, 17 Feb 2012 18:12:58 +0100 Subject: Generalize coordinator and worker --- lib/dialyzer/src/Makefile | 4 +- lib/dialyzer/src/dialyzer_coordinator.erl | 215 ++++++++++++++++++++++ lib/dialyzer/src/dialyzer_succ_typings.erl | 8 +- lib/dialyzer/src/dialyzer_typesig_coordinator.erl | 215 ---------------------- lib/dialyzer/src/dialyzer_typesig_worker.erl | 142 -------------- lib/dialyzer/src/dialyzer_worker.erl | 142 ++++++++++++++ 6 files changed, 363 insertions(+), 363 deletions(-) create mode 100644 lib/dialyzer/src/dialyzer_coordinator.erl delete mode 100644 lib/dialyzer/src/dialyzer_typesig_coordinator.erl delete mode 100644 lib/dialyzer/src/dialyzer_typesig_worker.erl create mode 100644 lib/dialyzer/src/dialyzer_worker.erl (limited to 'lib') diff --git a/lib/dialyzer/src/Makefile b/lib/dialyzer/src/Makefile index 2f1eaf5754..39b1b3eb3e 100644 --- a/lib/dialyzer/src/Makefile +++ b/lib/dialyzer/src/Makefile @@ -64,8 +64,8 @@ MODULES = \ dialyzer_races \ dialyzer_succ_typings \ dialyzer_typesig \ - dialyzer_typesig_coordinator \ - dialyzer_typesig_worker \ + dialyzer_coordinator \ + dialyzer_worker \ dialyzer_utils HRL_FILES= dialyzer.hrl dialyzer_gui_wx.hrl diff --git a/lib/dialyzer/src/dialyzer_coordinator.erl b/lib/dialyzer/src/dialyzer_coordinator.erl new file mode 100644 index 0000000000..a72d383365 --- /dev/null +++ b/lib/dialyzer/src/dialyzer_coordinator.erl @@ -0,0 +1,215 @@ +%% -*- erlang-indent-level: 2 -*- +%%----------------------------------------------------------------------- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2006-2011. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%%%------------------------------------------------------------------- +%%% File : dialyzer_coordinator.erl +%%% Authors : Stavros Aronis +%%% +%%% Description: +%%% +%%% The parallel version of Dialyzer's typesig analysis is spread over 4 modules +%%% with the intention to both minimize the changes on the original code and use +%%% a separate module for every kind of Erlang process that will be running. +%%% +%%% There are therefore 3 kinds of processes: +%%% +%%% - The original Dialyzer backend (in succ_typings module) +%%% - The worker process for the typesig analysis (in typesig and +%%% worker) +%%% - A coordinator of the worker processes (in coordinator) +%%% +%%% Operation guidelines: +%%% +%%% - The backend requests from the coordinator to spawn a worker for each SCC +%%% - The backend notifies the coordinator when all SCC have been spawned and +%%% waits for the server to report that the PLT has been updated +%%% - Each worker is responsible to notify all those who wait for it. +%%% +%%%------------------------------------------------------------------- + +-module(dialyzer_coordinator). + +-export([ + all_spawned/1, + scc_done/3, + scc_spawn/2, + sccs_to_pids_reply/0, + sccs_to_pids_request/2, + start/1, + receive_not_fixpoint/0 + ]). + +-behaviour(gen_server). + +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, + code_change/3]). + +-type coordinator() :: pid(). +-type map() :: dict(). +-type scc() :: [mfa_or_funlbl()]. + +-record(state, {parent :: pid(), + spawn_count = 0 :: integer(), + all_spawned = false :: boolean(), + scc_to_pid = new_map() :: map(), + not_fixpoint = [] :: [mfa_or_funlbl()], + servers :: dialyzer_typesig:servers() + }). + +-include("dialyzer.hrl"). + +%%-------------------------------------------------------------------- + +-spec start(dialyzer_typesig:servers()) -> pid(). + +start(Servers) -> + {ok, Pid} = gen_server:start(?MODULE, {self(), Servers}, []), + Pid. + +-spec scc_spawn(scc(), coordinator()) -> ok. + +scc_spawn(SCC, Coordinator) -> + cast({scc_spawn, SCC}, Coordinator). + +-spec sccs_to_pids_request([scc()], coordinator()) -> ok. + +sccs_to_pids_request(SCCs, Coordinator) -> + cast({sccs_to_pids, SCCs, self()}, Coordinator). + +scc_to_pids_request_handle(Worker, SCCs, SCCtoPID) -> + Pids = [fetch_map(SCC, SCCtoPID) || SCC <- SCCs], + Worker ! {sccs_to_pids, Pids}, + ok. + +-spec sccs_to_pids_reply() -> [dialyzer_worker:worker()]. + +sccs_to_pids_reply() -> + receive {sccs_to_pids, Pids} -> Pids end. + +-spec scc_done(scc(), scc(), coordinator()) -> ok. + +scc_done(SCC, NotFixpoint, Coordinator) -> + cast({scc_done, SCC, NotFixpoint}, Coordinator). + +-spec all_spawned(coordinator()) -> ok. + +all_spawned(Coordinator) -> + cast(all_spawned, Coordinator). + +send_done_to_parent(#state{parent = Parent, not_fixpoint = NotFixpoint}) -> + Parent ! {not_fixpoint, NotFixpoint}. + +-spec receive_not_fixpoint() -> dialyzer_plt:plt(). + +receive_not_fixpoint() -> + receive {not_fixpoint, NotFixpoint} -> NotFixpoint end. + +%%-------------------------------------------------------------------- + +-spec init([]) -> {ok, #state{}}. + +init({Parent, Servers}) -> + {ok, #state{parent = Parent, servers = Servers}}. + +-spec handle_call(Query::term(), From::term(), #state{}) -> + {reply, Reply::term(), #state{}}. + +handle_call(_Request, _From, State) -> + {reply, ok, State}. + +-spec handle_cast(Msg::term(), #state{}) -> + {noreply, #state{}} | {stop, normal, #state{}}. + +handle_cast({scc_done, _SCC, NotFixpoint}, + #state{spawn_count = SpawnCount, + all_spawned = AllSpawned, + not_fixpoint = OldNotFixpoint + } = State) -> + NewNotFixpoint = ordsets:union(OldNotFixpoint, NotFixpoint), + UpdatedState = State#state{not_fixpoint = NewNotFixpoint}, + Action = + case AllSpawned of + false -> reduce; + true -> + case SpawnCount of + 1 -> finish; + _ -> reduce + end + end, + case Action of + reduce -> + NewState = UpdatedState#state{spawn_count = SpawnCount - 1}, + {noreply, NewState}; + finish -> + send_done_to_parent(UpdatedState), + {stop, normal, State} + end; +handle_cast(all_spawned, #state{spawn_count = SpawnCount} = State) -> + case SpawnCount of + 0 -> + send_done_to_parent(State), + {stop, normal, State}; + _ -> + NewState = State#state{all_spawned = true}, + {noreply, NewState} + end; +handle_cast({sccs_to_pids, SCCs, Worker}, + #state{scc_to_pid = SCCtoPID} = State) -> + scc_to_pids_request_handle(Worker, SCCs, SCCtoPID), + {noreply, State}; +handle_cast({scc_spawn, SCC}, + #state{servers = Servers, + spawn_count = SpawnCount, + scc_to_pid = SCCtoPID + } = State) -> + Pid = dialyzer_worker:launch(SCC, Servers), + {noreply, + State#state{spawn_count = SpawnCount + 1, + scc_to_pid = store_map(SCC, Pid, SCCtoPID)} + }. + +-spec handle_info(term(), #state{}) -> {noreply, #state{}}. + +handle_info(_Info, State) -> + {noreply, State}. + +-spec terminate(term(), #state{}) -> ok. + +terminate(_Reason, _State) -> + ok. + +-spec code_change(term(), #state{}, term()) -> {ok, #state{}}. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%%-------------------------------------------------------------------- + +cast(Message, Coordinator) -> + gen_server:cast(Coordinator, Message). + +new_map() -> + dict:new(). + +store_map(Key, Value, Map) -> + dict:store(Key, Value, Map). + +fetch_map(Key, Map) -> + dict:fetch(Key, Map). diff --git a/lib/dialyzer/src/dialyzer_succ_typings.erl b/lib/dialyzer/src/dialyzer_succ_typings.erl index 76b6d8954b..237adb36ea 100644 --- a/lib/dialyzer/src/dialyzer_succ_typings.erl +++ b/lib/dialyzer/src/dialyzer_succ_typings.erl @@ -303,18 +303,18 @@ compare_types_1([], [], _Strict, NotFixpoint) -> find_succ_typings(SCCs, #st{codeserver = Codeserver, callgraph = Callgraph, plt = Plt} = State) -> Servers = {Codeserver, dialyzer_callgraph:mini_callgraph(Callgraph), Plt}, - Coordinator = dialyzer_typesig_coordinator:start(Servers), + Coordinator = dialyzer_coordinator:start(Servers), find_succ_typings(SCCs, State, Coordinator). find_succ_typings([SCC|Rest], #st{parent = Parent} = State, Coordinator) -> Msg = io_lib:format("Typesig analysis for SCC: ~w\n", [format_scc(SCC)]), ?debug("~s", [Msg]), send_log(Parent, Msg), - dialyzer_typesig_coordinator:scc_spawn(SCC, Coordinator), + dialyzer_coordinator:scc_spawn(SCC, Coordinator), find_succ_typings(Rest, State, Coordinator); find_succ_typings([], State, Coordinator) -> - dialyzer_typesig_coordinator:all_spawned(Coordinator), - NotFixpoint = dialyzer_typesig_coordinator:receive_not_fixpoint(), + dialyzer_coordinator:all_spawned(Coordinator), + NotFixpoint = dialyzer_coordinator:receive_not_fixpoint(), ?debug("==================== Typesig done ====================\n\n", []), case NotFixpoint =:= [] of true -> {fixpoint, State}; diff --git a/lib/dialyzer/src/dialyzer_typesig_coordinator.erl b/lib/dialyzer/src/dialyzer_typesig_coordinator.erl deleted file mode 100644 index 9475bc6895..0000000000 --- a/lib/dialyzer/src/dialyzer_typesig_coordinator.erl +++ /dev/null @@ -1,215 +0,0 @@ -%% -*- erlang-indent-level: 2 -*- -%%----------------------------------------------------------------------- -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2006-2011. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% - -%%%------------------------------------------------------------------- -%%% File : dialyzer_typesig_coordinator.erl -%%% Authors : Stavros Aronis -%%% -%%% Description: -%%% -%%% The parallel version of Dialyzer's typesig analysis is spread over 4 modules -%%% with the intention to both minimize the changes on the original code and use -%%% a separate module for every kind of Erlang process that will be running. -%%% -%%% There are therefore 3 kinds of processes: -%%% -%%% - The original Dialyzer backend (in succ_typings module) -%%% - The worker process for the typesig analysis (in typesig and -%%% typesig_worker) -%%% - A coordinator of the worker processes (in typesig_coordinator) -%%% -%%% Operation guidelines: -%%% -%%% - The backend requests from the coordinator to spawn a worker for each SCC -%%% - The backend notifies the coordinator when all SCC have been spawned and -%%% waits for the server to report that the PLT has been updated -%%% - Each worker is responsible to notify all those who wait for it. -%%% -%%%------------------------------------------------------------------- - --module(dialyzer_typesig_coordinator). - --export([ - all_spawned/1, - scc_done/3, - scc_spawn/2, - sccs_to_pids_reply/0, - sccs_to_pids_request/2, - start/1, - receive_not_fixpoint/0 - ]). - --behaviour(gen_server). - --export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, - code_change/3]). - --type coordinator() :: pid(). --type map() :: dict(). --type scc() :: [mfa_or_funlbl()]. - --record(state, {parent :: pid(), - spawn_count = 0 :: integer(), - all_spawned = false :: boolean(), - scc_to_pid = new_map() :: map(), - not_fixpoint = [] :: [mfa_or_funlbl()], - servers :: dialyzer_typesig:servers() - }). - --include("dialyzer.hrl"). - -%%-------------------------------------------------------------------- - --spec start(dialyzer_typesig:servers()) -> pid(). - -start(Servers) -> - {ok, Pid} = gen_server:start(?MODULE, {self(), Servers}, []), - Pid. - --spec scc_spawn(scc(), coordinator()) -> ok. - -scc_spawn(SCC, Coordinator) -> - cast({scc_spawn, SCC}, Coordinator). - --spec sccs_to_pids_request([scc()], coordinator()) -> ok. - -sccs_to_pids_request(SCCs, Coordinator) -> - cast({sccs_to_pids, SCCs, self()}, Coordinator). - -scc_to_pids_request_handle(Worker, SCCs, SCCtoPID) -> - Pids = [fetch_map(SCC, SCCtoPID) || SCC <- SCCs], - Worker ! {sccs_to_pids, Pids}, - ok. - --spec sccs_to_pids_reply() -> [dialyzer_typesig_worker:worker()]. - -sccs_to_pids_reply() -> - receive {sccs_to_pids, Pids} -> Pids end. - --spec scc_done(scc(), scc(), coordinator()) -> ok. - -scc_done(SCC, NotFixpoint, Coordinator) -> - cast({scc_done, SCC, NotFixpoint}, Coordinator). - --spec all_spawned(coordinator()) -> ok. - -all_spawned(Coordinator) -> - cast(all_spawned, Coordinator). - -send_done_to_parent(#state{parent = Parent, not_fixpoint = NotFixpoint}) -> - Parent ! {not_fixpoint, NotFixpoint}. - --spec receive_not_fixpoint() -> dialyzer_plt:plt(). - -receive_not_fixpoint() -> - receive {not_fixpoint, NotFixpoint} -> NotFixpoint end. - -%%-------------------------------------------------------------------- - --spec init([]) -> {ok, #state{}}. - -init({Parent, Servers}) -> - {ok, #state{parent = Parent, servers = Servers}}. - --spec handle_call(Query::term(), From::term(), #state{}) -> - {reply, Reply::term(), #state{}}. - -handle_call(_Request, _From, State) -> - {reply, ok, State}. - --spec handle_cast(Msg::term(), #state{}) -> - {noreply, #state{}} | {stop, normal, #state{}}. - -handle_cast({scc_done, _SCC, NotFixpoint}, - #state{spawn_count = SpawnCount, - all_spawned = AllSpawned, - not_fixpoint = OldNotFixpoint - } = State) -> - NewNotFixpoint = ordsets:union(OldNotFixpoint, NotFixpoint), - UpdatedState = State#state{not_fixpoint = NewNotFixpoint}, - Action = - case AllSpawned of - false -> reduce; - true -> - case SpawnCount of - 1 -> finish; - _ -> reduce - end - end, - case Action of - reduce -> - NewState = UpdatedState#state{spawn_count = SpawnCount - 1}, - {noreply, NewState}; - finish -> - send_done_to_parent(UpdatedState), - {stop, normal, State} - end; -handle_cast(all_spawned, #state{spawn_count = SpawnCount} = State) -> - case SpawnCount of - 0 -> - send_done_to_parent(State), - {stop, normal, State}; - _ -> - NewState = State#state{all_spawned = true}, - {noreply, NewState} - end; -handle_cast({sccs_to_pids, SCCs, Worker}, - #state{scc_to_pid = SCCtoPID} = State) -> - scc_to_pids_request_handle(Worker, SCCs, SCCtoPID), - {noreply, State}; -handle_cast({scc_spawn, SCC}, - #state{servers = Servers, - spawn_count = SpawnCount, - scc_to_pid = SCCtoPID - } = State) -> - Pid = dialyzer_typesig_worker:launch(SCC, Servers), - {noreply, - State#state{spawn_count = SpawnCount + 1, - scc_to_pid = store_map(SCC, Pid, SCCtoPID)} - }. - --spec handle_info(term(), #state{}) -> {noreply, #state{}}. - -handle_info(_Info, State) -> - {noreply, State}. - --spec terminate(term(), #state{}) -> ok. - -terminate(_Reason, _State) -> - ok. - --spec code_change(term(), #state{}, term()) -> {ok, #state{}}. - -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - -%%-------------------------------------------------------------------- - -cast(Message, Coordinator) -> - gen_server:cast(Coordinator, Message). - -new_map() -> - dict:new(). - -store_map(Key, Value, Map) -> - dict:store(Key, Value, Map). - -fetch_map(Key, Map) -> - dict:fetch(Key, Map). diff --git a/lib/dialyzer/src/dialyzer_typesig_worker.erl b/lib/dialyzer/src/dialyzer_typesig_worker.erl deleted file mode 100644 index 3a7cd22eed..0000000000 --- a/lib/dialyzer/src/dialyzer_typesig_worker.erl +++ /dev/null @@ -1,142 +0,0 @@ -%% -*- erlang-indent-level: 2 -*- -%%----------------------------------------------------------------------- -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2006-2010. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% - --module(dialyzer_typesig_worker). - --export([launch/2]). - --type worker() :: pid(). - --record(state, { - scc = [] :: mfa_or_funlbl(), - depends_on = [] :: list(), - coordinator :: dialyzer_coordinator:coordinator(), - servers :: dialyzer_typesig:servers(), - scc_data :: dialyzer_typesig:scc_data() - }). - --include("dialyzer.hrl"). - -%% -define(DEBUG, true). - --ifdef(DEBUG). --define(debug(X__, Y__), io:format(X__, Y__)). --else. --define(debug(X__, Y__), ok). --endif. - -%%-------------------------------------------------------------------- - --spec launch([mfa_or_funlbl()], dialyzer_typesig:servers()) -> worker(). - -launch(SCC, Servers) -> - State = #state{scc = SCC, - servers = Servers, - coordinator = self()}, - spawn(fun() -> loop(initializing, State) end). - -%%-------------------------------------------------------------------- - -loop(updating, State) -> - ?debug("Update: ~p\n",[State#state.scc]), - NextStatus = - case waits_more_success_typings(State) of - true -> waiting; - Other -> - case has_data(State) of - false -> getting_data; - true -> - case Other of - imminent -> waiting; - false -> running - end - end - end, - loop(NextStatus, State); -loop(initializing, #state{scc = SCC, servers = Servers} = State) -> - DependsOn = dialyzer_succ_typings:find_depends_on(SCC, Servers), - WithoutSelf = DependsOn -- [SCC], - ?debug("Deps ~p: ~p\n",[State#state.scc, WithoutSelf]), - loop(updating, State#state{depends_on = WithoutSelf}); -loop(waiting, State) -> - ?debug("Wait: ~p\n",[State#state.scc]), - NewState = wait_for_success_typings(State), - loop(updating, NewState); -loop(getting_data, State) -> - ?debug("Data: ~p\n",[State#state.scc]), - loop(updating, get_typesig_data(State)); -loop(running, State) -> - ?debug("Run: ~p\n",[State#state.scc]), - ok = ask_coordinator_for_callers(State), - NotFixpoint = find_succ_typings(State), - Callers = get_callers_reply_from_coordinator(), - ok = broadcast_own_succ_typings(State, Callers), - report_to_coordinator(NotFixpoint, State). - -waits_more_success_typings(#state{depends_on = Depends}) -> - case Depends of - [] -> false; - [_] -> imminent; - _ -> true - end. - -has_data(#state{scc_data = Data}) -> - case Data of - undefined -> false; - _ -> true - end. - -get_typesig_data(#state{scc = SCC, servers = Servers} = State) -> - State#state{scc_data = dialyzer_succ_typings:collect_scc_data(SCC, Servers)}. - -ask_coordinator_for_callers(#state{scc = SCC, - servers = Servers, - coordinator = Coordinator}) -> - RequiredBy = dialyzer_succ_typings:find_required_by(SCC, Servers), - WithoutSelf = RequiredBy -- [SCC], - ?debug("Waiting for me~p: ~p\n",[SCC, WithoutSelf]), - dialyzer_typesig_coordinator:sccs_to_pids_request(WithoutSelf, Coordinator). - -get_callers_reply_from_coordinator() -> - dialyzer_typesig_coordinator:sccs_to_pids_reply(). - -broadcast_own_succ_typings(#state{scc = SCC}, Callers) -> - ?debug("Sending ~p: ~p\n",[SCC, Callers]), - SendSTFun = fun(PID) -> PID ! {done, SCC} end, - lists:foreach(SendSTFun, Callers). - -wait_for_success_typings(#state{depends_on = DependsOn} = State) -> - receive - {done, SCC} -> - ?debug("GOT ~p: ~p\n",[State#state.scc, SCC]), - State#state{depends_on = DependsOn -- [SCC]} - after - 5000 -> - ?debug("Still Waiting ~p: ~p\n",[State#state.scc, DependsOn]), - State - end. - -find_succ_typings(#state{scc_data = SCCData}) -> - dialyzer_succ_typings:find_succ_types_for_scc(SCCData). - -report_to_coordinator(NotFixpoint, - #state{scc = SCC, coordinator = Coordinator}) -> - ?debug("Done: ~p\n",[SCC]), - dialyzer_typesig_coordinator:scc_done(SCC, NotFixpoint, Coordinator). diff --git a/lib/dialyzer/src/dialyzer_worker.erl b/lib/dialyzer/src/dialyzer_worker.erl new file mode 100644 index 0000000000..0bfdcf6bdb --- /dev/null +++ b/lib/dialyzer/src/dialyzer_worker.erl @@ -0,0 +1,142 @@ +%% -*- erlang-indent-level: 2 -*- +%%----------------------------------------------------------------------- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2006-2010. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +-module(dialyzer_worker). + +-export([launch/2]). + +-type worker() :: pid(). + +-record(state, { + scc = [] :: mfa_or_funlbl(), + depends_on = [] :: list(), + coordinator :: dialyzer_coordinator:coordinator(), + servers :: dialyzer_typesig:servers(), + scc_data :: dialyzer_typesig:scc_data() + }). + +-include("dialyzer.hrl"). + +%% -define(DEBUG, true). + +-ifdef(DEBUG). +-define(debug(X__, Y__), io:format(X__, Y__)). +-else. +-define(debug(X__, Y__), ok). +-endif. + +%%-------------------------------------------------------------------- + +-spec launch([mfa_or_funlbl()], dialyzer_typesig:servers()) -> worker(). + +launch(SCC, Servers) -> + State = #state{scc = SCC, + servers = Servers, + coordinator = self()}, + spawn(fun() -> loop(initializing, State) end). + +%%-------------------------------------------------------------------- + +loop(updating, State) -> + ?debug("Update: ~p\n",[State#state.scc]), + NextStatus = + case waits_more_success_typings(State) of + true -> waiting; + Other -> + case has_data(State) of + false -> getting_data; + true -> + case Other of + imminent -> waiting; + false -> running + end + end + end, + loop(NextStatus, State); +loop(initializing, #state{scc = SCC, servers = Servers} = State) -> + DependsOn = dialyzer_succ_typings:find_depends_on(SCC, Servers), + WithoutSelf = DependsOn -- [SCC], + ?debug("Deps ~p: ~p\n",[State#state.scc, WithoutSelf]), + loop(updating, State#state{depends_on = WithoutSelf}); +loop(waiting, State) -> + ?debug("Wait: ~p\n",[State#state.scc]), + NewState = wait_for_success_typings(State), + loop(updating, NewState); +loop(getting_data, State) -> + ?debug("Data: ~p\n",[State#state.scc]), + loop(updating, get_typesig_data(State)); +loop(running, State) -> + ?debug("Run: ~p\n",[State#state.scc]), + ok = ask_coordinator_for_callers(State), + NotFixpoint = find_succ_typings(State), + Callers = get_callers_reply_from_coordinator(), + ok = broadcast_own_succ_typings(State, Callers), + report_to_coordinator(NotFixpoint, State). + +waits_more_success_typings(#state{depends_on = Depends}) -> + case Depends of + [] -> false; + [_] -> imminent; + _ -> true + end. + +has_data(#state{scc_data = Data}) -> + case Data of + undefined -> false; + _ -> true + end. + +get_typesig_data(#state{scc = SCC, servers = Servers} = State) -> + State#state{scc_data = dialyzer_succ_typings:collect_scc_data(SCC, Servers)}. + +ask_coordinator_for_callers(#state{scc = SCC, + servers = Servers, + coordinator = Coordinator}) -> + RequiredBy = dialyzer_succ_typings:find_required_by(SCC, Servers), + WithoutSelf = RequiredBy -- [SCC], + ?debug("Waiting for me~p: ~p\n",[SCC, WithoutSelf]), + dialyzer_coordinator:sccs_to_pids_request(WithoutSelf, Coordinator). + +get_callers_reply_from_coordinator() -> + dialyzer_coordinator:sccs_to_pids_reply(). + +broadcast_own_succ_typings(#state{scc = SCC}, Callers) -> + ?debug("Sending ~p: ~p\n",[SCC, Callers]), + SendSTFun = fun(PID) -> PID ! {done, SCC} end, + lists:foreach(SendSTFun, Callers). + +wait_for_success_typings(#state{depends_on = DependsOn} = State) -> + receive + {done, SCC} -> + ?debug("GOT ~p: ~p\n",[State#state.scc, SCC]), + State#state{depends_on = DependsOn -- [SCC]} + after + 5000 -> + ?debug("Still Waiting ~p: ~p\n",[State#state.scc, DependsOn]), + State + end. + +find_succ_typings(#state{scc_data = SCCData}) -> + dialyzer_succ_typings:find_succ_types_for_scc(SCCData). + +report_to_coordinator(NotFixpoint, + #state{scc = SCC, coordinator = Coordinator}) -> + ?debug("Done: ~p\n",[SCC]), + dialyzer_coordinator:scc_done(SCC, NotFixpoint, Coordinator). -- cgit v1.2.3