Closing Channels in Erlang

In this example, we’ll use a jobs process to communicate work to be done from the main process to a worker process. When we have no more jobs for the worker, we’ll send a special message to indicate completion.

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

main() ->
    Jobs = spawn(fun() -> jobs_process([]) end),
    Done = spawn(fun() -> done_process() end),

    % This is the worker process. It repeatedly receives
    % messages from Jobs. When it receives the 'close' atom,
    % it knows that all jobs have been processed.
    Worker = spawn(fun() -> worker_process(Jobs, Done) end),

    % This sends 3 jobs to the worker via the Jobs process
    [Jobs ! {job, J} || J <- lists:seq(1, 3)],
    io:format("sent all jobs~n"),
    Jobs ! close,

    % We await the worker using a synchronization approach
    receive
        done -> ok
    end,

    % Checking if the Jobs process is still alive
    case is_process_alive(Jobs) of
        true -> io:format("Jobs process is still alive~n");
        false -> io:format("Jobs process has terminated~n")
    end.

jobs_process(Queue) ->
    receive
        {job, J} ->
            jobs_process([J | Queue]);
        {get_job, Pid} when Queue /= [] ->
            [Job | Rest] = lists:reverse(Queue),
            Pid ! {job, Job},
            jobs_process(Rest);
        {get_job, Pid} when Queue == [] ->
            Pid ! no_more_jobs,
            jobs_process([]);
        close when Queue == [] ->
            exit(normal);
        close ->
            jobs_process(Queue)
    end.

worker_process(Jobs, Done) ->
    Jobs ! {get_job, self()},
    receive
        {job, J} ->
            io:format("received job ~p~n", [J]),
            worker_process(Jobs, Done);
        no_more_jobs ->
            io:format("received all jobs~n"),
            Done ! done
    end.

done_process() ->
    receive
        done -> ok
    end.

To run the program, save it as closing_channels.erl and use the Erlang shell:

$ erl
1> c(closing_channels).
{ok,closing_channels}
2> closing_channels:main().
sent job 1
received job 1
sent job 2
received job 2
sent job 3
received job 3
sent all jobs
received all jobs
Jobs process has terminated
ok

In this Erlang version, we use processes and message passing to mimic the behavior of channels in Go. The jobs_process acts as a queue for jobs, the worker_process receives and processes jobs, and the done_process is used for synchronization.

The concept of closing a channel is simulated by sending a close message to the jobs_process. When the jobs_process receives this message and its queue is empty, it terminates.

The worker process keeps requesting jobs until it receives a no_more_jobs message, at which point it signals completion to the done_process.

Finally, we check if the Jobs process is still alive, which is analogous to checking if a channel is closed in Go.

This example demonstrates how Erlang’s processes and message passing can be used to implement patterns similar to Go’s goroutines and channels.