sb logoToday I Learned

1 post by davidlucia

How GenServer.call/2 works with a process alias

A coworker and I were discussing the mechanics of GenServer.reply/2, which led to a conversation about what is passed as the second argument to a GenServer.handle_call/3 callback.

def handle_call(:action, from, state)

What is in from? Well, typically, it is a tuple of the calling process’s pid, and a unique ref for the call. {pid(), ref()}. You can see this in action inside of the implementation of OTP

Mref = erlang:monitor(process, Process),
Process ! {Label, {self(), Mref}, Request},
receive
     {Mref, Reply} ->
            ...

The process creates the ref, makes the call, then blocks with a receive until the callee responds or it times out. The ref is essential so that it knows it’s receiving a reply to the message, instead of any arbitrary message.

If you call the GenServer not by a pid, but by using an atom alias, the call is a little different. Instead of just matching on the pid and ref, it also throws in an :alias atom with the ref, so that it can use slightly different dispatch logic.

Tag = [alias | Mref],

erlang:send(Process, {Label, {self(), Tag}, Request}, [noconnect]),

receive
    {[alias | Mref], Reply} ->
        erlang:demonitor(Mref, [flush]),
        {ok, Reply};

Cool!