fork 対応
Unicorn や Puma などの fork() でマルチプロセスするサーバーに対応するために必要
fork すると何が起こる?
新しいプロセスが生成される
子プロセスの PID は新しくなる
pthread ID (pthread_self() で得られるやつ) も新しくなる
カーネルスレッドのID (gettid() で得られるやつ) に関する保証はない?
子プロセスには fork() を呼んだスレッドのみが引き継がれる
他のスレッドは子には行かない(シングルスレッドな状態で起動する)
timer_create() で作ったタイマーは fork したときに引き継がれない
man 2 timer_create より
Timers are not inherited by the child of a fork(2), and are disarmed and deleted during an execve(2).
sigaction() で作成されたシグナルハンドラは引き継がれる
man 2 sigaction より
A child created via fork(2) inherits a copy of its parent's signal dispositions.
Ruby 的には何が起こる?
code:ruby
th = Thread.new { loop { sleep 1 } }
if child_pid = fork
# parent
sleep 1
p th
p "parent: th.native_thread_id=#{th.native_thread_id}"
else
# child
sleep 1
p th
p "child: th.native_thread_id=#{th.native_thread_id}"
end
code:result
% ruby fork.rb
#<Thread:0x000075e0a15800c8 fork.rb:1 sleep>
"parent: th.native_thread_id=7306"
#<Thread:0x000075e0a15800c8 fork.rb:1 dead>
"child: th.native_thread_id="
Pf2 の fork 対応
基本的にはプロセス単位で Scheduler を起動してください、で良い気がする
IPC して結果を親プロセスに集める部分の面倒を見るのはやりすぎそう
でもユーザーの認知が "プロセス全体の状態を watch してもらっている" 場合は面倒見てあげたほうが良い気がする
ruby -rpf2/watch で起動しているとき
複数の .pf2profile をマージする機能は別途必要そう
fork 後に TimerInstaller を改めて呼ぶだけで良さそう?
Ruby 3.1+ feature https://bugs.ruby-lang.org/issues/5446
pthread_atfork() で hook を登録しとけばいい説もある
この hook 内から thread hooks を呼んでいいかは不明
Puma (cluster mode) で以下の Rack middleware で何も取れないのはなぜ?
→ 取れないというのが気のせいだった(単に controller が速すぎて sampling interval に達してなかった)
https://github.com/puma/puma/blob/v6.4.2/docs/architecture.md
code:middleware.rb
def call(env)
::Pf2.start()
resp = @app.call(env)
profile = ::Pf2.stop()
resp
end