Issue #13885 has been updated by mame (Yusuke Endoh).


鴻сnil с RuntimeError を投げるようにしました(r59858)。

残るはシステムコールをループする点だけですが、

> それってO_NBLOCKのディスクリプタをビジーループで読み込みってことですよね。ユーザーランドからへんにビジーループするよりカーネル側で適切に生成されてくるのをユーザーランド側はすなおにブロッキングIOしたほうがトータルのコンテキストスイッチが少なそうな気がするんですが、まあ気がするだけで定量的評価ではないです。

これはちょっとよくわかりませんでした。ブロッキング IO にしても、getrandom(2) や read(2) システムコールは途中で終わる可能性がある(つまりカーネル側で要求量きっかり生成してくれることは保証されない)ので、ユーザランドでのループはどのみち必要です。

GVL を放すかどうかは、放してもいいとは思いますが、

* getrandom(2) などがブロックすることはない(はず)
* 普通のユースケース(数十バイト程度の urandom 取得)では、ループが必要になることもほぼない

ということで、オーバーヘッドになるだけのような気はしてます。
あと、この辺の関数はスレッド周りが初期化されていないプロセス起動時にも呼ばれるので、rb_thread_call_without_gvl が単純に呼べず、さらにパッチがでかくなりそうでした。

----------------------------------------
Bug #13885: Random.urandom と securerandom について
https://bugs.ruby-lang.org/issues/13885#change-66658

* Author: mame (Yusuke Endoh)
* Status: Open
* Priority: Normal
* Assignee: 
* Target version: 
* ruby -v: ruby 2.5.0dev (2017-09-09 trunk 59792) [x86_64-linux]
* Backport: 2.2: UNKNOWN, 2.3: UNKNOWN, 2.4: UNKNOWN
----------------------------------------
遠藤です。

Random.urandom と securerandom の仕様について卜部さんと話していて、いくつか気になる点が出てきたので挙げておきます。

1. Random.urandom は、getrandom(2) や (/dev/urandom に対する) read(2) システムコールを 1 回しか呼ばないようです。よって、要求量が大きくて 1 回では文字列を準備できないとき(例えば Random.urandom(100_000_000) )、失敗して nil を返します。これは意図的でしょうか。要求量が得られるまで(または他の要因で失敗するまで)繰り返し呼ぶべきではないでしょうか。
2. Random.urandom は失敗するとき、nil ではなく例外を投げるほうがよいのではないでしょうか。nil を返して実行が進んで幸せになるケースはあまり思い浮かびませんでした。
3. Random.urandom が nil を返す可能性があることは rdoc で明記されるべきではないでしょうか。(とりあえず r59803 で書き足しました。まずかったらすみません)
4. securerandom は、初回の呼び出しで Random.urandom が成功したらその後ずっと Random.urandom を使い、失敗したらずっと openssl を使うようになっています。これは本当に初回の呼び出しだけで振り分けるのでよいのでしょうか。具体的には、Random.urandom(0) は常に成功する(空文字列を返す)ので、実際には urandom が使えない環境でも urandom に固定される可能性があります。
5. 逆に、securerandom のバックエンドを動的に切り替えるようにしたら問題あるでしょうか。最初は成功していた Random.urandom が途中から失敗するようになったとき、openssl へフォールバックしても良いものでしょうか。また、Random.urandom が復活した場合、openssl から Random.urandom に戻すのは問題ないでしょうか。



-- 
https://bugs.ruby-lang.org/