Perlでマルチプロセスデーモンを作るためのモジュール「Parallel::Prefork」に(Min|Max)SpareServers対応を追加した話 (もしくは read(2) / write(2) の atomicity)

Perl で複数のワーカープロセスを制御するためのモジュールとしては Parallel::ForkManager が古参なんだけど、このモジュールはプロセスを fork するだけで、シグナルを受信したらワーカープロセスを再起動とかそういうことができないので、自分は Parallel::Prefork というモジュールを自作して、たとえば Plack の Server::Standalone::Prefork とかで使っています。

で、まあ、prefork なサーバとか書いてると、(Min|Max)SpareServers 対応してないんすか? というのは FAQ なわけで。プロのサーバ管理者の間では存在価値が疑問視されて久しい (Min|Max)SpareServers だと思うんですが、まあ書いてみるのもいいかと思って Parallel::Prefork のディストリビューションParallel::Prefork::SpareWorkers という (Min|Max)SpareServers 対応のクラスを追加しました。

作った理由はもうひとつあって、応答がなくなったワーカーを自動的に kill したり、スコアボードで処理状況を可視化したり、とかそういったこともできたらいいかな、みたいな。

閑話休題。でまあ、ワーカープロセス群の状態を管理しようと思うとスコアボードが必要になります。これをどう実現するか。C なら mmap + lock-free algorithm による読み書き、とかが楽だと思うんですけど、XS (Perl の C 拡張) 書くの面倒だし。現状は、各プロセス毎に1バイトしか (lock なしで) 読み書きしないので、race condition は問題にならないんですが、処理状況の可視化とかやり始めると、競合のこと考えないといけないし、どうしようかな...

というのが、

複数のプロセスが単一のファイルを同時に read / write をする場合、それぞれのオペレーションはアトミックなんでしょうか? (on linux or unix)

Kazuho Oku on Twitter: "複数のプロセスが単一のファイルを同時に read / write をする場合、それぞれのオペレーションはアトミックなんでしょうか? (on linux or unix)"

twitter で相談した背景。で、knu さんに、ずばり、

@kazuho POSIX.1-2008では、通常ファイルについてはread(2)/write(2)ともアトミックだとあります。

Akinori MUSHA on Twitter: "@kazuho POSIX.1-2008では、通常ファイルについてはread(2)/write(2)ともアトミックだとあります。"

と教えていただきました。ありがとうございます (他に返答をいただいた方々もありがとうございます)。

もうちょっと古い判から引用しておくと、

I/O is intended to be atomic to ordinary files and pipes and FIFOs. Atomic means that all the bytes from a single operation that started out together end up together, without interleaving from other I/O operations.

read

というわけで、普通に read(2) / write(2) (perl では sysread / syswrite) を使ってスコアボードを作れば、ロックとか気にしなくてもいいみたい。

@kazuho read/writeがアトミックでも、意図したバイト数を一発でread/writeする方法はないのでは?例えば、signalで割り込まれたり、バッファが足りなかったりすると、途中でsystem callやめて実際のread/writeバイト数を返す仕様ですよね。

Keiichi Koyama on Twitter: "@kazuho read/writeがアトミックでも、意図したバイト数を一発でread/writeする方法はないのでは?例えば、signalで割り込まれたり、バッファが足りなかったりすると、途中でsystem callやめて実際のread/writeバイト数を返す仕様ですよね。"

という指摘は一般論としてはその通りだと思いますが、各ワーカープロセスの書き込み位置が固定で複数ページにまたがることがないスコアボードのようなケースでは partial write は発生しないでしょうし、partial read になっちゃった場合は再試行すればいいよね、みたく思っています。

Twitter で反応いただいた方々、ありがとうございました。