Elixir Process, what is? how work? What is linked & monitored process

Mạnh Vũ - Jun 13 - - Dev Community

One of some difficult to understand from other languages go to Elixir is process (a lightweight/green thread, not OS process) and people usually ignore it to use libraries.

Actually, Elixir process is very powerful thing, If go to work with large scale system we need to do with process a lot.

I have long time work with Erlang (also Golang) and process is one of most interesting thing for me.

From my view, process in Elixir has 3 important things to care.

  1. Process is isolated code (like an island in ocean) & send/receive message is a way to communicate with outside world. If process is die, it doesn't affect to other processes (except one case is linked process).
  2. Process can be monitor to know how process is exited (:normal, :error,..).
  3. Process can be linked (group together) and all processes linked with failed process will be die (except only case is process turn trap_exit to true).

From 3 things above we can made a lot of things with process like: build a custom supervisor/worker model, simple make a pool processes to share workload, make a chain data processing,...

Now we go through one by one for understand Elixir process concept.

Isolated code in process

Work in Elixir much more easy if we understand how it works. From sharing variables (mutable variables) a lit of bit feel hard to bring algorithms (also real loop/while for Elixir). Just imagine you live alone on an island in the ocean and just to one way to communicate with outside by put message to a bottle then ocean will care anything else.
(Actually, we can share state by :ets, :persistent_termor another database like :mnesia).

To start new a process we use spawn, spawn_link or spawn_monitor function. An other way to start a process is use Supervisor I will talk about this in other post.

Create a process:

spawn(fn ->
    sum = Enum.reduce(1..100, 0, fn n, acc -> acc + n end)
    IO.puts "sum: #{inspect sum}"
  end)
Enter fullscreen mode Exit fullscreen mode

After spawn (created) process will have a PID to identify for communicate or control. We can register a name (an atom) for process.

Register a name for process:

# Register name for current process
Process.register(self(), :my_name)

# Register name for other process.
Process.register(pid, :my_friend)
Enter fullscreen mode Exit fullscreen mode

When you want to send a message you put message with address to bottle then go to beach and throw the bottle to the ocean. With a little bit of time your friend you will receive a message.
And when you want to get a message from other islands you go to the beach and see if has any bottle in the beach, check then get only message you needed (by use pattern matching), other messages stills on the beach.

For action like server/client, you send a message and stay at the beach with time (or forever) to get a feedback (send from island that received your message).

communicate between two processes

To send in code we use send/2 function:

send(pid, {:get, :user_info, self()})
Enter fullscreen mode Exit fullscreen mode

To receive message (already in mailbox or wait new one) we use receive do syntax just like case do:

receive do
      {:user_info, data} ->
        IO.puts "user data: #{inspect data}"
      other ->
        IO.puts "other data: #{inspect other}"
end
Enter fullscreen mode Exit fullscreen mode

You can put receive do to a function to go to loop again if you want to make process like a server.

In case you want to wait with time, you can use after N with N is:
0 - Just check existed messages in mailbox, doesn't wait.

0 - Wait in N miliseconds.
:infinity - Wait forever.

Example:

receive do
      {:user_info, data} ->
        IO.puts "user data: #{inspect data}"
  after 3_000 ->
     IO.puts "timeout, nothing for me :("
end
Enter fullscreen mode Exit fullscreen mode

In case message doesn't match any pattern in receive do it still stay in mailbox of process. You can get it in the future but need sure do not push a lot un matched message to process because it can make a OMM.

link processes

This is very powerful feature of Elixir. We can control a group of processes for do group tasks, chain of tasks and if any of process failed, other processes will die. We don't need take time for clean up that.

Example:
Process A --linked--> B --linked--> C

IO.puts "I'm A"

fun = fn ->
  receive do
    :shutdown ->
      IO.puts "exited"
    {:ping, from} ->
      IO.puts "got a ping from #{inspect from}"
      send(from, :pong)
    message ->
      IO.puts "Got a message: #{inspect message}"
  end
end

# spawn and link process B
spawn_link(fn ->
  IO.puts "I'm B"
  spawn_link(fn ->
    IO.puts "I'm C"

    fun.()
  end)

  fun.()
end)
Enter fullscreen mode Exit fullscreen mode

From this code we link processes in chain task. If any process failed all other processes will die also.

We also make can group like:
Process leader --linked--> worker1, worker2, ..., workerN
And any process is failed all other processes will die follow.

trap_exit

In case we don't want a process go to die follow failed process we can turn trap_exit on (set flag to true) and process will receive a failed message instead die follow other processes.

Process.flag(:trap_exit, true)
Enter fullscreen mode Exit fullscreen mode

Your process will receive a message like below when linked to your process failed.

{:EXIT, #PID<0.192.0>, {%RuntimeError{message: "#PID<0.192.0>, raise a RuntimeError :D"}, []}}
Enter fullscreen mode Exit fullscreen mode

monitor process

In other case, we just want to now why other process is crashed we can use monitor process.

Have some function for monitor process: spawn_monitor, Process.monitor for make a monitor to other process.

For remove monitor we can use Process.demonitor.

If monitored process is failed a process make monitor to that process will receive a message like:

{:DOWN, #Reference<...>, :process, #PID<...>, reason}
Enter fullscreen mode Exit fullscreen mode

From link/monitor process we can make a lot of things for our system and we can sleep well! We can make a our specific supervisor, our custom pool workers, chain task, fast fail task,...
In this time I just a explain in basic, I will explain more detail in the future.

I have LiveBook for similar this you can check a and see content + source demo on our Github repo

I will go to details in other posts.

Thank for reading!

. . . . . . . .