💎

Ruby 3.2 - Fiber

2022/12/24に公開

Ruby 3.2 アドベントカレンダーの24日目の記事です。

https://qiita.com/advent-calendar/2022/ruby32


Fiber

Fiber[], Fiber[]=, Fiber#storage 追加

Feature #19078: Introduce Fiber#storage for inheritable fiber-scoped variables. - Ruby master - Ruby Issue Tracking System

Fiber ストレージというのが導入された。Fiber ローカルなストレージだけど、親 Fiber から継承される。子 Fiber で設定したものは親には反映されない。

Fiber[key]=value で値を設定して Fiber[key] で値を取り出せる。ストレージ全体は Fiber#storage で参照できる。

Fiber[:hoge] = 123
p [:p1, Fiber[:hoge]]    #=> 設定された 123
f = Fiber.new do
  p [:c1, Fiber[:hoge]]  #=> 親から継承された 123
  Fiber[:hoge] = 456
  p [:c2, Fiber[:hoge]]  #=> 子で設定した 456
end
f.resume
p [:p2, Fiber[:hoge]]    #=> 親は 123 のまま

p Fiber.current.storage  #=> {:hoge=>123}
p f.storage              #=> {:hoge=>456}

実行結果:

[:p1, 123]
[:c1, 123]
[:c2, 456]
[:p2, 123]
{:hoge=>123}
{:hoge=>456}

Fiber.new(storage: {...}) で Fiber 生成時に初期状態を設定できる。

Fiber.new(storage: {hoge: 123}) do
  Fiber[:hoge]  #=> 123
end

Fiber#storage= で設定もできるが、これは experimental らしい。けど別に warning が出るわけではない。

f = Fiber.new(storage: {hoge: 123}) do
  Fiber[:hoge]  #=> 456
end
f.storage = {hoge: 456}
f.resume

Ruby は Thread#[]= でスレッドローカルに値を保持できる…と思いきや実はこれはスレッドローカルではなくて Fiber ローカルだという罠があって、真にスレッドローカルに値を保持するには Thread#thread_variable_set を使う必要があったりして、ややこしい。

3.2 からは Fiber ローカルなストレージは Fiber[] を使えばいいということになって、すこしはマシになるのかもしれない。

将来的には Thread#[] が真にスレッドローカルなストレージになったりするんだろうか。互換の問題があるからそれはないのかな…。

Discussion