ヽ(´・肉・`)ノログ

How do we fighting without fighting?

すごいE本をElixirでやる(28)

第 12 章 エラーとプロセス - 12.1 リンク - それは罠だ! から

12.1 リンク

それは罠だ!

プロセスを越えたエラーの伝搬は、メッセージパッシングと同様にプロセスを通じて行われますが、 その際にはシグナルと呼ばれる特別なメッセージが使われます。 終了シグナルは「秘密の」メッセージで、プロセス上で自動的に動作してそのプロセスを殺します。

プロセスの制御も普通のメッセージパッシングの仕組みを使う. ただし普段は意識されないところでやりとりされている.

信頼性のためには、プロセスの強制終了と再起動を両方とも素早く行えなければなりません。

強制終了はリンクでできる.では,再起動はどうだろう?

再起動を扱うために,リンク上にシステムプロセスという層を用意する.

システムプロセスは基本的には普通のプロセスですが、終了シグナルを普通のメッセージに変換できる点が異なります

プロセスをシステムプロセスにするには,プロセス上で process_flag(:trap_exit, true) を呼び出す.

defmodule Linkmon do
  def myproc do
    Process.sleep(5000)
    exit(:reason)
  end

  def chain(0) do
    receive do
      _ -> :ok
    after 2000 ->
      exit("chain dies here")
    end
  end

  def chain(n) do
    pid = spawn(fn -> chain(n-1) end)
    Process.link(pid)
    receive do
      _ -> :ok
    end
  end
end

で試す.前回やった,通常のときはこんな感じだった.

Eshell V8.0.2  (abort with ^G)
Interactive Elixir (1.3.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> c("linkmon.exs")
c("linkmon.exs")
[Linkmon]
iex(2)> Process.link(spawn(Linkmon, :chain, [3]))
Process.link(spawn(Linkmon, :chain, [3]))
true
iex(3)> ** (EXIT from #PID<0.81.0>) "chain dies here"
iex(3)>

システムプロセス化すると

Eshell V8.0.2  (abort with ^G)
Interactive Elixir (1.3.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> c("linkmon.exs")
c("linkmon.exs")
[Linkmon]
iex(2)> :erlang.process_flag(:trap_exit, true)
:erlang.process_flag(:trap_exit, true)
false
iex(3)> Process.link(spawn(Linkmon, :chain, [3]))
Process.link(spawn(Linkmon, :chain, [3]))
true
iex(4)> receive do
receive do
...(4)>   x -> x
  x -> x
...(4)> end
end
{:EXIT, #PID<0.90.0>, "chain dies here"}
iex(5)>

リンクしたプロセスの死を,単なるプロセスへのメッセージとして受け取れる.

システムプロセスを使ってプログラムを書くことで、何かが死んでいないか確認して死んだ場合には再起動させる、という役割だけを持つプロセスを簡単に作れます。

なるほどー.便利.