Bug #9105

callcc による不整合(例:Hash)

Added by Masaya Tarui over 1 year ago. Updated over 1 year ago.

[ruby-dev:47803]
Status:Closed
Priority:Normal
Assignee:-
ruby -v:ruby 2.1.0dev (2013-11-11 trunk 43647) [x86_64-linux] Backport:1.9.3: UNKNOWN, 2.0.0: UNKNOWN

Description

=begin
以下のコードを実行すると、
equire 'continuation'
h = {1=>2,3=>4}
c = nil
f = false
h.each { callcc {|c2| c = c2 } }
unless f
f = true
c.call
end
h.each {|i| h.delete(1); p i}

以下のような結果になります。

[1, 2]
[false, false]
test.rb:10:in each': hash modified during iteration (RuntimeError)
from test.rb:10:in
'

[false, false]自体はst_foreach_checkで全く機能していない
/* call func with error notice */
retval = (*func)(0, 0, arg, 1);
を消せばいいのですが、
RuntimeErrorが発生するのはcallccによりhash_foreach_ensureの実行が2重に行われて、
RHASH_ITER_LEV(hash)の管理が破綻してしまっている為です。

他の所でも容易に起こりうると思うのですが、未調査です。
&そもそもどう対処すべきでしょう?

=end

Associated revisions

Revision 43675
Added by Nobuyoshi Nakada over 1 year ago

hash.c: restore iter_lev

  • hash.c (hash_foreach_ensure): restore iter_lev to the previous value, not just decrement. [Bug #9105]

Revision 43675
Added by Nobuyoshi Nakada over 1 year ago

hash.c: restore iter_lev

  • hash.c (hash_foreach_ensure): restore iter_lev to the previous value, not just decrement. [Bug #9105]

Revision 43683
Added by Nobuyoshi Nakada over 1 year ago

hash.c: iteration level with reentering

  • hash.c (hash_foreach_iter, hash_foreach_ensure, rb_hash_foreach): deal with iteration level when reentering by callcc. temporary measure until rollback of ensure is introduced. [Bug #9105]

Revision 43683
Added by Nobuyoshi Nakada over 1 year ago

hash.c: iteration level with reentering

  • hash.c (hash_foreach_iter, hash_foreach_ensure, rb_hash_foreach): deal with iteration level when reentering by callcc. temporary measure until rollback of ensure is introduced. [Bug #9105]

Revision 43688
Added by Masaya Tarui over 1 year ago

  • cont.c : Introdule ensure rollback mechanism. Please see below.

    • internal.h (ruby_register_rollback_func_for_ensure): catch up above change. Add rollback mechanism API.
    • vm_core.h (typedef struct rb_vm_struct): catch up above change. Introdule ensure-rollback relation table.
    • vm_core.h (typedef struct rb_thread_struct): catch up above change. Introdule ensure stack.
    • eval.c (rb_ensure): catch up above change. Introdule ensure stack.
    • hash.c : New function for rollback ensure, and register it to ensure-rollback relation table. [Bug #9105]

    Ensure Rollback Mechanism:
    A rollback's function is a function to rollback a state before ensure's
    function execution.
    When the jump of callcc is across the scope of rb_ensure,
    ensure's functions and rollback's functions are executed appropriately
    for keeping consistency.

    Current API is unstable, and only internal use.

    ruby_register_rollback_func_for_ensure(ensure_func,rollback_func)
    This API create relation ensure's function to rollback's function.
    By registered rollback's function, it is executed When jumpping into
    corresponding rb_ensure scope.

Revision 43688
Added by Masaya Tarui over 1 year ago

  • cont.c : Introdule ensure rollback mechanism. Please see below.

    • internal.h (ruby_register_rollback_func_for_ensure): catch up above change. Add rollback mechanism API.
    • vm_core.h (typedef struct rb_vm_struct): catch up above change. Introdule ensure-rollback relation table.
    • vm_core.h (typedef struct rb_thread_struct): catch up above change. Introdule ensure stack.
    • eval.c (rb_ensure): catch up above change. Introdule ensure stack.
    • hash.c : New function for rollback ensure, and register it to ensure-rollback relation table. [Bug #9105]

    Ensure Rollback Mechanism:
    A rollback's function is a function to rollback a state before ensure's
    function execution.
    When the jump of callcc is across the scope of rb_ensure,
    ensure's functions and rollback's functions are executed appropriately
    for keeping consistency.

    Current API is unstable, and only internal use.

    ruby_register_rollback_func_for_ensure(ensure_func,rollback_func)
    This API create relation ensure's function to rollback's function.
    By registered rollback's function, it is executed When jumpping into
    corresponding rb_ensure scope.

Revision 43692
Added by Masaya Tarui over 1 year ago

  • test/ruby/test_hash.rb (class TestHash): add tests for [Bug #9105]

Revision 43692
Added by Masaya Tarui over 1 year ago

  • test/ruby/test_hash.rb (class TestHash): add tests for [Bug #9105]

History

#1 Updated by Nobuyoshi Nakada over 1 year ago

  • Status changed from Open to Closed
  • % Done changed from 0 to 100

This issue was solved with changeset r43675.
Masaya, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.


hash.c: restore iter_lev

  • hash.c (hash_foreach_ensure): restore iter_lev to the previous value, not just decrement. [Bug #9105]

#2 Updated by Tomoyuki Chikanaga over 1 year ago

  • Status changed from Closed to Open

r43675 はマルチスレッドで同一の Hash を使っている場合に RHASH_ITER_LEV(hash) の値が不正になることがあると思います。
以下のようなスクリプトを実行すると RHASH_ITER_LEV() が 0 に戻らなくなるので要素追加に失敗します。

h = {1=>2}
th1 = Thread.start{ h.each{ sleep 1 } }
sleep 0.1
th2 = Thread.start{ h.each { sleep 3 } }
th1.join
th2.join

h[5] = 6 # => can't add a new key into hash during iteration (RuntimeError)

#3 Updated by Nobuyoshi Nakada over 1 year ago

  • Status changed from Open to Closed

This issue was solved with changeset r43683.
Masaya, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.


hash.c: iteration level with reentering

  • hash.c (hash_foreach_iter, hash_foreach_ensure, rb_hash_foreach): deal with iteration level when reentering by callcc. temporary measure until rollback of ensure is introduced. [Bug #9105]

Also available in: Atom PDF