Project

General

Profile

Feature #20875

Updated by ko1 (Koichi Sasada) 18 days ago

## Motivation 

 Now there is no way to initialize Ractor local storage in multi-thread. 

 For example, if we want to introduce per-Ractor counter, which should be protected with a per-Ractor Mutex for multi-threading support. 

 ```ruby 

 def init 
   Ractor[:cnt] = 0 
   Ractor[:mtx] = Mutex.new 
 end 

 def inc 
   init unless Ractor[:cnt] 
   Ractor[:mtx].synchronize do 
     Ractor[:cnt] += 1 
   end 
 end 
 ``` 

 In this code, if `inc` was called on multiple threads, `init` can be called with multiple threads and `cnt` can not be synchronized correctly. 

 ## Proposal 

 Let's introduce `Ractor.local_storage_init(sym){ block }` to initialize values in Ractor local storage. 

 If there is no slot for `sym`, synchronize with per-Ractor mutex and call `block` and the slot will be filled with the evaluation with the block result. The return value of this method will be the filled value. 
 Otherwise, returning corresponding value will be returned. 

 The implementation is like that (in C): 

 ```ruby 
 class Ractor 
   def self.local_storage_init(sym) 
     Ractor.per_ractor_mutex.synchronize do 
       if Ractor.local_storage_has_key?(sym) 
         Ractor[:sym] 
       else 
         Ractor[:sym] = yield 
       end 
     end 
   end 
 end 
 ``` 

 The above examples will be rewritten with the following code: 

 ```ruby 
 def inc 
   Ractor.local_storage_init(:mtx) do 
     Ractor[:cnt] = 0 
     Mutex.new 
   end.synchronize do 
     Ractor[:cnt] += 1 
   end 
 end 
 ``` 

 ## Discussion 


 ### Approach 

 There is another approach like `pthread_atfork`, maybe like `Ractor.atcreate{ init }`. A library registers a callback which will be called when a new ractor is created. 
 However, there are many Ractors which don't use the library, so that `atcreate` can be huge overhead for Ractor creation. 

 ### Naming 

 I propose `local_storage_init`, but not sure it matches. 
 I also proposed `Ractor.local_variable_init(sym)`, but Matz said he doesn't like this naming because it should not be a "variable". 
 (there is a `Thread#thread_variable_get` method, though). 

 On another aspect, `lcoal_storage_init` seems it clears all of ractor local storage slots. 

 ### Reentrancy 

 This proposal uses `Mutex`, so it is not reentrant. I believe it should be simple and using Monitor is too much. 
 (but it is not big issue, though) 

 ## Implementation 

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

Back