"Forwarding" of gen_server calls?

F. Cesarini cesarini@REDACTED
Fri Sep 17 08:35:59 CEST 1999


Hi Jim,

if you save the From argument in your handle_call function, you can
later use it in the gen_server:reply(From, Reply) function call (When S2
asynchronously replies to S1's query). The preferred solution, however
is to pass the From argument to S2 (Using an asynchronous function) so
as to make S2 call the gen_server:reply/2 function.

e.g.

in S1:

handle_call(Request, From, State) ->
         %% ... some pre-processing goes on ...
         Reply = gen_server:cast(S2, {From,Arg}),
         {noreply, NewState}

in S2:

handle_cast({From, Arg}, State) ->
        Reply = whatever(State, Arg),
        gen_server:reply(From, Reply),
        {noreply, State}.

Cheers,
Francesco

Jim Larson wrote:
> 
> I'm using using open-source Erlang (erlang_base-47.4.1) on FreeBSD
> 2.2.8.
> 
> I'm writing a server S1 using the gen_server behaviour.  One of
> the handle_call() clauses makes a "tail call" to server S2 (which
> also uses gen_server), i.e.
> 
>     handle_call(Request, From, State) ->
>         %% ... some pre-processing goes on ...
>         Reply = gen_server:call(S2, Arg),
>         {reply, Reply, NewState}
> 
> However, the call to S2 may block for a long period of time, thus
> blocking all calls to S1 in the interim.
> 
> What would be nice is a "tail call" function in the gen_server
> package that would allows you to forward the From "continuation"
> to another gen_server module, but returns to its caller immediately.
> 
>     handle_call(Request, From, State) ->
>         %% ... the same pre-processing goes on ...
>         %% This call won't block
>         case gen_server:tail_call(S2, Arg, From) of
>             ok ->
>                 {noreply, NewState}; % S2 will reply to our caller
>             {error, Reason} ->
>                 %% error looking up S2
>                 {reply, {error, SomeReason}, SomeState}
>         end.
> 
> The implementation for the simplest clause of tail_call would be
> something like:
> 
>     tail_call(Pid, Request, From) when pid(Pid), node(Pid) == node() ->
>         Pid ! {'$gen_call', From, Request},
>         ok.
> 
> [Okay, so it doesn't quite handle all error cases, since the original
> gen_server caller will only catch 'EXIT' messages from the Pid of
> the server it originally called.  I'm sure there are ways to deal
> with this.]
> 
> I realize that there are other workarounds, including:
> 
>         - returning appropriate information from S1, and
>           allowing the original client process to call S2;
> 
>         - having S1 spawn a new process to make the blocking call;
> 
>         - explicitly passing the From "continuation" as an
>           argument to a special method of S2.
> 
> However, these workarounds are unappealing.  Am I overlooking any
> other options or existing infrastructure for accomplishing what I
> want?
> 
> I await the comments of those wiser in the ways of Erlang than I.
> 
> Jim



More information about the erlang-questions mailing list