From: s.wanabe@... Date: 2018-01-30T03:42:33+00:00 Subject: [ruby-dev:50452] [Ruby trunk Bug#14387] Ruby 2.5 を Alpine Linux で実行すると比較的浅めで SystemStackError 例外になる Issue #14387 has been updated by wanabe (_ wanabe). mame (Yusuke Endoh) wrote: > 環境変数 `RUBY_THREAD_MACHINE_STACK_SIZE` に 1048576 とか大きい値を設定してみたら動きますか? > これで pthread_attr_setstacksize に渡すスタックサイズを調整できます。 スレッドといってもメインスレッドしか存在せず、native_thread_create() が呼び出されるわけではないので、残念ながら動作は変化していないようです。 ``` /work # ./miniruby test.rb 2>&1|grep level test.rb:8:in `each_value': stack level too deep (SystemStackError) ... 194 levels... /work # RUBY_THREAD_MACHINE_STACK_SIZE=1048576 ./miniruby test.rb 2>&1|grep level test.rb:8:in `each_value': stack level too deep (SystemStackError) ... 188 levels... ``` スレッドで包んでやると期待通りの挙動が得られました。 ``` /work # RUBY_THREAD_MACHINE_STACK_SIZE=1048576 ./miniruby -e 'Thread.new{ load "test.rb" }.join' 2>&1|grep level test.rb:8:in `each_value': stack level too deep (SystemStackError) ... 1720 levels... test.rb:8:in `each_value': stack level too deep (SystemStackError) ... 1720 levels... ``` 以下のようなテストプログラムを動かすと、Alpine musl 環境では rlim_cur の値と pthread_attr_getstacksize の値が大きく異なることがわかりました。 このあたりが関係しているのではないでしょうか。 ``` #include #include #include int main (void) { struct rlimit limit; pthread_attr_t attr; size_t stacksize; getrlimit(RLIMIT_STACK, &limit); pthread_attr_init(&attr); pthread_attr_getstacksize(&attr, &stacksize); pthread_attr_destroy(&attr); printf("rlim_cur: %ld\nrlim_max: %ld\nstacksize: %ld\n", (size_t)limit.rlim_cur, (size_t)limit.rlim_max, stacksize); return 0; } ``` ``` /work # gcc -pthread test.c && ./a.out rlim_cur: 8388608 rlim_max: -1 stacksize: 81920 ``` なお、同じプログラムを ubuntu 18.04 上で動かした場合には両者は一致しました。 ``` $ gcc -pthread test.c && ./a.out rlim_cur: 8388608 rlim_max: -1 stacksize: 8388608 ``` ---------------------------------------- Bug #14387: Ruby 2.5 を Alpine Linux で実行すると比較的浅めで SystemStackError 例外になる https://bugs.ruby-lang.org/issues/14387#change-69996 * Author: koshigoe (Masataka SUZUKI) * Status: Open * Priority: Normal * Assignee: * Target version: * ruby -v: ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-linux-musl] * Backport: 2.3: UNKNOWN, 2.4: UNKNOWN, 2.5: UNKNOWN ---------------------------------------- CircleCI で Alpine Linux を使って Ruby 2.5.0 で Rubocop を実行した時に遭遇した例外です(Ruby 2.4.3 では発生しませんでした)。 Ruby のバージョンによって、再帰が止められるまでの回数に大きな違いがあるのはなぜでしょうか? これは、意図された挙動なのか、Ruby の変更によるものでは無く Alpine Linux 固有の問題なのか、教えていただく事は可能でしょうか? Alpine Linux の Tread stack size が比較的小さい事で、Ruby 2.5.0 からこのような挙動になったのでしょうか? https://wiki.musl-libc.org/functional-differences-from-glibc.html#Thread-stack-size ## 再現 問題の再現のため、以下の様な再帰するコードを実行します。 ~~~ ruby # test.rb n = 100000 res = {} 1.upto(n).to_a.inject(res) do |r, i| r[i] = {} end def f(x) x.each_value { |v| f(v) } end f(res) ~~~ Ruby 2.4.3 で実行した場合、 10061 levels で例外があがりました。 ~~~ % docker container run \ -v (pwd):/mnt/my --rm \ ruby:2.4.3-alpine3.7 \ ruby -v /mnt/my/test.rb ruby 2.4.3p205 (2017-12-14 revision 61247) [x86_64-linux-musl] /mnt/my/test.rb:9:in `each_value': stack level too deep (SystemStackError) from /mnt/my/test.rb:9:in `f' from /mnt/my/test.rb:9:in `block in f' from /mnt/my/test.rb:9:in `each_value' from /mnt/my/test.rb:9:in `f' from /mnt/my/test.rb:9:in `block in f' from /mnt/my/test.rb:9:in `each_value' from /mnt/my/test.rb:9:in `f' from /mnt/my/test.rb:9:in `block in f' ... 10061 levels... from /mnt/my/test.rb:9:in `block in f' from /mnt/my/test.rb:9:in `each_value' from /mnt/my/test.rb:9:in `f' from /mnt/my/test.rb:12:in `
' ``` 一方で Ruby 2.5.0 で実行した場合、 134 level で例外があがりました。 ``` % docker container run \ -v (pwd):/mnt/my --rm \ test/ruby:trunk-alpine3.7 \ ruby -v /mnt/my/test.rb ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-linux-musl] /mnt/my/test.rb:9:in `each_value': stack level too deep (SystemStackError) from /mnt/my/test.rb:9:in `f' from /mnt/my/test.rb:9:in `block in f' from /mnt/my/test.rb:9:in `each_value' from /mnt/my/test.rb:9:in `f' from /mnt/my/test.rb:9:in `block in f' from /mnt/my/test.rb:9:in `each_value' from /mnt/my/test.rb:9:in `f' from /mnt/my/test.rb:9:in `block in f' ... 134 levels... from /mnt/my/test.rb:9:in `block in f' from /mnt/my/test.rb:9:in `each_value' from /mnt/my/test.rb:9:in `f' from /mnt/my/test.rb:12:in `
' ``` また、Ruby trunk で実行した場合は 2.5.0 同等の結果になりました。 ``` ruby 2.6.0dev (2018-01-24 trunk 62017) [x86_64-linux-musl] /mnt/my/test.rb:9:in `each_value': stack level too deep (SystemStackError) from /mnt/my/test.rb:9:in `f' from /mnt/my/test.rb:9:in `block in f' from /mnt/my/test.rb:9:in `each_value' from /mnt/my/test.rb:9:in `f' from /mnt/my/test.rb:9:in `block in f' from /mnt/my/test.rb:9:in `each_value' from /mnt/my/test.rb:9:in `f' from /mnt/my/test.rb:9:in `block in f' ... 134 levels... from /mnt/my/test.rb:9:in `block in f' from /mnt/my/test.rb:9:in `each_value' from /mnt/my/test.rb:9:in `f' from /mnt/my/test.rb:12:in `
' ``` ※ trunk の Docker イメージを作った際の Dockerfile は以下。 https://gist.github.com/koshigoe/509be02a3580cdfc7a2cc45a4e6e44c5 -- https://bugs.ruby-lang.org/