Project

General

Profile

Actions

Feature #20875

open

Atomic initialization for Ractor local storage

Added by ko1 (Koichi Sasada) 28 days ago. Updated about 7 hours ago.

Status:
Open
Assignee:
-
Target version:
-
[ruby-core:119769]

Description

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.


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):

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:

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

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0