Question

Is it possible to block a synchronous function (handle_call) to wait for a asynchronous call (handle_cast) in a different module?

i.e. How can I make the synchronous function wait (i.e. block) until a specific "done" message from a asynchronous function is received? (and only then continue with execution) Both modules are following gen_server and gen_fsm behaviors.

Can handle_info be used in some way?

Regards /Peter

Was it helpful?

Solution

The way to do this is to save away the reference to the sender in handle_call, and call gen_server:reply at a later point. That is, while you'd normally write your handle_call function like this:

handle_call(foo, _From, State) ->
    {reply, {ok, bar}, State}.

you'd do something like this:

handle_call(foo, From, State = #state{pending = Pending}) ->
    NewState = State#state{pending = [From | Pending]},
    {noreply, NewState}.

And then later, perhaps in handle_info:

handle_info({bar_event, Data}, State = #state{pending = [Head | Tail]) ->
    gen_server:reply(Head, {ok, Data}),
    NewState = State#state{pending = Tail},
    {noreply, NewState}.

That means that when a process calls gen_server:call(Pid, foo), it will block until the server process receives {bar_event, Data}, at which point the server will return the data contained in the event to the caller.

I tried to keep the example simple by storing information about the callers in a "stack", so in the case of multiple concurrent callers the last caller will return first and vice versa. In many cases you'll want to save caller info with a key of some kind, that you can look up when you get the asynchronous event.

This can also be done in a gen_fsm process; the function calls and return values are similar.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top