Skip to content

Instantly share code, notes, and snippets.

@hfeeki
Last active January 3, 2016 07:49
Show Gist options
  • Save hfeeki/8432017 to your computer and use it in GitHub Desktop.
Save hfeeki/8432017 to your computer and use it in GitHub Desktop.
Process ring.
%% from https://erlangcentral.org/wiki/index.php/Process_Ring_Across_Nodes
%%
%% Written by Mazen Harake
%% GPLv3 License -> http://www.gnu.org/licenses/gpl-3.0.txt
-module(nodeloop).
-compile(export_all).
start(N) when N > 0 ->
erlang:register(head, proc_lib:spawn_link(?MODULE, head_proc, [nil, nil])),
create_links(N).
create_links(0) ->
ok;
create_links(N) when N > 0 ->
head ! {cmd, spawn_new},
create_links(N - 1).
stop() ->
head ! {cmd, stop}.
send(Msg, NodeSkips) when NodeSkips > 0 ->
head ! {msg, node(), NodeSkips, 0, 1, Msg}.
head_proc(nil, Next) ->
receive
{msg, Origin, MaxNodeSkips, NodeSkips, ProcSkips, Msg} ->
case {node(), MaxNodeSkips == NodeSkips} of
{Origin, true} ->
io:format("head: Finished!~n");
{Origin, false} ->
Next ! {msg, Origin, MaxNodeSkips, NodeSkips+1, ProcSkips, Msg};
{_, _} ->
Next ! {msg, Origin, MaxNodeSkips, NodeSkips, ProcSkips, Msg}
end,
?MODULE:head_proc(nil,Next);
{cmd, stop} ->
Next ! {cmd, stop},
io:format("head: Stop sent, Exiting...!~n");
{cmd, spawn_new} ->
Pid = proc_lib:spawn_link(?MODULE, link_proc, [nil, nil]),
Pid ! {cmd, linkin, [head, Next]},
io:format("head: Added ~p...~n",[Pid]),
?MODULE:head_proc(nil, Pid)
end.
link_proc(Prev, Next) ->
receive
{cmd, linkin, [NewPrev, NewNext]} ->
?MODULE:link_proc(NewPrev, NewNext);
{msg, Origin, MaxNodeSkips, NodeSkips, ProcSkips, Msg} ->
io:format("~p: ~p | ~p | ~p | ~p | ~1000p~n",
[self(), Origin, MaxNodeSkips, NodeSkips, ProcSkips, Msg]),
timer:sleep(50),
Receiver = next(Next),
Receiver ! {msg, Origin, MaxNodeSkips, NodeSkips, ProcSkips + 1, Msg},
?MODULE:link_proc(Prev, Next);
{cmd, stop} ->
io:format("~p: Exiting...~n",[self()]),
next(Next) ! {cmd, stop}
end.
next(nil) ->
Nodes = lists:sort([node()|nodes()]),
{head, nextnode(hd(Nodes), node(), Nodes)};
next(Pid) -> Pid.
nextnode(First, _Me, []) -> First;
nextnode(_First, Me, [Me, Next | _Rest]) -> Next;
nextnode(First, Me, [_|Rest]) -> nextnode(First, Me, Rest).
%%
%% from: http://stackoverflow.com/questions/16438367/erlang-ring-controller-process-related
%%
%% The first one is: The control spawns all the workers in the ring and here is the solution:
%%
-module(ring).
-export([start/3, create/4]).
start(M, N, Message) ->
create(undef, N, M, Message).
create(Parent, 0, M, Message) ->
Parent ! {created, self()},
evaluate(Parent, M, Message);
create(Parent, N, M, Message) ->
Child = spawn(?MODULE, create, [self(), N-1, M, Message]),
io:format("~w ~w created~n", [Child, N]),
evaluate(Parent, M, Message).
evaluate(undef, M, Message) ->
receive
{created, Last} ->
Last ! Message,
io:format("~w sent ~w to ~w~n", [self(), Message, Last]),
evaluate(Last, M-1, Message)
end;
evaluate(Parent, 0, _) ->
receive
Msg ->
io:format("~w received ~w~n", [self(), Msg]),
Parent ! stop,
io:format("~w sent ~w to ~w~n", [self(), stop, Parent])
end;
evaluate(Parent, M, Message) ->
receive
{created, Last} ->
Parent ! {created, Last},
evaluate(Parent, M, Message);
Message ->
io:format("~w received ~w~n", [self(), Message]),
Parent ! Message,
io:format("~w sent ~w to ~w~n", [self(), Message, Parent]),
evaluate(Parent, M-1, Message)
end.
%%
%% from: http://stackoverflow.com/questions/16438367/erlang-ring-controller-process-related
%%
%%
%% And the second one is:control spawns only the first worker in the ring. Every new worker in the ring, but the last one, spawns the next worker:
%%
-module(ring).
-export([start/3, start_process/1, start_process/2]).
start(M, N, Message) ->
Pid = spawn(ring, start_process, [N]),
Pid ! {message, Message, M},
ok.
start_process(Count) ->
% This is the first spawned process - send its
% pid down the chain so the last process knows who its
% next pid is.
io:format("~p: Spawned ~p~n", [self(), Count]),
Pid = spawn(ring, start_process, [Count-1, self()]),
loop(Pid).
start_process(0, Last) ->
% This is the last process
io:format("~p: Linking to last ~p~n", [self(), Last]),
loop(Last);
start_process(Count, Last) ->
io:format("~p: Spawned ~p~n", [self(), Count]),
Pid = spawn(ring, start_process, [Count-1, Last]),
loop(Pid).
loop(NextPid) ->
receive
{message, _, 0} -> true;
{message, Msg, M} ->
io:format("~p (~p) ~p~n", [Msg, self(), M]),
NextPid ! {message, Msg, M-1},
loop(NextPid)
end.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment