erlangerlang-otperlang-shellerlang-supervisor

Is there a way to modify when the kill message is sent to any process all others has to die?


Thanks for taking a look at my question.

I wrote a code for the question ring problem from the o'reilly francesco cesarini and simpson thompson, Exercise 4-2: The Process Ring.

Now here's my question,How can I modify the code so that when message kill is sent to any process, all the other process should die automatically without the message getting propagated through the ring.

Even If you can't solve it, just thanks for at least looking at it. This is for my own curiosity on what would happen if this is how it goes and thanks again I'll share the code and the original question so that you guys can understand.

Original question : Write a program that will create N processes connected in a ring, as shown in Figure 4-17. Once started, these processes will send M number of messages around thering and then terminate gracefully when they receive a quit message. You can start the ring with the call ring:start(M, N, Message).

This is the reference

The Code :

-module(r).
-export([start/3,processes/1]).

start(M, N, Message) ->
  io:format("Central Process pid: ~w\n", [self()]),
  { ok, NextPid } = spawner(N),
  ok = sender(Message, M, NextPid),
  ok = sender(stop, 1, NextPid),
  ok.

spawner(N) ->
  spawner(N, self()).
      
spawner(1, Nring) ->
  { ok, Nring };
            
spawner(N, Nring) ->
  Pid = spawn(r, processes, [Nring]),
  spawner(N - 1, Pid).
  
processes(Nring) ->
  loop(Nring).

loop(Nring) ->
  receive
    { stop, From } ->
      io:format("~w received stop from ~w\n", [self(), From]),
      Nring ! { stop, self() },
      ok;
    { Msg, From } ->
      io:format("~w received ~w from ~w\n", [self(), Msg, From]),
      Nring ! { Msg, self() },
      loop(Nring)
  end.
  
sender(Msg, Times, NextPid) ->
  NextPid ! { Msg, self() },
  ok = receiver(Msg),
  case Times > 1 of
    true ->
      sender(Msg, Times - 1, NextPid);
    false ->
      ok
  end.
  
receiver(Msg) ->
  receive
      { Msg, From } ->
      io:format("~w received ~w from ~w\n", [self(), Msg, From]),
      ok
  end.

Pardon me If there's any spelling mistake and I would really appreciate it. Thank you so much.


Solution

  • As was suggested by @BrujoBenavides in a comment, you can link all processes in the ring. If one of the linked processes terminates, Erlang VM will automatically terminate all other linked processes. See the reference manual and a chapter from "Learn You Some Erlang for great good" for more details.

    In your example change spawner/2 to use spawn_link/3 instead of spawn/3:

    spawner(N, Nring) ->
        Pid = spawn_link(r, processes, [Nring]),
        spawner(N - 1, Pid).
    

    This way loop/1 does not need to propagate { stop, Pid } anymore:

    loop(Nring) ->
        receive
            { stop, From } ->
                io:format("~w received stop from ~w\n", [self(), From]),
                %% All linked processes will be terminated
                %% once we break out the loop/1
                ok;
    

    Note that in your example the Central Process spawning other processes is also part of the ring. This means that a) it will also be terminated whenever one of the other ring processes dies and b) the whole process ring will be terminated when the Central Process dies.

    You can avoid this by not linking self():

    spawner(N, Nring) ->
        if Nring == self() ->
            Pid = spawn(r, processes, [Nring]);
        true ->
            Pid = spawn_link(r, processes, [Nring])
        end,
        spawner(N - 1, Pid).