Timeouts in Erlang

Timeouts are important for programs that connect to external resources or that otherwise need to bound execution time. Implementing timeouts in Erlang is straightforward using the timer module and message passing.

-module(timeouts).
-export([main/0]).

main() ->
    % For our example, suppose we're executing an external
    % call that returns its result after 2s. We'll use
    % a separate process to simulate this delay.
    Pid1 = spawn(fun() ->
        timer:sleep(2000),
        self() ! {result, "result 1"}
    end),

    % Here we implement a timeout using the receive..after construct.
    % We'll wait for a message from Pid1 for 1 second.
    Result1 = receive
        {result, Res1} ->
            io:format("~s~n", [Res1])
    after 1000 ->
        io:format("timeout 1~n")
    end,

    % If we allow a longer timeout of 3s, then the receive
    % from Pid2 will succeed and we'll print the result.
    Pid2 = spawn(fun() ->
        timer:sleep(2000),
        self() ! {result, "result 2"}
    end),

    Result2 = receive
        {result, Res2} ->
            io:format("~s~n", [Res2])
    after 3000 ->
        io:format("timeout 2~n")
    end.

Running this program shows the first operation timing out and the second succeeding.

$ erl -noshell -s timeouts main -s init stop
timeout 1
result 2

In this Erlang version:

  1. We use separate processes (created with spawn) to simulate the delayed operations instead of goroutines.

  2. Instead of channels, we use Erlang’s built-in message passing mechanism.

  3. The select statement with timeout is replaced by Erlang’s receive..after construct, which allows us to specify a timeout directly.

  4. We use timer:sleep/1 to introduce delays, similar to time.Sleep in the original example.

  5. The io:format/2 function is used for output, replacing fmt.Println.

This example demonstrates how to implement timeouts in Erlang, which is a crucial feature for programs that need to bound execution time or interact with external resources.