When I call gen_server:reply/2
:
gen_server:reply(From, Msg),
the client, From
, receives a message with the format:
{Ref, Msg)
I can't find any documentation for the message format sent by gen_server:reply/2
, and I'm wondering how I can pattern match the Ref
in the message. Currently, I use a don't care variable for the Ref
:
receive
{_Ref, Msg} -> Msg;
Other -> Other
end
which means that a process other than the gen_server
could potentially send my client a message that would match the {_Ref, Msg}
clause.
In the call gen_server:reply(From, Msg)
, From
is not simply the client: it is in fact a tuple containing two values, the process id of the caller and a unique reference. We can see this in the implementation of gen_server:reply/2
:
%% -----------------------------------------------------------------
%% Send a reply to the client.
%% -----------------------------------------------------------------
reply({To, Tag}, Reply) ->
catch To ! {Tag, Reply}.
The idea is that Tag
is a unique value provided by the caller, so that the caller can distinguish the result from this call from any other incoming message:
Ref = make_ref(),
MyServer ! {'$gen_call', {self(), Ref}, foo},
receive
{Ref, Reply} -> io:format("Result of foo call: ~p~n", [Reply])
end
In the code above, the receive
will block until it gets a response to this very call.
(gen_server:call/2
does something like the above, and additionally monitors the server in case it crashes, and checks for timeouts.)
The reason this is undocumented is that it is considered an internal implementation detail subject to change, and users are advised to rely on gen_server:call
and gen_server:reply
instead of generating and matching the messages themselves.
Most of the time you wouldn't need to use gen_server:reply/2
at all: the server process receives a call and handles it synchronously, returning a reply
tuple:
handle_call(foo, _From, State) ->
%% ignoring 'From' here, because we're replying immediately
{reply, foo_result, State}.
But sometimes you'd want the server process to delay replying to the call, for example waiting for network input:
handle_call(foo, From, State) ->
send_request(foo),
NewState = State#state{pending_request = From},
{noreply, NewState}.
handle_info({received_response, Response}, State = #state{pending_request = From}) ->
gen_server:reply(From, Response),
NewState = State#state{pending_request = undefined},
{noreply, NewState}.
In the example above, we save the From
value in the server state, and when the response comes in as an Erlang message, we forward it to the caller, which will block until it gets the response. (A more realistic example would handle multiple requests concurrently and match incoming responses to outstanding requests somehow.)