Skip to content
tsahara-iij edited this page Sep 24, 2012 · 7 revisions

Errno モジュールの設計メモ

Errno::EPERM 等、Errno::(名前) クラスが Errno モジュールの主要なクラスである。 Errno::EXXX クラスは SystemCallError クラスのサブクラス。 そしてこの親子関係はたいへんややこしい。

SystemCallError.initialize

引数の仕様がややこしい。

  1. new(errno), new(message), new(message, errno) の 3通りの呼び方がある。 1引数の場合は引数の型(String or Fixnum)によって動作が変わる。
  2. errno が指定されており、しかもそれがそのプラットホームで EHOGE で define されている場合、SystemCallError.new は SystemCallError のインスタンスではなく Error::EHOGE のインスタンスを生成する。なお、SystemCallError と Error::EHOGE のインタフェースは違う(!)ことに注意。
  3. errno が指定されており、しかしそれがそのプラットホームで EHOGE で define されていない場合、SystemCallError.new は SystemCallError のインスタンスを生成する。 そしてその message は format("Unknown error: %d", errno) から始まる文字列となる。
  4. errno と message の両方が指定されている場合、2,3 の動作に加え、 message が返す文字列の末尾に "- #{message}" が付加される。
  5. errno が指定されていない場合(= message のみ指定されている場合)、message は "unknown error - #{message}" となる。

2,3 で必要とされる、「errno がプラットホームで EHOGE が define されているか」を実行時に知るため、 errno2class という構造体の配列を用意した。 no メンバが errno に対応する整数値、cl がそれに対応する Errno::EXXX クラスである。 errno2class は二分探索ができるようソートしてある。 が、探索する方のコードはまだ書いておらず、今は先頭から順に探している。 errno の数値に対応するクラスは数値さえあっていれば何でも良いため不安定なソートで良い。 そして配列のいくつかのメンバが使われず無駄になるが、無視した。

errno を配列のインデックスにそのまま使いたい気もするが、errno の値が疎であるような環境で問題になりえるためやめた。

SystemCallError#message メソッドが返す文字は初期化のしかたによって変わり、 しかもインスタンスが生成された後は変化しないため、 message メソッドが返すべき文字列も SystemCallError.initialize で作成する。

Errno::EXXX クラスの生成

Errno::EPERM, Errno::EAGAIN 等のクラスの生成が難しい。

  1. 「そのプラットホームで定義されている EXXX に対応する」クラスだけを生成する必要がある。そしてそのプラットホームでどの EXXX が定義されているかは、コンパイル時にしかわからない。
  2. しかも「errno の値が同じだが EXXX のシンボルが違うクラス」は、どれかひとつを代表として選んで生成し、そのクラス以外は代表クラスを示す定数として定義する必要がある。

mruby のビルド時にかんたんなプログラムをコンパイル/実行すれば必要な情報は集められるが、 ビルドの処理を複雑にしたことによるポータビリティの低下を避けるため、 cpp マクロのみで完結できるよう設計した。

known_errors.def は EXXX のシンボルを並べただけのファイルである。 このファイルから gen.rb によって known_errors_def.cstub と known_errors_e2c.cstub が生成される。 known_errors_def.cstub は errno2class の定義に使われる。 known_errors_e2c.cstub は Errno::EXXX クラスを生成するコードに展開される。 詳細はこれらのファイルを見ればわかるため以下略。

SystemCallError#errno

SystemCallError のインスタンスの場合、#new で指定された errno を覚えておき、それを返す。 Errno::EXXX クラスのインスタンスには定数 Errno が定義されているため、それを返す。

mrb_sys_fail()

mrb_sys_fail() は mruby プロセスが呼んだシステムコールが errno をセットして返ってきた時に 呼ばれることが期待される C の関数である。 この関数はその errno に対応した SystemCallError またはそのサブクラスのインスタンスを生成する。 しかしひとつやっかいな仕様があり、「errno に対応した Errno::EXXX クラスが存在しない場合、Errno::ENNN というクラスを生成し、そのインスタンスを返す」必要がある。

Clone this wiki locally