[#101179] Spectre Mitigations — Amel <amel.smajic@...>
Hi there!
5 messages
2020/12/01
[#101180] Re: Spectre Mitigations
— Chris Seaton <chris@...>
2020/12/01
I wouldn’t recommend using Ruby to run in-process untrusted code in the first place. Are people doing that?
[#101694] Ruby 3.0.0 Released — "NARUSE, Yui" <naruse@...>
We are pleased to announce the release of Ruby 3.0.0. From 2015 we
4 messages
2020/12/25
[ruby-core:101399] [Ruby master Feature#17378] Ractor#receive with filtering like other actor langauge
From:
marcandre-ruby-core@...
Date:
2020-12-10 17:16:15 UTC
List:
ruby-core #101399
Issue #17378 has been updated by marcandre (Marc-Andre Lafortune).
When discussing with @ko1 and @eregon, I had an idea for a better API.
I'd like to be able to call `Ractor.receive` this way:
```ruby
def example
loop do
Ractor.receive do |message|
case message
in [:cmd1, arg] then task1(arg)
in [:cmd2] then return task2
end
end
end
end
```
It does not matter what `task1` returns; the value of the block is not used to determine filtering.
`task1` and `task2` can also call `Ractor.receive`, for example:
```ruby
def task1(arg)
other_ractor.send(:do_something, arg)
Ractor.receive do |message|
case message
in [:do_something_response, :success, response] then process(response)
in [:do_something_response, :failure] then raise 'Oh oh'
end
end
end
```
The filtering is implicit in these examples, like in Elixir/Erlang.
If there is no match, the `case in` raises a `NoMatchingPatternError`. This is a way to signal that `message` was not consumed. `Ractor.receive` rescues the exception and saves the message.
For example, if some `:cmd2` is sent just when `task1` is doing it's `Ractor.receive`, it will not match the `case`, and the message is not consumed. Just after that, the response from `other_ractor` arrives and `task1` finishes, the `:cmd2` will be received in `example`.
This has the benefits:
- `task1` and `task2` can return anything, including `nil` or `false` (otherwise can be error prone)
- you can `return/break` out of `receive`
- implicit handling of exception case
- very close to Elixir/Erlang semantics
- also would make `case/in` more popular!
There would be an additional way to reject a message, with a second parameter with meta information (client ractor and unique request ID):
```ruby
Ractor.receive do |message, req|
cmd, arg = message
case cmd
when :cmd1 then task1(arg)
when :cmd2 then return task2
else
req.reject # => same effect as `raise NoMatchingPatternError`
end
end
```
The `req` object would have methods returning the `ractor` that sent the request, as well as a unique id. This makes responding correctly very easy. Another thread could be communicating with the same ractor and responses could not be mixed:
```ruby
def task1(arg)
req = other_ractor.send(:do_something, arg)
Ractor.receive do |message|
case message
in [^req, :success, response] then process(response)
in [^req, :failure] then raise 'Oh oh'
end
end
end
```
Matz, what do you think of this API?
Note: there are remaining questions on how to handle `receive`/`receive_if` with block and multi-thread; hopefully we can keep this issue focused on the API assuming we have a such a method.
----------------------------------------
Feature #17378: Ractor#receive with filtering like other actor langauge
https://bugs.ruby-lang.org/issues/17378#change-89159
* Author: ko1 (Koichi Sasada)
* Status: Open
* Priority: Normal
----------------------------------------
With discussion with @marcandre, we found good extension for `Ractor.receive` with block.
```ruby
Ractor.receive do |msg|
if msg is match to condition?
true
else
false
end
end
```
This block iterates incoming queue's values and the value is passed in `msg`.
If the passed value is matched you want to process, the block should return true and the value will be removed from the queue.
Otherwise (returning falsy value), the value remains in the queue, and other `Ractor.receive` accesses it again.
If there is not more values in queue, the interpreter sleeps until new values are received.
```ruby
r = Ractor.new do |;r|
loop do
r, msg = Ractor.receive
r << msg
r << 1
r << 2
r << 3
end
end
r.send([Ractor.current, :ping])
# incoming queue: [:ping, 1, 2, 3] (3 is at the last)
Ractor.receive #=> :ping # without a block
# incoming queue: [1, 2, 3]
p Ractor.receive{|msg| msg == 2}
#=> 2
# incoming queue: [1, 3]
begin
# exception in the block doesn't touch the queue
p Ractor.receive{|msg| raise "err"}
rescue => e
p e.message #=> "err"
end
# incoming queue: [1, 3]
p Ractor.receive #=> 1
# incoming queue: [3]
p Ractor.receive #=> 3
```
With multiple server, we can make it:
```ruby
morning_server = Ractor.new do
loop do
task = Proc.new{|obj| "Good morning #{obj}"}
r, req = Ractor.receive
res = task.(req)
r.send([Ractor.current, res])
end
end
evening_server = Ractor.new do
loop do
task = Proc.new{|obj| "Good evening #{obj}"}
r, req = Ractor.receive
res = task.(req)
r.send([Ractor.current, res])
end
end
morning_server << [Ractor.current, 'ko1']
morning_server << [Ractor.current, 'ko2']
morning_server << [Ractor.current, 'ko3']
evening_server << [Ractor.current, 'ko1']
evening_server << [Ractor.current, 'ko2']
evening_server << [Ractor.current, 'ko3']
def receive r
Ractor.receive{|(from, msg)|
r == from
}[1]
end
p receive(morning_server) #=> "Good morning ko1"
p receive(evening_server) #=> "Good evening ko1"
p receive(morning_server) #=> "Good morning ko2"
p receive(evening_server) #=> "Good evening ko2"
p receive(morning_server) #=> "Good morning ko3"
p receive(evening_server) #=> "Good evening ko3"
```
--
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>