Irrational Exuberance!

Replicating receive-after in an Erlang gen_server

December 13, 2009. Filed under erlang

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