The model - what if our process crashes?

We originally used spawn/1 to spawn our process, but there is a better way to do it overall. The trouble with just using spawn is that if something happens to that for any reason, there's not really a good way to track it! Let's test this out by replacing our behavior in the receive to instead just raise an exception:

process = fn ->
receive do
_ -> raise "Oh no!"
end
end

Now in IEx, if we spawn this and attempt to do anything with it by sending it a message, we'll get an error message about a raised exception inside of a process:

iex(2)> pid = spawn(process)
#PID<0.151.0>
iex(3)> send(pid, :test)
:test

18:08:16.958 [error] Process #PID<0.151.0> raised an exception
** (RuntimeError) Oh no!
(stdlib) erl_eval.erl:668: :erl_eval.do_apply/6

But the calling process itself will remain fine. What if there was something mission critical in that process and we want to make sure that it either occurs successfully or terminates up the tree of processes that called it? We can instead use spawn_link for that, which spawns a process and then links it to the caller instead. If we do this from IEx, we expect IEx to also shut down when that exception is raised:

iex(4)> pid = spawn_link(process)
#PID<0.154.0>
iex(5)> send(pid, :test)
** (EXIT from #PID<0.84.0>) evaluator process exited with reason: an exception was raised:
** (RuntimeError) Oh no!
(stdlib) erl_eval.erl:668: :erl_eval.do_apply/6

18:10:04.065 [error] Process #PID<0.154.0> raised an exception
** (RuntimeError) Oh no!
(stdlib) erl_eval.erl:668: :erl_eval.do_apply/6

Interactive Elixir (1.5.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>
Your IEx session will restart itself when this happens, you don't have to manually restart it!

That last bit is where IEx will restart itself if the process shuts down for any reason! Since we used spawn_link in our function that deliberately raised an exception, the process halted and took its parent process down with it! Ultimately, spawn_link is useful when you want to make sure that one process is aware of any sort of state changes in another process. If we wanted to instead trap the exception and exit from the block, we can call the following line before sending a message or implementing a receive block:

iex(3)> Process.flag(:trap_exit, true)
false
iex(4)> send(pid, :test)

18:23:26.023 [error] Process #PID<0.169.0> raised an exception
** (RuntimeError) Oh no!
(stdlib) erl_eval.erl:668: :erl_eval.do_apply/6
:test

Finally, we can use the helper function flush/0 to flush all of the messages out of the current process' mailbox:

iex(5)> flush()
{:EXIT, #PID<0.169.0>,
{%RuntimeError{message: "Oh no!"},
[{:erl_eval, :do_apply, 6, [file: 'erl_eval.erl', line: 668]}]}}
:ok

And there we are! We can see the exit message from our linked process that raised the exception, but this one did not kill our IEx process when it was called! flush() is a neat little function but one that is pretty specific to our IEx terminal:

iex(6)> h flush

def flush()

Flushes all messages sent to the shell and prints them out.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset