diff --git a/.gitignore b/.gitignore index b455448..7729023 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ ebin/*.beam ebin/*.rel erl_crash.dump .gen_leader* +.eunit +.gen_leader_plt # OS X .DS_Store @@ -11,6 +13,3 @@ profile # Vim *.swp *.swo - -# rebar -.eunit diff --git a/Makefile b/Makefile index 247ef48..c608f52 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,26 @@ + +DIALYZER=dialyzer +DIALYZER_OPTS=-Wunmatched_returns -Wrace_conditions -Wunderspecs -Werror_handling -Wbehaviours +PLT_FILE=.gen_leader_plt + + all: compile compile: ./rebar compile +doc: + ./rebar doc + +plt: + ./rebar build_plt + +check_plt: + ./rebar check_plt + +analyze: + $(DIALYZER) --plt $(PLT_FILE) $(DIALYZER_OPTS) --verbose -r ebin --src src/gen_leader.erl examples/skeleton.erl + tests: ./rebar eunit diff --git a/rebar.config b/rebar.config index 4df9a7e..0588e31 100644 --- a/rebar.config +++ b/rebar.config @@ -1,2 +1,3 @@ {erl_first_files, ["src/gen_leader.erl"]}. {erl_opts, [debug_info, fail_on_warning]}. +{dialyzer_opts, [{plt, ".gen_leader_plt"}]}. diff --git a/src/gen_leader.erl b/src/gen_leader.erl index 72480da..ac2d7c9 100644 --- a/src/gen_leader.erl +++ b/src/gen_leader.erl @@ -57,21 +57,6 @@ %%%
%%% @end %%% -%%% @type leader_options() = [OptArg] -%%% OptArg = {workers, Workers::[node()]} | -%%% {vardir, Dir::string()} | -%%% {bcast_type,Type::bcast_type()} | -%%% {heartbeat, Seconds::integer()} -%%% @type bcast_type() = all | sender. -%%% Notification control of candidate membership changes. `all' -%%% means that returns from the handle_DOWN/3 and elected/3 leader's events -%%% will be broadcast to all candidates. -%%% @type election() = tuple(). Opaque state of the gen_leader behaviour. -%%% @type node() = atom(). A node name. -%%% @type name() = atom(). A locally registered name. -%%% @type serverRef() = Name | {name(),node()} | {global,Name} | pid(). -%%% See gen_server. -%%% @type callerRef() = {pid(), reference()}. See gen_server. %%% -module(gen_leader). @@ -114,29 +99,69 @@ keydelete/3, keysearch/3]). + +-type option() :: {'workers', Workers::[node()]} + | {'vardir', Dir::string()} + | {'bcast_type', Type::bcast_type()} + | {'heartbeat', Seconds::integer()}. + +-type options() :: [option()]. + +%% Notification control of candidate membership changes. `all' +%% means that returns from the handle_DOWN/3 and elected/3 leader's events +%% will be broadcast to all candidates. +-type bcast_type() :: 'all' | 'sender'. + +-type status() :: 'elec1' | 'elec2' | 'wait' | 'joining' | 'worker' | + 'waiting_worker' | 'norm'. + +%% A locally registered name +-type name() :: atom(). + +%% A monitor ref +-type mon_ref() :: reference(). + +-type server_ref() :: name() | {name(),node()} | {global,name()} | pid(). + +%% Incarnation number +-type incarn() :: integer(). + +%% Logical clock +-type lclock() :: integer(). + +%% Node priority in the election +-type priority() :: integer(). + +%% Election id +-type elid() :: {priority(), incarn(), lclock()}. + +%% See gen_server. +-type caller_ref() :: {pid(), reference()}. + +%% Opaque state of the gen_leader behaviour. -record(election, { - leader = none, - previous_leader = none, - name, - leadernode = none, - candidate_nodes = [], - worker_nodes = [], - down = [], - monitored = [], - buffered = [], - seed_node = none, - status, - elid, - acks = [], - work_down = [], - cand_timer_int, - cand_timer, - pendack, - incarn, - nextel, + leader = none :: 'none' | pid(), + previous_leader = none :: 'none' | pid(), + name :: name(), + leadernode = none :: node(), + candidate_nodes = [] :: [node()], + worker_nodes = [] :: [node()], + down = [] :: [node()], + monitored = [] :: [{mon_ref(), node()}], + buffered = [] :: [caller_ref()], + seed_node = none :: 'none' | node(), + status :: status(), + elid :: elid(), + acks = [] :: [node()], + work_down = [] :: [node()], + cand_timer_int :: integer(), + cand_timer :: timer:tref(), + pendack :: node(), + incarn :: incarn(), + nextel :: integer(), %% all | one. When `all' each election event %% will be broadcast to all candidate nodes. - bcast_type + bcast_type :: bcast_type() }). -record(server, { @@ -151,7 +176,8 @@ %%% Interface functions. %%% --------------------------------------------------- -%% @hidden +-spec behaviour_info(atom()) -> 'undefined' | [{atom(), arity()}]. + behaviour_info(callbacks) -> [{init,1}, {elected,3}, @@ -168,21 +194,17 @@ behaviour_info(callbacks) -> behaviour_info(_Other) -> undefined. -%% @spec (Name::node(), CandidateNodes::[node()], -%% OptArgs::leader_options(), Mod::atom(), Arg, Options::list()) -> -%% {ok,pid()} -%% +-type start_ret() :: {'ok', pid()} | {'error', term()}. + %% @doc Starts a gen_leader process without linking to the parent. %% @see start_link/6 +-spec start(Name::atom(), CandidateNodes::[node()], OptArgs::options(), + Mod::module(), Arg::term(), Options::list()) -> start_ret(). start(Name, CandidateNodes, OptArgs, Mod, Arg, Options) when is_atom(Name), is_list(CandidateNodes), is_list(OptArgs) -> gen:start(?MODULE, nolink, {local,Name}, Mod, {CandidateNodes, OptArgs, Arg}, Options). -%% @spec (Name::node(), CandidateNodes::[node()], -%% OptArgs::leader_options(), Mod::atom(), Arg, Options::list()) -> -%% {ok,pid()} -%% %% @doc Starts a gen_leader process. %%Name | The locally registered name of the process |