Issue #17298 has been reported by ko1 (Koichi Sasada).

----------------------------------------
Feature #17298: Ractor's basket communication APIs
https://bugs.ruby-lang.org/issues/17298

* Author: ko1 (Koichi Sasada)
* Status: Open
* Priority: Normal
----------------------------------------
This ticket proposes send_basket/send_receive, yield_basket/take_basket APIs to make effective and flexible bridge ractors.

## Background

When we want to send an object as a message, usually we need to copy it.
Copying is achieved by marshal protocol, and receiver load it immediately.

If we want to make a bridge ractor which receive a message and send it to another ractor, the immediate loading is not effective.

Receiver load

```ruby
bridge = Ractor.new do
  Ractor.yield Ractor.receive
end

consumer = Ractor.new bridge do |from|
  obj = from.take
  do_task(obj)
end

msg = [1, 2, 3]
bridge.send msg
```

In this case, the array (`[1, 2, 3]`) is

* (1) dumped at the first `bridge.send msg`
* (2) loaded at `Ractor.receive`
* (3) dumped again at `Ractor.yield`
* (4) laoded at `from.take`

Essentially we only need one dump/load pair, but now it needs 2 pairs.

Mixing "moving" is more complex.
Now there is no way to pass the "moving" status to the bridge ractors, we can not make a moving bridge.

## Proposal

To make more effective and flexible bridge ractors, we propose new basket APIs

* `Ractor.receive_basket`
* `Ractor#send_basket`
* `Ractor.take_basket`
* `Ractor.yield_basket`

They receive a message, but remaining dumped state and send it without dumping again.
We can rewrite the above example with these APIs.

```ruby
bridge = Ractor.new do
  Ractor.yield_basket Ractor.receive_basket
end

consumer = Ractor.new bridge do |from|
  obj = from.take
  do_task(obj)
end

msg = [1, 2, 3]
bridge.send msg
```

In this case,

* (1) dumped at the first `bridge.send msg`
* (2) laoded at `from.take`

we only need one dump/load pair.


## Implementation

https://github.com/ruby/ruby/pull/3725

## Evaluation

The following program makes 4 type of bridges and pass an array as a message through them.

```ruby
USE_BASKET = false

receive2yield = Ractor.new do
  loop do
    if USE_BASKET
      Ractor.yield_basket Ractor.receive_basket
    else
      Ractor.yield Ractor.receive
    end
  end
end

receive2send = Ractor.new receive2yield do |r|
  loop do
    if USE_BASKET
      r.send_basket Ractor.receive_basket
    else
      r.send Ractor.receive
    end
  end
end

take2yield = Ractor.new receive2yield do |from|
  loop do
    if USE_BASKET
      Ractor.yield_basket from.take_basket
    else
      Ractor.yield from.take
    end
  end
end

take2send = Ractor.new take2yield, Ractor.current do |from, to|
  loop do
    if USE_BASKET
      to.send_basket from.take_basket
    else
      to.send from.take
    end
  end
end

AN = 1_000
LN = 10_000

ary = Array.new(AN) # 1000
LN.times{
  receive2send << ary
  Ractor.receive
}

# This program passes the message as:
#   main ->
#   receive2send ->
#   receive2yield ->
#   take2yield ->
#   take2send ->
#   main
```

The result is:

```
w/ basket API   0m2.056s
w/o basket API  0m5.974s
```

on my machine (=~ x3 faster).

(BTW, if we have a TVar, we can change the value `USE_BASKET` dynamically)

## Discussion

### naming

Of course, naming is an issue. Now, I named "_basket" because source code using this terminology.
There are other candidates:

* container metaphor
  * package
  * parcel
  * box
  * envelope
  * packet (maybe bad idea because of confusion of networking)
  * bundle (maybe bad idea because of confusion of bin/bundle)
* "don't touch the content" metaphor
  * raw
  * unseal
  * unopened

I like "basket" because I like picnic.

### feature

Now, basket is represented by "Ractor::Basket" and there is no methods.
We can add the following feature:

* `Ractor::Basket#sender` return the sending ractor.
* `Ractor::Basket#sender = a_ractor` change the sending ractor.
* `Ractor::Basket#value` returns the content.

There was another proposal `Ractor.recvfrom`, but we only need these APIs.




-- 
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>