Replicating receive-after in an Erlang gen_server
Using message passing in Erlang relies on the receive
construct, which applies user specified patterns against
a process' mailbox and performs the matching instructions. receive
is supplemented with the after
construct, which executes some code if no messages are received for a specified period of time.
A simple example looks like:
store(Store) ->
receive
add ->
io:format("started!"),
store(Store + 1);
stop ->
Store
after
1000 ->
io:format("Store is ~p",[Store]),
store(Store)
end.
However, it's pretty uncommon to use message passing directly in production Erlang.
It is much more common to use a gen_server instead. In the process of
porting some code from a process to a gen_server I became stuck because I couldn't
figure out how to replicate the after
construct in a gen_server.
Largely by accident, a week later I discovered the solution: returning a 3-tuple instead of a 2-tuple
from init/1
in the gen_server.
% 0 is the initial State value
% 1000 is the timeout value
init([]) -> {ok, 0, 1000}.
handle_cast(add, _Sender, State) ->
{noreply, State+1, 1000};
handle_cast(stop, _Sender, State) ->
{stop, stopped, stopped, State}.
handle_info(timeout, State) ->
io:format("Store is ~p", [State]),
{noreply, State}.
Simpler than anticipated.
(My first implementation here was a bit naive. It isn't sufficient to return a timeout only with init/1
, but instead must do so with every single call and cast.)