D7net
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
proc
/
self
/
root
/
opt
/
alt
/
ruby34
/
share
/
ri
/
system
/
Ractor
/
Filename :
cdesc-Ractor.ri
back
Copy
U:RDoc::NormalClass[iI"Ractor:ET@I"Object;To:RDoc::Markup::Document:@parts[o;;[ : @fileI" ractor.c;T:0@omit_headings_from_table_of_contents_below0o;;[So:RDoc::Markup::Paragraph;[I"a\Ractor is an Actor-model abstraction for Ruby that provides thread-safe parallel execution.;To:RDoc::Markup::BlankLine o;;[I"?Ractor.new makes a new \Ractor, which can run in parallel.;T@o:RDoc::Markup::Verbatim;[ I"# The simplest ractor ;TI"-r = Ractor.new {puts "I am in Ractor!"} ;TI"$r.take # wait for it to finish ;TI"*# Here, "I am in Ractor!" is printed ;T:@format0o;;[I"zRactors do not share all objects with each other. There are two main benefits to this: across ractors, thread-safety ;TI"hconcerns such as data-races and race-conditions are not possible. The other benefit is parallelism.;T@o;;[I"@To achieve this, object sharing is limited across ractors. ;TI"|For example, unlike in threads, ractors can't access all the objects available in other ractors. Even objects normally ;TI"bavailable through variables in the outer scope are prohibited from being used across ractors.;T@o; ;[ I"a = 1 ;TI"4r = Ractor.new {puts "I am in Ractor! a=#{a}"} ;TI"# fails immediately with ;TI"W# ArgumentError (can not isolate a Proc because it accesses outer variables (a).) ;T;0o;;[I"*The object must be explicitly shared:;To; ;[I"a = 1 ;TI">r = Ractor.new(a) { |a1| puts "I am in Ractor! a=#{a1}"} ;T;0o;;[I"eOn CRuby (the default implementation), Global Virtual Machine Lock (GVL) is held per ractor, so ;TI"kractors can perform in parallel without locking each other. This is unlike the situation with threads ;TI"on CRuby.;T@o;;[I"XInstead of accessing shared state, objects should be passed to and from ractors by ;TI",sending and receiving them as messages.;T@o; ;[ I"a = 1 ;TI"r = Ractor.new do ;TI"N a_in_ractor = receive # receive blocks until somebody passes a message ;TI"/ puts "I am in Ractor! a=#{a_in_ractor}" ;TI" end ;TI"r.send(a) # pass it ;TI"r.take ;TI".# Here, "I am in Ractor! a=1" is printed ;T;0o;;[I"CThere are two pairs of methods for sending/receiving messages:;T@o:RDoc::Markup::List: @type:BULLET:@items[o:RDoc::Markup::ListItem:@label0;[o;;[I"TRactor#send and Ractor.receive for when the _sender_ knows the receiver (push);;To;;0;[o;;[I"RRactor.yield and Ractor#take for when the _receiver_ knows the sender (pull);;T@o;;[I"iIn addition to that, any arguments passed to Ractor.new are passed to the block and available there ;TI"Was if received by Ractor.receive, and the last block value is sent outside of the ;TI"'ractor as if sent by Ractor.yield.;T@o;;[I"3A little demonstration of a classic ping-pong:;T@o; ;[I",server = Ractor.new(name: "server") do ;TI"- puts "Server starts: #{self.inspect}" ;TI"! puts "Server sends: ping" ;TI"x Ractor.yield 'ping' # The server doesn't know the receiver and sends to whoever interested ;TI"u received = Ractor.receive # The server doesn't know the sender and receives from whoever sent ;TI"+ puts "Server received: #{received}" ;TI" end ;TI" ;TI"jclient = Ractor.new(server) do |srv| # The server is sent to the client, and available as srv ;TI"- puts "Client starts: #{self.inspect}" ;TI"^ received = srv.take # The client takes a message from the server ;TI"& puts "Client received from " \ ;TI"* "#{srv.inspect}: #{received}" ;TI"! puts "Client sends to " \ ;TI"# "#{srv.inspect}: pong" ;TI"\ srv.send 'pong' # The client sends a message to the server ;TI" end ;TI" ;TI"O[client, server].each(&:take) # Wait until they both finish ;T;0o;;[I"%This will output something like:;T@o; ;[I":Server starts: #<Ractor:#2 server test.rb:1 running> ;TI"Server sends: ping ;TI"3Client starts: #<Ractor:#3 test.rb:8 running> ;TI"GClient received from #<Ractor:#2 server test.rb:1 blocking>: ping ;TI"BClient sends to #<Ractor:#2 server test.rb:1 blocking>: pong ;TI"Server received: pong ;T;0o;;[ I"RRactors receive their messages via the <em>incoming port</em>, and send them ;TI"^to the <em>outgoing port</em>. Either one can be disabled with Ractor#close_incoming and ;TI"YRactor#close_outgoing, respectively. When a ractor terminates, its ports are closed ;TI"automatically.;T@S:RDoc::Markup::Heading: leveli: textI"&Shareable and unshareable objects;T@o;;[ I"[When an object is sent to and from a ractor, it's important to understand whether the ;TI"Yobject is shareable or unshareable. Most Ruby objects are unshareable objects. Even ;TI"cfrozen objects can be unshareable if they contain (through their instance variables) unfrozen ;TI" objects.;T@o;;[ I"[Shareable objects are those which can be used by several threads without compromising ;TI"ithread-safety, for example numbers, +true+ and +false+. Ractor.shareable? allows you to check this, ;TI"jand Ractor.make_shareable tries to make the object shareable if it's not already, and gives an error ;TI"if it can't do it.;T@o; ;[I"hRactor.shareable?(1) #=> true -- numbers and other immutable basic values are shareable ;TI"qRactor.shareable?('foo') #=> false, unless the string is frozen due to # frozen_string_literal: true ;TI".Ractor.shareable?('foo'.freeze) #=> true ;TI"PRactor.shareable?([Object.new].freeze) #=> false, inner object is unfrozen ;TI" ;TI"ary = ['hello', 'world'] ;TI"+ary.frozen? #=> false ;TI"+ary[0].frozen? #=> false ;TI" Ractor.make_shareable(ary) ;TI"*ary.frozen? #=> true ;TI"*ary[0].frozen? #=> true ;TI"*ary[1].frozen? #=> true ;T;0o;;[ I"bWhen a shareable object is sent (via #send or Ractor.yield), no additional processing occurs ;TI"bon it. It just becomes usable by both ractors. When an unshareable object is sent, it can be ;TI"]either _copied_ or _moved_. The first is the default, and it copies the object fully by ;TI"Jdeep cloning (Object#clone) the non-shareable parts of its structure.;T@o; ;[ I""data = ['foo', 'bar'.freeze] ;TI"r = Ractor.new do ;TI" data2 = Ractor.receive ;TI"Z puts "In ractor: #{data2.object_id}, #{data2[0].object_id}, #{data2[1].object_id}" ;TI" end ;TI"r.send(data) ;TI"r.take ;TI"Uputs "Outside : #{data.object_id}, #{data[0].object_id}, #{data[1].object_id}" ;T;0o;;[I"%This will output something like:;T@o; ;[I"In ractor: 340, 360, 320 ;TI"Outside : 380, 400, 320 ;T;0o;;[I"fNote that the object ids of the array and the non-frozen string inside the array have changed in ;TI"[the ractor because they are different objects. The second array's element, which is a ;TI"1shareable frozen string, is the same object.;T@o;;[I"kDeep cloning of objects may be slow, and sometimes impossible. Alternatively, <tt>move: true</tt> may ;TI"obe used during sending. This will <em>move</em> the unshareable object to the receiving ractor, making it ;TI"(inaccessible to the sending ractor.;T@o; ;[I"data = ['foo', 'bar'] ;TI"r = Ractor.new do ;TI"' data_in_ractor = Ractor.receive ;TI"U puts "In ractor: #{data_in_ractor.object_id}, #{data_in_ractor[0].object_id}" ;TI" end ;TI"r.send(data, move: true) ;TI"r.take ;TI"<puts "Outside: moved? #{Ractor::MovedObject === data}" ;TI"%puts "Outside: #{data.inspect}" ;T;0o;;[I"This will output:;T@o; ;[I"In ractor: 100, 120 ;TI"Outside: moved? true ;TI"dtest.rb:9:in `method_missing': can not send any methods to a moved object (Ractor::MovedError) ;T;0o;;[I"^Notice that even +inspect+ (and more basic methods like <tt>__id__</tt>) is inaccessible ;TI"on a moved object.;T@o;;[I"hClass and Module objects are shareable so the class/module definitions are shared between ractors. ;TI"|\Ractor objects are also shareable. All operations on shareable objects are thread-safe, so the thread-safety property ;TI"lwill be kept. We can not define mutable shareable objects in Ruby, but C extensions can introduce them.;T@o;;[ I"tIt is prohibited to access (get) instance variables of shareable objects in other ractors if the values of the ;TI"ivariables aren't shareable. This can occur because modules/classes are shareable, but they can have ;TI"hinstance variables whose values are not. In non-main ractors, it's also prohibited to set instance ;TI"Cvariables on classes/modules (even if the value is shareable).;T@o; ;[I" class C ;TI" class << self ;TI" attr_accessor :tricky ;TI" end ;TI" end ;TI" ;TI""C.tricky = "unshareable".dup ;TI" ;TI" r = Ractor.new(C) do |cls| ;TI" puts "I see #{cls}" ;TI"( puts "I can't see #{cls.tricky}" ;TI"P cls.tricky = true # doesn't get here, but this would also raise an error ;TI" end ;TI"r.take ;TI"# I see C ;TI"a# can not access instance variables of classes/modules from non-main Ractors (RuntimeError) ;T;0o;;[I"cRactors can access constants if they are shareable. The main \Ractor is the only one that can ;TI"$access non-shareable constants.;T@o; ;[I"GOOD = 'good'.freeze ;TI"BAD = 'bad'.dup ;TI" ;TI"r = Ractor.new do ;TI" puts "GOOD=#{GOOD}" ;TI" puts "BAD=#{BAD}" ;TI" end ;TI"r.take ;TI"# GOOD=good ;TI"d# can not access non-shareable objects in constant Object::BAD by non-main Ractor. (NameError) ;TI" ;TI",# Consider the same C class from above ;TI" ;TI"r = Ractor.new do ;TI" puts "I see #{C}" ;TI"& puts "I can't see #{C.tricky}" ;TI" end ;TI"r.take ;TI"# I see C ;TI"a# can not access instance variables of classes/modules from non-main Ractors (RuntimeError) ;T;0o;;[I"OSee also the description of <tt># shareable_constant_value</tt> pragma in ;TI"B{Comments syntax}[rdoc-ref:syntax/comments.rdoc] explanation.;T@S;;i;I"Ractors vs threads;T@o;;[I"YEach ractor has its own main Thread. New threads can be created from inside ractors ;TI"K(and, on CRuby, they share the GVL with other threads of this ractor).;T@o; ;[I"r = Ractor.new do ;TI" a = 1 ;TI"9 Thread.new {puts "Thread in ractor: a=#{a}"}.join ;TI" end ;TI"r.take ;TI"4# Here "Thread in ractor: a=1" will be printed ;T;0S;;i;I"Note on code examples;T@o;;[I"[In the examples below, sometimes we use the following method to wait for ractors that ;TI"?are not currently blocked to finish (or to make progress).;T@o; ;[I"def wait ;TI" sleep(0.1) ;TI" end ;T;0o;;[I"UIt is **only for demonstration purposes** and shouldn't be used in a real code. ;TI"CMost of the time, #take is used to wait for ractors to finish.;T@S;;i;I"Reference;T@o;;[I"BSee {Ractor design doc}[rdoc-ref:ractor.md] for more details.;T; I"ractor.rb;T; 0; 0; 0[ [ [ [[I" class;T[[:public[[I"[];TI"ractor.rb;T[I"[]=;T@;[I" count;T@;[I"current;T@;[I" main;T@;[I" main?;T@;[I"make_shareable;T@;[I"new;T@;[I"receive;T@;[I"receive_if;T@;[I" recv;T@;[I"select;T@;[I"shareable?;T@;[I"store_if_absent;T@;[I" yield;T@;[:protected[ [:private[ [I" instance;T[[;[[I"<<;T@;[I"[];T@;[I"[]=;T@;[I"close_incoming;T@;[I"close_outgoing;T@;[I"inspect;T@;[I" name;T@;[I" recv;T@;[I" send;T@;[I" take;T@;[I" to_s;T@;[;[ [;[[I"receive;T@;[I"receive_if;T@;[ [U:RDoc::Context::Section[i 0o;;[ ; 0; 0[@ @/@/cRDoc::TopLevel