ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
* [ruby-core:107980] [Ruby master Bug#18649] Enumerable#first breaks out of the incorect block when across threads
@ 2022-03-18 16:20 Eregon (Benoit Daloze)
  2022-03-23  0:37 ` [ruby-core:108032] " jeremyevans0 (Jeremy Evans)
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Eregon (Benoit Daloze) @ 2022-03-18 16:20 UTC (permalink / raw
  To: ruby-core

Issue #18649 has been reported by Eregon (Benoit Daloze).

----------------------------------------
Bug #18649: Enumerable#first breaks out of the incorect block when across threads
https://bugs.ruby-lang.org/issues/18649

* Author: Eregon (Benoit Daloze)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.1.1p18 (2022-02-18 revision 53f5fc4236) [x86_64-linux]
* Backport: 2.6: UNKNOWN, 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN
----------------------------------------
```ruby
def synchronize
  yield
end

def execute(task)
  success = true
  value = reason = nil
  end_sync = false

  synchronize do
    begin
      p :before
      value = task.call
      p :never_reached
      success = true
    rescue StandardError => ex
      p [:rescue, ex]
      reason = ex
      success = false
    end

    end_sync = true
    p :end_sync
  end

  p :should_not_reach_here! unless end_sync
  [success, value, reason]
end

def foo
  Thread.new do
    result = execute(-> { yield 42 })
    p [:result, result]
  end.join
end

p [:first, to_enum(:foo).first]
```

This code should raise LocalJumpError (and that should get `rescue`'d) because Enumerable#first can't break/return across threads.
But instead, it seems to break out of the block given to `synchronize`, which is clearly wrong.
That case is shown as `:should_not_reach_here!`.

Results:
```
ruby 3.0.3p157 (2021-11-24 revision 3fb7d2cadc) [x86_64-linux]
:before
:should_not_reach_here!
[:result, [true, nil, nil]]
[:first, 42]

ruby 3.1.1p18 (2022-02-18 revision 53f5fc4236) [x86_64-linux]
:before
:should_not_reach_here!
[:result, [true, nil, nil]]
[:first, 42]
```

CRuby (3.0 and 3.1) print `:should_not_reach_here!`, which is a semantic bug, if we get to the end of `execute` we should have gotten to the end of the block given to synchronize.

This is related to #18474 and https://github.com/ruby-concurrency/concurrent-ruby/issues/931.



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

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [ruby-core:108032] [Ruby master Bug#18649] Enumerable#first breaks out of the incorect block when across threads
  2022-03-18 16:20 [ruby-core:107980] [Ruby master Bug#18649] Enumerable#first breaks out of the incorect block when across threads Eregon (Benoit Daloze)
@ 2022-03-23  0:37 ` jeremyevans0 (Jeremy Evans)
  2022-04-19  9:46 ` [ruby-core:108292] " nobu (Nobuyoshi Nakada)
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: jeremyevans0 (Jeremy Evans) @ 2022-03-23  0:37 UTC (permalink / raw
  To: ruby-core

Issue #18649 has been updated by jeremyevans0 (Jeremy Evans).


Looks like this bug started in Ruby 2.2:

```
$ ruby21 -v t/t55.rb
ruby 2.1.9p490 (2016-03-30 revision 54437) [x86_64-openbsd]
:before
t/t55.rb:34:in `join': unexpected break (LocalJumpError)
        from t/t55.rb:34:in `foo'
        from t/t55.rb:37:in `each'
        from t/t55.rb:37:in `first'
        from t/t55.rb:37:in `<main>'
$ ruby22 -v t/t55.rb
ruby 2.2.10p489 (2018-03-28 revision 63023) [x86_64-openbsd]
:before
:should_not_reach_here!
[:result, [true, nil, nil]]
[:first, 42]
```

I found out that the `:should_not_reach_here!` issue can be avoided by uncommenting a block of code added in commit:3d980e1643305ff2ef7492d5fe25d89f63b29268.  This results in different behavior than pre-Ruby 2.2, and I'm not sure which is the desired behavior.  With the block uncommented, the yielding thread raises LocalJumpError, but the calling thread gets the yielded value without an exception.  I submitted a pull request for this approach: https://github.com/ruby/ruby/pull/5692

----------------------------------------
Bug #18649: Enumerable#first breaks out of the incorect block when across threads
https://bugs.ruby-lang.org/issues/18649#change-96991

* Author: Eregon (Benoit Daloze)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.1.1p18 (2022-02-18 revision 53f5fc4236) [x86_64-linux]
* Backport: 2.6: UNKNOWN, 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN
----------------------------------------
```ruby
def synchronize
  yield
end

def execute(task)
  success = true
  value = reason = nil
  end_sync = false

  synchronize do
    begin
      p :before
      value = task.call
      p :never_reached
      success = true
    rescue StandardError => ex
      p [:rescue, ex]
      reason = ex
      success = false
    end

    end_sync = true
    p :end_sync
  end

  p :should_not_reach_here! unless end_sync
  [success, value, reason]
end

def foo
  Thread.new do
    result = execute(-> { yield 42 })
    p [:result, result]
  end.join
end

p [:first, to_enum(:foo).first]
```

This code should raise LocalJumpError (and that should get `rescue`'d) because Enumerable#first can't break/return across threads.
But instead, it seems to break out of the block given to `synchronize`, which is clearly wrong.
That case is shown as `:should_not_reach_here!`.

Results:
```
ruby 3.0.3p157 (2021-11-24 revision 3fb7d2cadc) [x86_64-linux]
:before
:should_not_reach_here!
[:result, [true, nil, nil]]
[:first, 42]

ruby 3.1.1p18 (2022-02-18 revision 53f5fc4236) [x86_64-linux]
:before
:should_not_reach_here!
[:result, [true, nil, nil]]
[:first, 42]
```

CRuby (3.0 and 3.1) print `:should_not_reach_here!`, which is a semantic bug, if we get to the end of `execute` we should have gotten to the end of the block given to synchronize.

This is related to #18474 and https://github.com/ruby-concurrency/concurrent-ruby/issues/931.



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

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [ruby-core:108292] [Ruby master Bug#18649] Enumerable#first breaks out of the incorect block when across threads
  2022-03-18 16:20 [ruby-core:107980] [Ruby master Bug#18649] Enumerable#first breaks out of the incorect block when across threads Eregon (Benoit Daloze)
  2022-03-23  0:37 ` [ruby-core:108032] " jeremyevans0 (Jeremy Evans)
@ 2022-04-19  9:46 ` nobu (Nobuyoshi Nakada)
  2022-04-19 17:24 ` [ruby-core:108296] " jeremyevans0 (Jeremy Evans)
  2022-04-21  7:02 ` [ruby-core:108328] " nobu (Nobuyoshi Nakada)
  3 siblings, 0 replies; 5+ messages in thread
From: nobu (Nobuyoshi Nakada) @ 2022-04-19  9:46 UTC (permalink / raw
  To: ruby-core

Issue #18649 has been updated by nobu (Nobuyoshi Nakada).


jeremyevans0 (Jeremy Evans) wrote in #note-2:
> With the block uncommented, the yielding thread raises LocalJumpError, but the calling thread gets the yielded value without an exception.

Isn't it natural because the test code rescues but doesn't re-raise?

----------------------------------------
Bug #18649: Enumerable#first breaks out of the incorect block when across threads
https://bugs.ruby-lang.org/issues/18649#change-97313

* Author: Eregon (Benoit Daloze)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.1.1p18 (2022-02-18 revision 53f5fc4236) [x86_64-linux]
* Backport: 2.6: UNKNOWN, 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN
----------------------------------------
```ruby
def synchronize
  yield
end

def execute(task)
  success = true
  value = reason = nil
  end_sync = false

  synchronize do
    begin
      p :before
      value = task.call
      p :never_reached
      success = true
    rescue StandardError => ex
      p [:rescue, ex]
      reason = ex
      success = false
    end

    end_sync = true
    p :end_sync
  end

  p :should_not_reach_here! unless end_sync
  [success, value, reason]
end

def foo
  Thread.new do
    result = execute(-> { yield 42 })
    p [:result, result]
  end.join
end

p [:first, to_enum(:foo).first]
```

This code should raise LocalJumpError (and that should get `rescue`'d) because Enumerable#first can't break/return across threads.
But instead, it seems to break out of the block given to `synchronize`, which is clearly wrong.
That case is shown as `:should_not_reach_here!`.

Results:
```
ruby 3.0.3p157 (2021-11-24 revision 3fb7d2cadc) [x86_64-linux]
:before
:should_not_reach_here!
[:result, [true, nil, nil]]
[:first, 42]

ruby 3.1.1p18 (2022-02-18 revision 53f5fc4236) [x86_64-linux]
:before
:should_not_reach_here!
[:result, [true, nil, nil]]
[:first, 42]
```

CRuby (3.0 and 3.1) print `:should_not_reach_here!`, which is a semantic bug, if we get to the end of `execute` we should have gotten to the end of the block given to synchronize.

This is related to #18474 and https://github.com/ruby-concurrency/concurrent-ruby/issues/931.



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

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [ruby-core:108296] [Ruby master Bug#18649] Enumerable#first breaks out of the incorect block when across threads
  2022-03-18 16:20 [ruby-core:107980] [Ruby master Bug#18649] Enumerable#first breaks out of the incorect block when across threads Eregon (Benoit Daloze)
  2022-03-23  0:37 ` [ruby-core:108032] " jeremyevans0 (Jeremy Evans)
  2022-04-19  9:46 ` [ruby-core:108292] " nobu (Nobuyoshi Nakada)
@ 2022-04-19 17:24 ` jeremyevans0 (Jeremy Evans)
  2022-04-21  7:02 ` [ruby-core:108328] " nobu (Nobuyoshi Nakada)
  3 siblings, 0 replies; 5+ messages in thread
From: jeremyevans0 (Jeremy Evans) @ 2022-04-19 17:24 UTC (permalink / raw
  To: ruby-core

Issue #18649 has been updated by jeremyevans0 (Jeremy Evans).


nobu (Nobuyoshi Nakada) wrote in #note-3:
> jeremyevans0 (Jeremy Evans) wrote in #note-2:
> > With the block uncommented, the yielding thread raises LocalJumpError, but the calling thread gets the yielded value without an exception.
> 
> Isn't it natural because the test code rescues but doesn't re-raise?

I think the behavior of raising LocalJumpError is natural for the yielding thread (the yielding thread is the one that rescues but doesn't re-raise).  What I'm not sure about is whether the calling thread should still be able to get the value for a cross-thread yield.

----------------------------------------
Bug #18649: Enumerable#first breaks out of the incorect block when across threads
https://bugs.ruby-lang.org/issues/18649#change-97316

* Author: Eregon (Benoit Daloze)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.1.1p18 (2022-02-18 revision 53f5fc4236) [x86_64-linux]
* Backport: 2.6: UNKNOWN, 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN
----------------------------------------
```ruby
def synchronize
  yield
end

def execute(task)
  success = true
  value = reason = nil
  end_sync = false

  synchronize do
    begin
      p :before
      value = task.call
      p :never_reached
      success = true
    rescue StandardError => ex
      p [:rescue, ex]
      reason = ex
      success = false
    end

    end_sync = true
    p :end_sync
  end

  p :should_not_reach_here! unless end_sync
  [success, value, reason]
end

def foo
  Thread.new do
    result = execute(-> { yield 42 })
    p [:result, result]
  end.join
end

p [:first, to_enum(:foo).first]
```

This code should raise LocalJumpError (and that should get `rescue`'d) because Enumerable#first can't break/return across threads.
But instead, it seems to break out of the block given to `synchronize`, which is clearly wrong.
That case is shown as `:should_not_reach_here!`.

Results:
```
ruby 3.0.3p157 (2021-11-24 revision 3fb7d2cadc) [x86_64-linux]
:before
:should_not_reach_here!
[:result, [true, nil, nil]]
[:first, 42]

ruby 3.1.1p18 (2022-02-18 revision 53f5fc4236) [x86_64-linux]
:before
:should_not_reach_here!
[:result, [true, nil, nil]]
[:first, 42]
```

CRuby (3.0 and 3.1) print `:should_not_reach_here!`, which is a semantic bug, if we get to the end of `execute` we should have gotten to the end of the block given to synchronize.

This is related to #18474 and https://github.com/ruby-concurrency/concurrent-ruby/issues/931.



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

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [ruby-core:108328] [Ruby master Bug#18649] Enumerable#first breaks out of the incorect block when across threads
  2022-03-18 16:20 [ruby-core:107980] [Ruby master Bug#18649] Enumerable#first breaks out of the incorect block when across threads Eregon (Benoit Daloze)
                   ` (2 preceding siblings ...)
  2022-04-19 17:24 ` [ruby-core:108296] " jeremyevans0 (Jeremy Evans)
@ 2022-04-21  7:02 ` nobu (Nobuyoshi Nakada)
  3 siblings, 0 replies; 5+ messages in thread
From: nobu (Nobuyoshi Nakada) @ 2022-04-21  7:02 UTC (permalink / raw
  To: ruby-core

Issue #18649 has been updated by nobu (Nobuyoshi Nakada).


There is no way to tell whether the underlying `each` method is broken or exited gently.
I think that your PR would be better than the current.

----------------------------------------
Bug #18649: Enumerable#first breaks out of the incorect block when across threads
https://bugs.ruby-lang.org/issues/18649#change-97349

* Author: Eregon (Benoit Daloze)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.1.1p18 (2022-02-18 revision 53f5fc4236) [x86_64-linux]
* Backport: 2.6: UNKNOWN, 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN
----------------------------------------
```ruby
def synchronize
  yield
end

def execute(task)
  success = true
  value = reason = nil
  end_sync = false

  synchronize do
    begin
      p :before
      value = task.call
      p :never_reached
      success = true
    rescue StandardError => ex
      p [:rescue, ex]
      reason = ex
      success = false
    end

    end_sync = true
    p :end_sync
  end

  p :should_not_reach_here! unless end_sync
  [success, value, reason]
end

def foo
  Thread.new do
    result = execute(-> { yield 42 })
    p [:result, result]
  end.join
end

p [:first, to_enum(:foo).first]
```

This code should raise LocalJumpError (and that should get `rescue`'d) because Enumerable#first can't break/return across threads.
But instead, it seems to break out of the block given to `synchronize`, which is clearly wrong.
That case is shown as `:should_not_reach_here!`.

Results:
```
ruby 3.0.3p157 (2021-11-24 revision 3fb7d2cadc) [x86_64-linux]
:before
:should_not_reach_here!
[:result, [true, nil, nil]]
[:first, 42]

ruby 3.1.1p18 (2022-02-18 revision 53f5fc4236) [x86_64-linux]
:before
:should_not_reach_here!
[:result, [true, nil, nil]]
[:first, 42]
```

CRuby (3.0 and 3.1) print `:should_not_reach_here!`, which is a semantic bug, if we get to the end of `execute` we should have gotten to the end of the block given to synchronize.

This is related to #18474 and https://github.com/ruby-concurrency/concurrent-ruby/issues/931.



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

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2022-04-21  7:02 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-03-18 16:20 [ruby-core:107980] [Ruby master Bug#18649] Enumerable#first breaks out of the incorect block when across threads Eregon (Benoit Daloze)
2022-03-23  0:37 ` [ruby-core:108032] " jeremyevans0 (Jeremy Evans)
2022-04-19  9:46 ` [ruby-core:108292] " nobu (Nobuyoshi Nakada)
2022-04-19 17:24 ` [ruby-core:108296] " jeremyevans0 (Jeremy Evans)
2022-04-21  7:02 ` [ruby-core:108328] " nobu (Nobuyoshi Nakada)

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).