[前][次][番号順一覧][スレッド一覧]

ruby-changes:64350

From: Victor <ko1@a...>
Date: Sun, 20 Dec 2020 03:05:01 +0900 (JST)
Subject: [ruby-changes:64350] 1f565ac6d9 (master): Add documentation for Ractor (#3895)

https://git.ruby-lang.org/ruby.git/commit/?id=1f565ac6d9

From 1f565ac6d98e902f51a0ea7b8b4aa85693deea6e Mon Sep 17 00:00:00 2001
From: Victor Shepelev <zverok.offline@g...>
Date: Sat, 19 Dec 2020 20:04:40 +0200
Subject: Add documentation for Ractor (#3895)


diff --git a/.document b/.document
index 0e0969a..8616203 100644
--- a/.document
+++ b/.document
@@ -20,6 +20,7 @@ kernel.rb https://github.com/ruby/ruby/blob/trunk/.document#L20
 pack.rb
 trace_point.rb
 warning.rb
+ractor.rb
 
 # the lib/ directory (which has its own .document file)
 lib
diff --git a/ractor.c b/ractor.c
index 08150f4..0565827 100644
--- a/ractor.c
+++ b/ractor.c
@@ -1933,6 +1933,100 @@ ractor_moved_missing(int argc, VALUE *argv, VALUE self) https://github.com/ruby/ruby/blob/trunk/ractor.c#L1933
     rb_raise(rb_eRactorMovedError, "can not send any methods to a moved object");
 }
 
+/*
+ *  Document-class: Ractor::ClosedError
+ *
+ *  Raised when an attempt is made to take something from the Ractor's outgoing port,
+ *  but it is closed with Ractor#close_outgoing, or to send something to Ractor's
+ *  incoming port, and it is closed with Ractor#close_incoming, or an attempt to
+ *  send/take something with ractor which was already terminated.
+ *
+ *     r = Ractor.new { sleep(500) }
+ *     r.close_outgoing
+ *     r.take # Ractor::ClosedError
+ *
+ *  ClosedError is a descendant of StopIteration, so the closing of the ractor will break
+ *  the loops without propagating the error:
+ *
+ *     r = Ractor.new { 3.times { puts "Received: " + receive } }
+ *
+ *     loop { r.send "test" }
+ *     puts "Continue successfully"
+ *
+ *  This will print:
+ *
+ *     Received: test
+ *     Received: test
+ *     Received: test
+ *     Continue successfully
+ */
+
+/*
+ *  Document-class: Ractor::RemoteError
+ *
+ *  Raised on attempt to Ractor#take if there was an uncaught exception in the Ractor.
+ *  Its +cause+ will contain the original exception, and +ractor+ is the original ractor
+ *  it was raised in.
+ *
+ *     r = Ractor.new { raise "Something weird happened" }
+ *
+ *     begin
+ *       r.take
+ *     rescue => e
+ *       p e             # => #<Ractor::RemoteError: thrown by remote Ractor.>
+ *       p e.ractor == r # => true
+ *       p e.cause       # => #<RuntimeError: Something weird happened>
+ *     end
+ *
+ */
+
+/*
+ *  Document-class: Ractor::MovedError
+ *
+ *  Raised on an attempt to access an object which was moved in Ractor#send or Ractor.yield.
+ *
+ *     r = Ractor.new { sleep }
+ *
+ *     ary = [1, 2, 3]
+ *     r.send(ary, move: true)
+ *     ary.inspect
+ *     # Ractor::MovedError (can not send any methods to a moved object)
+ *
+ */
+
+/*
+ *  Document-class: Ractor::MovedObject
+ *
+ *  A special object which replaces any value that was moved to another ractor in Ractor#send
+ *  or Ractor.yield. Any attempt to access the object results in Ractor::MovedError.
+ *
+ *     r = Ractor.new { receive }
+ *
+ *     ary = [1, 2, 3]
+ *     r.send(ary, move: true)
+ *     p Ractor::MovedObject === ary
+ *     # => true
+ *     ary.inspect
+ *     # Ractor::MovedError (can not send any methods to a moved object)
+ *
+ *  The class MovedObject is frozen to avoid tampering with it:
+ *
+ *     class Ractor::MovedObject
+ *       def inspect
+ *         "<MyMovedObject>"
+ *       end
+ *     end
+ *     # FrozenError (can't modify frozen class: Ractor::MovedObject)
+ */
+
+// Main docs are in ractor.rb, but without this clause there are weird artifacts
+// in their rendering.
+/*
+ *  Document-class: Ractor
+ *
+ */
+
+
 void
 Init_Ractor(void)
 {
diff --git a/ractor.rb b/ractor.rb
index 8874107..3a8aa99 100644
--- a/ractor.rb
+++ b/ractor.rb
@@ -1,34 +1,242 @@ https://github.com/ruby/ruby/blob/trunk/ractor.rb#L1
+# Ractor is a Actor-model abstraction for Ruby that provides thread-safe parallel execution.
+#
+# To achieve this, ractors severely limit data sharing between different ractors.
+# Unlike threads, ractors can't access each other's data, nor any data through variables of
+# the outer scope.
+#
+#     # The simplest ractor
+#     r = Ractor.new {puts "I am in Ractor!"}
+#     r.take # allow it to finish
+#     # here "I am in Ractor!" would be printed
+#
+#     a = 1
+#     r = Ractor.new {puts "I am in Ractor! a=#{a}"}
+#     # fails immediately with
+#     # ArgumentError (can not isolate a Proc because it accesses outer variables (a).)
+#
+# On CRuby (the default implementation), Global Virtual Machine Lock (GVL) is held per ractor, so
+# ractors are performed in parallel without locking each other.
+#
+# Instead of accessing the shared state, the data should be passed to and from ractors via
+# sending and receiving messages, thus making them _actors_ ("Ractor" stands for "Ruby Actor").
+#
+#     a = 1
+#     r = Ractor.new do
+#       a_in_ractor = receive # receive blocks till somebody will pass message
+#       puts "I am in Ractor! a=#{a_in_ractor}"
+#     end
+#     r.send(a)  # pass it
+#     r.take
+#     # here "I am in Ractor! a=1" would be printed
+#
+# There are two pairs of methods for sending/receiving messages:
+#
+# * Ractor#send and Ractor.receive for when the _sender_ knows the receiver (push);
+# * Ractor.yield and Ractor#take for when the _receiver_ knows the sender (pull);
+#
+# In addition to that, an argument to Ractor.new would be passed to block and available there
+# as if received by Ractor.receive, and the last block value would be sent outside of the
+# ractor as if sent by Ractor.yield.
+#
+# A little demonstration on a classic ping-pong:
+#
+#     server = Ractor.new do
+#       puts "Server starts: #{self.inspect}"
+#       puts "Server sends: ping"
+#       Ractor.yield 'ping'                       # The server doesn't know the receiver and sends to whoever interested
+#       received = Ractor.receive                 # The server doesn't know the sender and receives from whoever sent
+#       puts "Server received: #{received}"
+#     end
+#
+#     client = Ractor.new(server) do |srv|        # The server is sent inside client, and available as srv
+#       puts "Client starts: #{self.inspect}"
+#       received = srv.take                       # The Client takes a message specifically from the server
+#       puts "Client received from " \
+#            "#{srv.inspect}: #{received}"
+#       puts "Client sends to " \
+#            "#{srv.inspect}: pong"
+#       srv.send 'pong'                           # The client sends a message specifically to the server
+#     end
+#
+#     [client, server].each(&:take)               # Wait till they both finish
+#
+# This will output:
+#
+#     Server starts: #<Ractor:#2 test.rb:1 running>
+#     Server sends: ping
+#     Client starts: #<Ractor:#3 test.rb:8 running>
+#     Client received from #<Ractor:#2 rac.rb:1 blocking>: ping
+#     Client sends to #<Ractor:#2 rac.rb:1 blocking>: pong
+#     Server received: pong
+#
+# It is said that Ractor receives messages via the <em>incoming port</em>, and sends them
+# to the <em>outgoing port</em>. Either one can be disabled with Ractor#close_incoming and
+# Ractor#close_outgoing respectively.
+#
+# == Shareable and unshareable objects
+#
+# When the object is sent to and from the ractor, it is important to understand whether the
+# object is shareable or not. Shareable objects are basically those which can be used by several
+# threads without compromising thread-safety; e.g. immutable ones. Ractor.shareable? allows to
+# check this, and Ractor.make_shareable tries to make object shareable if it is not.
+#
+#     Ractor.shareable?(1)            #=> true -- numbers and other immutable basic values are
+#     Ractor.shareable?('foo')        #=> false, unless the string is frozen due to # freeze_string_literals: true
+#     Ractor.shareable?('foo'.freeze) #=> true
+#
+#     ary = ['hello', 'world']
+#     ary.frozen?                 #=> false
+#     ary[0].frozen?              #=> false
+#     Ractor.make_shareable(ary)
+#     ary.frozen?                 #=> true
+#     ary[0].frozen?              #=> true
+#     ary[1].frozen?              #=> true
+#
+# When a shareable object is sent (via #send or Ractor.yield), no additional processing happens,
+# and it just becomes usable by both ractors. When an unshareable object is sent, it can be
+# either _copied_ or _moved_. The first is the default, and it makes the object's full copy by
+# deep cloning of non-shareable parts of its structure.
+#
+#     data = ['foo', 'bar'.freeze]
+#     r = Ractor.new do
+#       data2 = Ractor.receive
+#       puts "In ractor: #{data2.object_id}, #{data2[0].object_id}, #{data2[1].object_id}"
+#     end
+#     r.send(data)
+#     r.take
+#     puts "Outside  : #{data.object_id}, #{data[0].object_id}, #{data[1].object_id}"
+#
+# This will output:
+#
+#     In ractor: 340, 360, 320
+#     Outside  : 380, 400, 320
+#
+# (Note that object id of both array and non-frozen string inside array have changed inside
+# the ractor, showing it is different objects. But the second array's element, which is a
+# shareable frozen string, has the same object_id.)
+#
+# Deep cloning of the data may be slow, and sometimes impossible. Alternatively,
+# <tt>move: true</tt> may be used on sending. This will <em>move</em> the object to the
+# receiving ractor, making it inaccessible for a sending ractor.
+#
+#     data = ['foo', 'bar']
+#     r = Ractor.new do
+#       data_in_ractor = Ractor.receive
+#       puts "In ractor: #{data_in_ractor.object_id}, #{data_in_ractor[0].object_id}"
+#     end
+#     r.send(data, move: true)
+#     r.take
+#     puts "Outside: moved? #{Ractor::MovedObject === data}"
+#     puts "Outside: #{data.inspect}"
+#
+# This will output:
+#
+#     In ractor: 100, 120
+#     Outside: moved? true
+#     test.rb:9:in `method_missing': can not send any methods to a moved object (Ractor::MovedError)
+#
+# Notice that even +inspect+ (and more basic methods like <tt>__id__</tt>) is inaccessible
+# on a moved object.
+#
+# Besides frozen objects, shareable objects a (... truncated)

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]