%% @author Jacob Torrey '' -module(paxos). -export([start/0, request/0, add_acceptor/0, add_learner/0, stats/0, add_acceptor/1]). request() -> proposer ! {self(), get_value}, ok. stats() -> io:format("Acceptors: ~p~nLearners: ~p~nCurrent Value: ~p with ~p-ary agreement~n", [length(pg:members(acceptors)), length(pg:members(learners)), get_value(), get_count()]). add_acceptor() -> add_acceptor(0). add_acceptor(Num) -> pg:join(acceptors, start_acceptor(Num)). add_learner() -> pg:join(learners, start_learner()). start() -> ok = pg:create(acceptors), ok = pg:create(learners), register(proposer, start_proposer()), ok. get_count() -> pg:send(learners, {self(), get_count}), receive {count, C} -> C end. get_value() -> pg:send(learners, {self(), get_value}), receive {value, V} -> V end. start_acceptor(Num) -> spawn(fun() -> acceptor_loop(Num) end). start_proposer() -> spawn(fun() -> proposer_loop(1) end). start_learner() -> spawn(fun() -> learner_loop({0, 0}) end). proposer_loop(Num) -> receive {Pid, get_value} -> io:format("Proposing ~p~n", [Num]), pg:send(acceptors, {self(), 'accept?', Num}), case count_mbox({'accept!', Num}) > length(pg:members(acceptors)) div 2 of true -> pg:send(acceptors, {self(), 'accepted', Num}), proposer_loop(Num + 1); false -> self() ! {Pid, get_value}, proposer_loop(Num + 1) end end. count_mbox({Msg, Count}) -> receive {Msg} -> count_mbox({Msg, Count + 1}) after 0 -> Count end; count_mbox(Msg) -> count_mbox({Msg, 0}). count_lmbox({N, Count}) -> receive {pg_message, _, learners, {'accepted', N}} -> count_lmbox({N, Count + 1}) after 0 -> Count end; count_lmbox(Msg) -> count_lmbox({Msg, 1}). learner_loop({Num, Count}) -> receive {pg_message, _, learners, {'accepted', N}} -> case N > Num of true -> C = count_lmbox(N), io:format("Got ~p req for ~p~n", [C, N]), case C > (length(pg:members(acceptors)) div 2) of true -> io:format("Learned ~p~n", [N]), learner_loop({N, C}); false -> io:format("Didn't learn ~p~n", [N]), learner_loop({Num, Count}) end; false -> case N =:= Num of true -> learner_loop({Num, Count + 1}); false -> learner_loop({Num, Count}) end end; {pg_message, Pid, learners, {Pid, get_value}} -> Pid ! {value, Num}, learner_loop({Num, Count}); {pg_message, Pid, learners, {Pid, get_count}} -> Pid ! {count, Count}, learner_loop({Num, Count}) end. acceptor_loop(Num) -> receive {pg_message, Pid, acceptors, {Pid, 'accept?', N}} -> case N > Num of true -> Pid ! {'accept!', N}, acceptor_loop(Num); false -> acceptor_loop(Num) end; {pg_message, _Pid, acceptors, {_Pid, 'accepted', N}} -> case N > Num of true -> io:format("Accepted ~p~n", [N]), pg:send(learners, {'accepted', N}), acceptor_loop(N); false -> acceptor_loop(Num) end end.