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
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.