From c34c6a89cb7524050dc6f174da53a8159a7bbd64 Mon Sep 17 00:00:00 2001 From: Koichi Sasada Date: Tue, 22 Dec 2020 01:05:52 +0900 Subject: fix ractor's doc. [ci skip] --- ractor.c | 20 ++++++++++++++------ ractor.rb | 53 ++++++++++++++++++++++++++++++++++++----------------- 2 files changed, 50 insertions(+), 23 deletions(-) diff --git a/ractor.c b/ractor.c index 5937ac65bb..35fbc89a29 100644 --- a/ractor.c +++ b/ractor.c @@ -1935,16 +1935,25 @@ ractor_moved_missing(int argc, VALUE *argv, VALUE self) * 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 } } + * r = Ractor.new do + * loop do + * msg = receive # raises ClosedError and loop traps it + * puts "Received: #{msg}" + * end + * puts "loop exited" + * end * - * loop { r.send "test" } + * 3.times{|i| r << i} + * r.close_incoming + * r.take * puts "Continue successfully" * * This will print: * - * Received: test - * Received: test - * Received: test + * Received: 0 + * Received: 1 + * Received: 2 + * loop exited * Continue successfully */ @@ -2004,7 +2013,6 @@ ractor_moved_missing(int argc, VALUE *argv, VALUE self) * */ - void Init_Ractor(void) { diff --git a/ractor.rb b/ractor.rb index 0a152425f0..cac18d44b9 100644 --- a/ractor.rb +++ b/ractor.rb @@ -1,14 +1,19 @@ # 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. +# Ractor.new can make new Ractor and it will run in parallel. # # # The simplest ractor # r = Ractor.new {puts "I am in Ractor!"} -# r.take # allow it to finish +# r.take # wait it to finish # # here "I am in Ractor!" would be printed # +# Ractors do not share usual objects, so the some kind of thread-safety concerns such as data-race, +# race-conditions are not available on multi-ractor programming. +# +# To achieve this, ractors severely limit object sharing between different ractors. +# For example, unlike threads, ractors can't access each other's objects, nor any objects through +# variables of the outer scope. +# # a = 1 # r = Ractor.new {puts "I am in Ractor! a=#{a}"} # # fails immediately with @@ -17,8 +22,8 @@ # 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"). +# Instead of accessing the shared state, the objects should be passed to and from ractors via +# sending and receiving objects as messages. # # a = 1 # r = Ractor.new do @@ -71,14 +76,17 @@ # # It is said that Ractor receives messages via the incoming port, and sends them # to the outgoing port. Either one can be disabled with Ractor#close_incoming and -# Ractor#close_outgoing respectively. +# Ractor#close_outgoing respectively. If a ractor terminated, its ports will be closed +# automatically. # # == 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. +# object is shareable or unshareable. Most of objects are unshareable objects. +# +# 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 @@ -115,7 +123,7 @@ # 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, +# Deep cloning of the objects may be slow, and sometimes impossible. Alternatively, # move: true may be used on sending. This will move the object to the # receiving ractor, making it inaccessible for a sending ractor. # @@ -138,8 +146,13 @@ # Notice that even +inspect+ (and more basic methods like __id__) is inaccessible # on a moved object. # -# Besides frozen objects, shareable objects are instances of Module, Class and Ractor itself. Modules -# and classes, though, can not access instance variables from ractors other than main: +# Besides frozen objects, there are shareable objects. Class and Module objects are shareable so +# the Class/Module definitons are shared between ractors. Ractor objects are also shareable objects. +# All operations for the shareable mutable objects are thread-safe, so the thread-safety property +# will be kept. We can not define mutable sharable objects in Ruby, but C extensions can introduce them. +# +# It is prohibited to access instance variables of mutable shareable objects (especially Modules and classes) +# from ractors other than main: # # class C # class << self @@ -186,8 +199,8 @@ # # == Ractors vs threads # -# Each ractor creates its own thread. New threads can be created from inside ractor, having the full -# access to its data (and, on CRuby, sharing GVL with other threads of this ractor). +# Each ractor creates its own thread. New threads can be created from inside ractor +# (and, on CRuby, sharing GVL with other threads of this ractor). # # r = Ractor.new do # a = 1 @@ -208,6 +221,10 @@ # It is **only for demonstration purposes** and shouldn't be used in a real code. # Most of the times, just #take is used to wait till ractor will finish. # +# == Reference +# +# See {Ractor desgin doc}[rdoc-ref:doc/ractor.md] for more details. +# class Ractor # Create a new Ractor with args and a block. # @@ -258,8 +275,9 @@ class Ractor # Returns total count of Ractors currently running. # # Ractor.count #=> 1 - # r = Ractor.new(name: 'example') {sleep(0.1)} + # r = Ractor.new(name: 'example') { Ractor.yield(1) } # Ractor.count #=> 2 (main + example ractor) + # r.take # wait for Ractor.yield(1) # r.take # wait till r will finish # Ractor.count #=> 1 def self.count @@ -374,7 +392,8 @@ class Ractor # Still received only one # Received: message2 # - # If close_incoming was called on the ractor, the method raises Ractor::ClosedError: + # If close_incoming was called on the ractor, the method raises Ractor::ClosedError + # if there are no more messages in incoming queue: # # Ractor.new do # close_incoming -- cgit v1.2.1