Processes
Recreate a simple GenServer implementation
Why?
- Everyone was talking about it
- I didn't get it correctly
- It seemed like the building block of OTP
Because:
Who?
- Don't understand Elixir Processes
- Don't understand GenServer behaviour
People who
Processes
- Lightweight thread of Erlang VM (1 or 2 KB)
- Run in concurrence of other Processes
- Use all the available CPU resources
- Isolated from each other
- crash wise
- data wise
- Can have an internal state
- Can receive messages only
DISCLAIMER: From now on, we won't talk about OS but Erlang processes
Processes
spawn(fn ->
Process.sleep(2000)
IO.puts(2 * 2)
end)
# Execution:
# ...wait 2 seconds...
# 4
# PID<0.84.0>
Cool, but useless. Would be awesome to take some parameters
Starting from scratch
Processes
background_job = fn(value) ->
spawn(fn ->
Process.sleep(2_000)
IO.puts(value * 2)
end)
end
# Execution:
# > background_job.(4)
# ... Wait 2 seconds...
# 8
# PID<0.87.0>
Better. We can trigger tasks in background.
What about communication?
Starting from scratch
Processes
background_job = fn(value, caller) ->
spawn(fn ->
Process.sleep(2_000)
send(caller, {:done, value * 2})
end)
end
# Execution:
# > background_job.(4, self())
# PID<0.87.0>
# > receive do
# > {:done, result} -> IO.puts("Result is #{result}")
# > end
# Result is 8
# :ok
Yay! we can now send and receive messages but what about not dying after each call?
Exchanging messages
Processes
defmodule Multiplicator do
def start(initial_value) do
spawn(fn -> loop(initial_value) end)
end
defp loop(result) do
receive do
{multiplicator, caller} ->
result = result * multiplicator
send(caller, {:done, result})
loop(result)
end
end
end
# Execution:
# > pid = Multiplicator.start(1)
# PID<0.87.0>
# > send(pid, {2, self()})
# > receive do
# > {:done, result} -> IO.puts("Result is #{result}")
# > end
# Result is 2
# > send(pid, {21, self()})
# > receive ....
# Result is 42
Now, we are ready!
Let's move to an editor
https://github.com/kdisneur/processes
Step 2
What we've seen previously in theory but now with a real use case
Pros
Cons
- It works
- Concurrent
- Elixir "by the book"
- Verbose
- Mix Interface with Implementation details
git checkout 9f788ca
Step 3
We partially fixed the cons from Step 2
Pros
Cons
- It works
- Concurrent
- Elixir "by the book"
- Clean separation between interface and implementation
- Verbose
git checkout 0e32b1f
Step 5
Generic
Specific
- Spawn a new process
- Return a response tuple
- For asynchronous calls, send the message and return an ok atom
- For synchronous calls, send the message, wait for a response and return a value
- Internal state
- Messages
git checkout a1d6436
Step 6
Well done! We just (re)created a basic GenServer
In a perfect world, we could now add new features more easily (new servers, or news messages)
git checkout 78e87e7
RECOMMENDATION
Processes
By Kevin Disneur
Processes
- 1,520