[erlang-questions] gen_server & wait/notify

Mikael Pettersson mikpelinux@REDACTED
Sun Oct 1 23:44:35 CEST 2017


See Vlad's reply a few days ago.  What you're describing is easily solvable
by:
1. having the gen_server's handle_call for this API call stash away the
>From and uuid in its internal state and return noreply as that blocks the
caller, and
2. when the response comes in (tagged with that uuid) match it to the
corresponding From and gen_server:reply to that one, which unblocks the
caller.


On Sun, Oct 1, 2017 at 7:22 PM, Chris Waymire <chris@REDACTED> wrote:

> I'll try to provide some context around what I am doing. Basically I am
> writing a client for a custom MQTT network that in addition to the basic
> MQTT functionality also provides a request/response system. So the idea
> here is that when a user of the app executes the "request" function a uuid
> will be attached to their message payload and published onto the network.
> Another endpoint that is subscribed to that topic will receive the message,
> process it, and publish a response with a reference to the original message
> id. The original sender then picks up this message and accepts it as the
> response to their request. There's more going on there but that's the gist
> of it. So which the request function is called I need the caller to block,
> but not the MQTT client itself as it will still need to be processing
> messages on the network.
>
> On Fri, Sep 29, 2017 at 12:40 AM, zxq9 <zxq9@REDACTED> 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
>> erlang-questions@REDACTED
>> http://erlang.org/mailman/listinfo/erlang-questions
>>
>
>
> _______________________________________________
> erlang-questions mailing list
> erlang-questions@REDACTED
> http://erlang.org/mailman/listinfo/erlang-questions
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20171001/06046c94/attachment.htm>


More information about the erlang-questions mailing list