Issue #10344 has been updated by yugui (Yuki Sonoda).

Assignee set to ko1 (Koichi Sasada)
Target version set to 2.5

Let me add another use case which I discussed with naruse and akr in RubyKaigi.

`Fiber#raise` is helpful to improve compatibility of methods with blocks (internal iterators) and `Enumerator` (external iterators).
This happened for me when I tried to write an adapter between two independently-developed APIs.

Here's a simplified example below.  You can also find the real example in https://github.com/supership-jp/activerecord-spanner-adapter/blob/master/lib/active_record/connection_adapters/spanner/client.rb.

# Example
Suppose that we have a third-paty API, which we cannot control.

```
def with_transaction
  tx = Transaction.new
  begin
    yield tx
  rescue
    tx.rollback
  else
    tx.commit
  end
end
```

And now I need to begin a transaction in a method and then need to commit or rollback the transaction in other methods.

```
class TransactionManager
  def begin_transaction
     @iter = Transaction.enum_for(:begin_transaction)
     @tx = @iter.next # skip error handlings for simplicity
  end

  def commit_transaction
    loop { @iter.next }
  ensure
    @tx = nil
  end

  def abort_transaction(reason = RuntimeError.new)
    # ??? How can I let the iterator know the error raised
    @iter.raise(reason)
  end
  
  attr_reader :tx
end
```

In other words, internal iterators in Ruby can manage resources in it but external iterators can't even if the user tried to take responsibility of calling cleanup mechanism.
In the real usecase, `with_transaction` was an API given by `google-cloud-spanner` gem and the interface of the `TransactionManager` was the one I had to implement for ActiveRecord.

# Proposal
Although I could certainly implement the adapter without the native `Fiber#raise`, it would be still helpful if internal iterators and external iterators in Ruby were more consistent.

Can we have `Fiber#raise` and `Enumerator#raise` on top of it to improve the consistency? ko1?

FYI: I've confirmed that the patch by nome still works fine for ruby-trunk.  https://github.com/yugui/ruby/commit/5a04a8b49b5086686fd75c6635c95c12ccc6caa8

----------------------------------------
Feature #10344: [PATCH] Implement Fiber#raise
https://bugs.ruby-lang.org/issues/10344#change-66770

* Author: nome (Knut Franke)
* Status: Open
* Priority: Normal
* Assignee: ko1 (Koichi Sasada)
* Target version: 2.5
----------------------------------------
While it is possible to implement this in pure Ruby (by wrapping Fiber.yield and Fiber#resume), this feels like a low-level feature that ought to be provided out of the box. Also, the C implementation is more straight-forward, and more efficient. Unfortunately, it is not quite possible to implement this as a C extension module (without resorting to wrappers again); cf. the change to make_passing_arg().

Example usage:

~~~
fib = Fiber.new do
  counter = 0
  loop { counter += Fiber.yield }
  counter
end
fib.resume
fib.resume 10
fib.resume 100
fib.raise StopIteration # => 110
~~~

---Files--------------------------------
0001-Implement-Fiber-raise.patch (4.12 KB)
0001-Implement-Fiber-raise.patch (3.51 KB)
0001-Implement-Fiber-raise-in-ext-fiber.patch (3.6 KB)


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

Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>