goncalotomas
goncalotomas

Reputation: 1000

Why can you create multiple monitor references to the same process in Erlang?

Here is an example trace where I'm able to call erlang:monitor/2 on the same Pid:

1> Loop = fun F() -> F() end.
#Fun<erl_eval.30.99386804>
2> Pid = spawn(Loop).
<0.71.0>
3> erlang:monitor(process, Pid).
#Ref<0.2485499597.1470627842.126937>
4> erlang:monitor(process, Pid).
#Ref<0.2485499597.1470627842.126942>
5> erlang:monitor(process, Pid).
#Ref<0.2485499597.1470627842.126947>

The expressions returned by instruction #4 and #5 are different than #3, meaning that it is possible to create multiple monitor references between the current process and Pid. Is there a practical case where you would need or use multiple monitor references to the same process?

I would expect this to return the same reference (returning a new one would perhaps imply that the old one had failed/crashed), following the same logic that exists for link/1.

Upvotes: 3

Views: 220

Answers (1)

Hynek -Pichi- Vychodil
Hynek -Pichi- Vychodil

Reputation: 26131

Imagine you use third party library which does this (basically what OTP *:call/* functions does):

call(Pid, Request) ->
    call(Pid, Request, ?DEFAULT_TIMEOUT).

call(Pid, Request, Timeout) ->
    MRef = erlang:monitor(process, Pid),
    Pid ! {call, self(), MRef, Request},
    receive
      {answer, MRef, Result} ->
        erlang:demonitor(Mref, [flush]),
        {ok, Result};
      {'DOWN', MRef, _, _, Info} ->
        {error, Info}
    after Timeout ->
        erlang:demonitor(MRef, [flush]),
        {error, timeout}
    end.

and then you use it in your code where you would monitor the same process Pid and then call function call/2,3.

my_fun1(Service) ->
    MRef = erlang:monitor(process, Service),
    ok = check_if_service_runs(MRef),
    my_fun2(Service),
    mind_my_stuf(),
    ok = check_if_service_runs(MRef),
    erlang:demonitor(MRef, [flush]),
    return_some_result().

check_if_service_runs(MRef) ->
    receive
      {'DOWN', MRef, _, _, Info} -> {down, Info}
    after 0 -> ok
    end.

my_fun2(S) -> my_fun3(S).

% and a many layers of other stuff and modules
my_fun3(S) -> call(S, hello).

What a nasty surprise it would be if erlang:monitor/2,3 would always return the same reference and if erlang:demonitor/1,2 would remove your previous monitor. It would be a source of ugly and unsolvable bugs. You should start to think that there are libraries, other processes, your code is part of a huge system and Erlang was made by experienced people who thought it through. Maintainability is key here.

Upvotes: 5

Related Questions