Concurrent Erlang

I’m sure you’ve met processes before, but only in the context of operating systems. In Erlang, processes belong to the programming language and not the operating system. This means that Erlang processes will have the same logical behavior on any operating system, so we can write portable concurrent code that can run on any operating system that supports Erlang.

In Erlang:

(Armstrong 2013, 181)

Processes

In Erlang, creation of a parallel process is achieved by evaluating the spawn primitive. This primitive creates a concurrent process and returns a process identifier (PID) that can used to interact with the newly created process:

Pid = spawn(ModName, FuncName, [Arg1, Arg2, ..., ArgN]).
erlang_spawn.png
  • This BIF never fails
  • A process can either terminate:
    • Abormally when run-time errors occur
    • Normally when there is no more code to execute

Modeling Concurrency

erlang_send.png

The syntax Pid ! Msg means "send the message Msg to the process Pid", where Msg is from any valid Erlang data type. Also, Pid1 ! Pid2 ! ... ! Msg means send the message Msg to all the processes Pid1, Pid2, and so on. The receive ... end construct is used for a process to read a message that has been sent:

receive
   Pattern1 [when Guard1] -> Expression1;
   Pattern2 [when Guard2] -> Expression2;
   ...
end
  • Sending a message will never fail.
  • A message sent to non-existing processes are throw away.
  • Received messages are store in a process' mailbox.
  • Mailboxes are scanned sequentially.
  • If a message fails to be pattern matched, it's saved in a queue for later processing and the process waits for the next message. It is possible to update a process with a new version of the code that retrieves those messages.

    The erlang shell (erl) is itself a process, you can test its message-passing functionalities by using the self keyword:

        1> self() ! hello.
        hello
        2> receive X -> X end.
        hello
    
    • Messages can be matched and selectivelly retrieved.
    • We can force an order (emulating a priority queue) by using multiple receives:

      erlang_selective.png
            receive
              {foo, _} ->
              (...)
            end,
            (...)
            receive
              {bar, _} ->
              (...)
            end,
      

      this way the message {foo, ...} is received before the message {bar, ...}.

Registered Processes

BIF Description
register(Name, Pid) Associates the name Name, an atom, with the process Pid.
registered/0 Returns a list of names that have been registered using register/2.
whereis(Name) Returns the pid registered under Name, or undefined if the name is not registered.
registered() Return a list of all registered processes in the system.
  • Sending messages to a non-existing registered process causes the calling process to terminate with a badarg error.

Timeouts

  • If the message Msg is received within the TimeOut, expr01 will be executed. Otherwise, expr02 will be executed.
  • TimeOut is an integer denoting the time in miliseconds or the Atom infinity.
erlang_timeout.png
  receive 
    Msg ->
      <expr01>
    after TimeOut ->
      <expr02>
  end

Skeleton of an Erlang Process

erlang_process_skeleton.png

References:

Armstrong, Joe. 2013. “Programming Erlang: Software for a Concurrent World.”

Backlinks: