Elixir Process, what is? how work? What is linked & monitored process
Published: 2024-06-13 00:22:38

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_term`or 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:

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

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:

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

# Register name for other process.
Process.register(pid, :my_friend)
```

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qcpe8gkd7l4em6p5fenr.png)

To send in code we use `send/2` function:

```Elixir
send(pid, {:get, :user_info, self()})
```

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

```Elixir
receive do
      {:user_info, data} ->
        IO.puts "user data: #{inspect data}"
      other ->
        IO.puts "other data: #{inspect other}"
end
```

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:


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

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

```Elixir
Process.flag(:trap_exit, true)
```

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

```Elixir
{:EXIT, #PID<0.192.0>, {%RuntimeError{message: "#PID<0.192.0>, raise a RuntimeError :D"}, []}}
```

**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:

```Elixir
{:DOWN, #Reference<...>, :process, #PID<...>, reason}
```

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](https://github.com/ohhi-vn/sharing_elixir_sg_elixir_dec_6)

I will go to details in other posts.

Thank for reading!