[erlang-questions] gen_server & wait/notify

Dmitry Kolesnikov <>
Fri Sep 29 10:37:23 CEST 2017


+1 to Craig

The usage of blocking receive within gen_server is possible but then the value of async call is questionable in your design. You either make it fully async with state-machine like approach or make it sync.

Best Regards,
Dmitry 


> On 29 Sep 2017, at 10.40, zxq9 <> wrote:
> 
> On 2017年09月29日 金曜日 16:23:42 zxq9 wrote:
>> On 2017年09月28日 木曜日 17:58:08 Chris Waymire wrote:
>>> handle_call({request, Data#data{uid=UID}}, _From, State) ->
>>>    make_asycnc_req(Data),
>>>    receive
>>>        {UID, Response} -> {reply, Response, State}
>>>    end.
>>> 
>>> handle_info({response, Response#response{uid=UID}}, State) ->
>>>    self() ! {UID, Response},
>>>    {noreply, State}.
>> 
>> 
>> Your naked `receive` will work just fine. The flow of the process has never
>> returned to the gen_server module yet to await another message to dispatch,
>> so when you write a naked `receive` in some handling code that is exactly
>> where the process will block, and you can receive any arbitrary thing you
>> want there. Just be careful not to match on any system or gen_server message
>> types and you'll get the behavior you expect (though your mailbox may be
>> filling up with other stuff in the meantime).
> 
> One note of caution is that you might actually be fighting against the
> natural order of things. Consider carefully whether you need this and what
> it is achieving for you. You may really be better off with casts. See if
> there is a way for you to design the system to be entirely async, and if
> not, why not. What state is being async going to threaten? Is it an
> ordering issue? Are you relaying a message and waiting for a response
> that will be returned? If so, why is the originating process not just
> receiving the response directly? (Do you really need a middle-man?)
> 
> Another way to achieve this without blocking is to use references to
> tag messages and keep a digest of them. That prevents your gen_server
> from becoming unresponsive to system messages or blocking indefinitely
> in the event the sender of the message you are waiting on crashes.
> 
> This is actually what the `From` argument to handle_call/3 is for:
> 
> handle_call({request, Data = data{uid = UID}},
>            {Sender, Tag},
>            State = #s{queue = Q}) ->
>    ok = make_async_request(Data, Tag),
>    {noreply, State#s{queue = [{{Tag, UID, Sender} | Queue]}};
> % ...
> 
> handle_cast({response, {Tag, UID}, Message}, State = #s{queue = Queue}) ->
>    case lists:keyfind(Tag, 1, Queue) of
>        Ticket = {Tag, UID, Sender} ->
>            NewState = State#s{queue = lists:delete(Ticket, Queue)},
>            {reply, Message, NewState};
>        false ->
>            LogString = "Received bad message: ~tp",
>            ok = log(warning, LogString, [{{Tag, UID}, Message}]),
>            {noreply, State}
>    end;
> % ...
> 
> There may be any number of ways you might want to phrase that or structure
> it, but basically that's how you can safely stow such a pending response
> value and sender, and get the response back out to them without any weird
> blockages or unresponsiveness.
> 
> -Craig
> _______________________________________________
> erlang-questions mailing list
>  <mailto:>
> http://erlang.org/mailman/listinfo/erlang-questions <http://erlang.org/mailman/listinfo/erlang-questions>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20170929/271b3826/attachment.html>


More information about the erlang-questions mailing list