ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
* [ruby-core:119769] [Ruby master Feature#20875] Atomic initialization for Ractor local storage
@ 2024-11-06  8:28 ko1 (Koichi Sasada) via ruby-core
  2024-11-28  7:48 ` [ruby-core:120037] " ko1 (Koichi Sasada) via ruby-core
                   ` (10 more replies)
  0 siblings, 11 replies; 12+ messages in thread
From: ko1 (Koichi Sasada) via ruby-core @ 2024-11-06  8:28 UTC (permalink / raw)
  To: ruby-core; +Cc: ko1 (Koichi Sasada)

Issue #20875 has been reported by ko1 (Koichi Sasada).

----------------------------------------
Feature #20875: Atomic initialization for Ractor local storage
https://bugs.ruby-lang.org/issues/20875

* Author: ko1 (Koichi Sasada)
* Status: Open
----------------------------------------
## 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 are 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.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [ruby-core:120037] [Ruby master Feature#20875] Atomic initialization for Ractor local storage
  2024-11-06  8:28 [ruby-core:119769] [Ruby master Feature#20875] Atomic initialization for Ractor local storage ko1 (Koichi Sasada) via ruby-core
@ 2024-11-28  7:48 ` ko1 (Koichi Sasada) via ruby-core
  2024-12-02 16:15 ` [ruby-core:120076] " Dan0042 (Daniel DeLorme) via ruby-core
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: ko1 (Koichi Sasada) via ruby-core @ 2024-11-28  7:48 UTC (permalink / raw)
  To: ruby-core; +Cc: ko1 (Koichi Sasada)

Issue #20875 has been updated by ko1 (Koichi Sasada).


@matz how about `Ractor.local_storage_once(key){ ... }`? It is from [pthread_once](https://man7.org/linux/man-pages/man3/pthread_once.3p.html).

`Ractor.once(key){ ... }` seems too short?


----------------------------------------
Feature #20875: Atomic initialization for Ractor local storage
https://bugs.ruby-lang.org/issues/20875#change-110775

* Author: ko1 (Koichi Sasada)
* Status: Open
----------------------------------------
## 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




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [ruby-core:120076] [Ruby master Feature#20875] Atomic initialization for Ractor local storage
  2024-11-06  8:28 [ruby-core:119769] [Ruby master Feature#20875] Atomic initialization for Ractor local storage ko1 (Koichi Sasada) via ruby-core
  2024-11-28  7:48 ` [ruby-core:120037] " ko1 (Koichi Sasada) via ruby-core
@ 2024-12-02 16:15 ` Dan0042 (Daniel DeLorme) via ruby-core
  2024-12-04  1:49 ` [ruby-core:120090] " ko1 (Koichi Sasada) via ruby-core
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Dan0042 (Daniel DeLorme) via ruby-core @ 2024-12-02 16:15 UTC (permalink / raw)
  To: ruby-core; +Cc: Dan0042 (Daniel DeLorme)

Issue #20875 has been updated by Dan0042 (Daniel DeLorme).


Would it be possible to make `Ractor[:mtx] ||= Mutex.new` behave in an atomic way?  Like maybe add a special `[]||=` method which is automatically called such that `Ractor[:mtx] ||= Mutex.new` becomes equivalent to `Ractor[:mtx] || Ractor.send(:"[]||=", :mtx, Mutex.new)`

I'm just throwing out the general idea here, because it would be nice to use common ruby idioms instead of yet another special API to handle concurrent behavior.

----------------------------------------
Feature #20875: Atomic initialization for Ractor local storage
https://bugs.ruby-lang.org/issues/20875#change-110819

* Author: ko1 (Koichi Sasada)
* Status: Open
----------------------------------------
## 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




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [ruby-core:120090] [Ruby master Feature#20875] Atomic initialization for Ractor local storage
  2024-11-06  8:28 [ruby-core:119769] [Ruby master Feature#20875] Atomic initialization for Ractor local storage ko1 (Koichi Sasada) via ruby-core
  2024-11-28  7:48 ` [ruby-core:120037] " ko1 (Koichi Sasada) via ruby-core
  2024-12-02 16:15 ` [ruby-core:120076] " Dan0042 (Daniel DeLorme) via ruby-core
@ 2024-12-04  1:49 ` ko1 (Koichi Sasada) via ruby-core
  2024-12-04 16:09 ` [ruby-core:120102] " Dan0042 (Daniel DeLorme) via ruby-core
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: ko1 (Koichi Sasada) via ruby-core @ 2024-12-04  1:49 UTC (permalink / raw)
  To: ruby-core; +Cc: ko1 (Koichi Sasada)

Issue #20875 has been updated by ko1 (Koichi Sasada).


Dan0042 (Daniel DeLorme) wrote in #note-6:
> Would it be possible to make `Ractor[:mtx] ||= Mutex.new` behave in an atomic way?

On `x[y()] ||= z()`, `z()` can change the context and it violates atomicity.

----------------------------------------
Feature #20875: Atomic initialization for Ractor local storage
https://bugs.ruby-lang.org/issues/20875#change-110837

* Author: ko1 (Koichi Sasada)
* Status: Open
----------------------------------------
## 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




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [ruby-core:120102] [Ruby master Feature#20875] Atomic initialization for Ractor local storage
  2024-11-06  8:28 [ruby-core:119769] [Ruby master Feature#20875] Atomic initialization for Ractor local storage ko1 (Koichi Sasada) via ruby-core
                   ` (2 preceding siblings ...)
  2024-12-04  1:49 ` [ruby-core:120090] " ko1 (Koichi Sasada) via ruby-core
@ 2024-12-04 16:09 ` Dan0042 (Daniel DeLorme) via ruby-core
  2024-12-08  6:51 ` [ruby-core:120131] " ko1 (Koichi Sasada) via ruby-core
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Dan0042 (Daniel DeLorme) via ruby-core @ 2024-12-04 16:09 UTC (permalink / raw)
  To: ruby-core; +Cc: Dan0042 (Daniel DeLorme)

Issue #20875 has been updated by Dan0042 (Daniel DeLorme).


ko1 (Koichi Sasada) wrote in #note-7:
> On `x[y()] ||= z()`, `z()` can change the context and it violates atomicity.

Hmm, I don't think so? Of course for the regular `||=` that would be the case, but in something like `x.assign_if_unset(k(), v())` I don't see how `v()` can violate atomicity. In the worst case the result of `v()` would be thrown away.

But I'm talking here about a fairly general mechanism for atomic operations, so perhaps it's out of scope for this ticket.


----------------------------------------
Feature #20875: Atomic initialization for Ractor local storage
https://bugs.ruby-lang.org/issues/20875#change-110850

* Author: ko1 (Koichi Sasada)
* Status: Open
----------------------------------------
## 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




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [ruby-core:120131] [Ruby master Feature#20875] Atomic initialization for Ractor local storage
  2024-11-06  8:28 [ruby-core:119769] [Ruby master Feature#20875] Atomic initialization for Ractor local storage ko1 (Koichi Sasada) via ruby-core
                   ` (3 preceding siblings ...)
  2024-12-04 16:09 ` [ruby-core:120102] " Dan0042 (Daniel DeLorme) via ruby-core
@ 2024-12-08  6:51 ` ko1 (Koichi Sasada) via ruby-core
  2024-12-09 12:09 ` [ruby-core:120137] " Eregon (Benoit Daloze) via ruby-core
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: ko1 (Koichi Sasada) via ruby-core @ 2024-12-08  6:51 UTC (permalink / raw)
  To: ruby-core; +Cc: ko1 (Koichi Sasada)

Issue #20875 has been updated by ko1 (Koichi Sasada).


Matz said that `Ractor.local_storage_once(key){ init_block }` can be acceptable if returning the assigned value is out-of-scope, even if it returns assigned value.

other ideas:

* `Ractor.local_storage_fetch(key){}` from `Hash#fetch{}`, but `Hash#fetch` doesn't set the block value, so it can lead wrong understanding.
* `Ractor.local_storage_safe_init(key){}` (by mame)
  * `Ractor.local_storage_thread_safe_init(key){}`
  * a bit long?


----------------------------------------
Feature #20875: Atomic initialization for Ractor local storage
https://bugs.ruby-lang.org/issues/20875#change-110881

* Author: ko1 (Koichi Sasada)
* Status: Open
----------------------------------------
## 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




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [ruby-core:120137] [Ruby master Feature#20875] Atomic initialization for Ractor local storage
  2024-11-06  8:28 [ruby-core:119769] [Ruby master Feature#20875] Atomic initialization for Ractor local storage ko1 (Koichi Sasada) via ruby-core
                   ` (4 preceding siblings ...)
  2024-12-08  6:51 ` [ruby-core:120131] " ko1 (Koichi Sasada) via ruby-core
@ 2024-12-09 12:09 ` Eregon (Benoit Daloze) via ruby-core
  2024-12-12  4:26 ` [ruby-core:120188] " ko1 (Koichi Sasada) via ruby-core
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Eregon (Benoit Daloze) via ruby-core @ 2024-12-09 12:09 UTC (permalink / raw)
  To: ruby-core; +Cc: Eregon (Benoit Daloze)

Issue #20875 has been updated by Eregon (Benoit Daloze).


ko1 (Koichi Sasada) wrote in #note-9:
> can be acceptable if returning the assigned value is out-of-scope, even if it returns assigned value.

What does this mean?
It seems clear such a method should return the same as `Ractor[key]`, after potentially computing + storing the value.

BTW, it sounds like `Map#computeIfAbsent` in Java and `Concurrent::Map#compute_if_absent` in concurrent-ruby.
Maybe Ruby should have `Concurrent::Map` built-in, or `Hash#compute_if_absent` built-in.
Then each Ractor could have such a map/hash and this could be solved like `Ractor[:mtx] ||= Ractor.map.compute_if_absent(:mtx) { Mutex.new }`.
In fact maybe that map could be the storage of ractor-local variables, and then `Ractor.map.compute_if_absent(:mtx) { Mutex.new }` is enough.

----------------------------------------
Feature #20875: Atomic initialization for Ractor local storage
https://bugs.ruby-lang.org/issues/20875#change-110886

* Author: ko1 (Koichi Sasada)
* Status: Open
----------------------------------------
## 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




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [ruby-core:120188] [Ruby master Feature#20875] Atomic initialization for Ractor local storage
  2024-11-06  8:28 [ruby-core:119769] [Ruby master Feature#20875] Atomic initialization for Ractor local storage ko1 (Koichi Sasada) via ruby-core
                   ` (5 preceding siblings ...)
  2024-12-09 12:09 ` [ruby-core:120137] " Eregon (Benoit Daloze) via ruby-core
@ 2024-12-12  4:26 ` ko1 (Koichi Sasada) via ruby-core
  2024-12-12  7:17 ` [ruby-core:120199] " ko1 (Koichi Sasada) via ruby-core
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: ko1 (Koichi Sasada) via ruby-core @ 2024-12-12  4:26 UTC (permalink / raw)
  To: ruby-core; +Cc: ko1 (Koichi Sasada)

Issue #20875 has been updated by ko1 (Koichi Sasada).


> Then each Ractor could have such a map/hash and this could be solved like `Ractor[:mtx] ||= Ractor.map.compute_if_absent(:mtx) { Mutex.new }`.

`Ractor[:mtx] ||= ` is needed?



----------------------------------------
Feature #20875: Atomic initialization for Ractor local storage
https://bugs.ruby-lang.org/issues/20875#change-110948

* Author: ko1 (Koichi Sasada)
* Status: Open
----------------------------------------
## 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




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [ruby-core:120199] [Ruby master Feature#20875] Atomic initialization for Ractor local storage
  2024-11-06  8:28 [ruby-core:119769] [Ruby master Feature#20875] Atomic initialization for Ractor local storage ko1 (Koichi Sasada) via ruby-core
                   ` (6 preceding siblings ...)
  2024-12-12  4:26 ` [ruby-core:120188] " ko1 (Koichi Sasada) via ruby-core
@ 2024-12-12  7:17 ` ko1 (Koichi Sasada) via ruby-core
  2024-12-12 19:58 ` [ruby-core:120224] " ko1 (Koichi Sasada) via ruby-core
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: ko1 (Koichi Sasada) via ruby-core @ 2024-12-12  7:17 UTC (permalink / raw)
  To: ruby-core; +Cc: ko1 (Koichi Sasada)

Issue #20875 has been updated by ko1 (Koichi Sasada).


Ah, it assigs from `Ractor.map`.

----------------------------------------
Feature #20875: Atomic initialization for Ractor local storage
https://bugs.ruby-lang.org/issues/20875#change-110958

* Author: ko1 (Koichi Sasada)
* Status: Open
----------------------------------------
## 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




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [ruby-core:120224] [Ruby master Feature#20875] Atomic initialization for Ractor local storage
  2024-11-06  8:28 [ruby-core:119769] [Ruby master Feature#20875] Atomic initialization for Ractor local storage ko1 (Koichi Sasada) via ruby-core
                   ` (7 preceding siblings ...)
  2024-12-12  7:17 ` [ruby-core:120199] " ko1 (Koichi Sasada) via ruby-core
@ 2024-12-12 19:58 ` ko1 (Koichi Sasada) via ruby-core
  2024-12-12 21:02 ` [ruby-core:120225] " Dan0042 (Daniel DeLorme) via ruby-core
  2024-12-12 21:17 ` [ruby-core:120226] " ko1 (Koichi Sasada) via ruby-core
  10 siblings, 0 replies; 12+ messages in thread
From: ko1 (Koichi Sasada) via ruby-core @ 2024-12-12 19:58 UTC (permalink / raw)
  To: ruby-core; +Cc: ko1 (Koichi Sasada)

Issue #20875 has been updated by ko1 (Koichi Sasada).


quote from https://github.com/ruby/dev-meeting-log/blob/master/2024/DevMeeting-2024-12-12.md

* ko1: `compute_if_absent` terminology is introduced.
* matz: `compute` is not known in Ruby world. `Ractor.store_if_absent(key){ init_block }` is aceptable. I prefer than `init`/`once`.
* ko1`Ractor.local_storage_store_if_absent(key){ init_block }` (local_storage) is not needed?
* matz: too long.
* mame: atomicity (thread-safety) is not represented with this name. Is it okay?
* matz: no problem.

Conclusion:

* matz: Accept `Ractor.store_if_absent(key){ init_block }`


----------------------------------------
Feature #20875: Atomic initialization for Ractor local storage
https://bugs.ruby-lang.org/issues/20875#change-110987

* Author: ko1 (Koichi Sasada)
* Status: Open
----------------------------------------
## 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




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [ruby-core:120225] [Ruby master Feature#20875] Atomic initialization for Ractor local storage
  2024-11-06  8:28 [ruby-core:119769] [Ruby master Feature#20875] Atomic initialization for Ractor local storage ko1 (Koichi Sasada) via ruby-core
                   ` (8 preceding siblings ...)
  2024-12-12 19:58 ` [ruby-core:120224] " ko1 (Koichi Sasada) via ruby-core
@ 2024-12-12 21:02 ` Dan0042 (Daniel DeLorme) via ruby-core
  2024-12-12 21:17 ` [ruby-core:120226] " ko1 (Koichi Sasada) via ruby-core
  10 siblings, 0 replies; 12+ messages in thread
From: Dan0042 (Daniel DeLorme) via ruby-core @ 2024-12-12 21:02 UTC (permalink / raw)
  To: ruby-core; +Cc: Dan0042 (Daniel DeLorme)

Issue #20875 has been updated by Dan0042 (Daniel DeLorme).


"store_if_absent" is fairly verbose; I should point out that "add" is a common name for this operation. For example there's Set#add, and the memcached ADD command.

----------------------------------------
Feature #20875: Atomic initialization for Ractor local storage
https://bugs.ruby-lang.org/issues/20875#change-110988

* Author: ko1 (Koichi Sasada)
* Status: Open
----------------------------------------
## 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




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [ruby-core:120226] [Ruby master Feature#20875] Atomic initialization for Ractor local storage
  2024-11-06  8:28 [ruby-core:119769] [Ruby master Feature#20875] Atomic initialization for Ractor local storage ko1 (Koichi Sasada) via ruby-core
                   ` (9 preceding siblings ...)
  2024-12-12 21:02 ` [ruby-core:120225] " Dan0042 (Daniel DeLorme) via ruby-core
@ 2024-12-12 21:17 ` ko1 (Koichi Sasada) via ruby-core
  10 siblings, 0 replies; 12+ messages in thread
From: ko1 (Koichi Sasada) via ruby-core @ 2024-12-12 21:17 UTC (permalink / raw)
  To: ruby-core; +Cc: ko1 (Koichi Sasada)

Issue #20875 has been updated by ko1 (Koichi Sasada).


store is from `Hash#store`.

----------------------------------------
Feature #20875: Atomic initialization for Ractor local storage
https://bugs.ruby-lang.org/issues/20875#change-110989

* Author: ko1 (Koichi Sasada)
* Status: Open
----------------------------------------
## 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




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2024-12-12 21:17 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-11-06  8:28 [ruby-core:119769] [Ruby master Feature#20875] Atomic initialization for Ractor local storage ko1 (Koichi Sasada) via ruby-core
2024-11-28  7:48 ` [ruby-core:120037] " ko1 (Koichi Sasada) via ruby-core
2024-12-02 16:15 ` [ruby-core:120076] " Dan0042 (Daniel DeLorme) via ruby-core
2024-12-04  1:49 ` [ruby-core:120090] " ko1 (Koichi Sasada) via ruby-core
2024-12-04 16:09 ` [ruby-core:120102] " Dan0042 (Daniel DeLorme) via ruby-core
2024-12-08  6:51 ` [ruby-core:120131] " ko1 (Koichi Sasada) via ruby-core
2024-12-09 12:09 ` [ruby-core:120137] " Eregon (Benoit Daloze) via ruby-core
2024-12-12  4:26 ` [ruby-core:120188] " ko1 (Koichi Sasada) via ruby-core
2024-12-12  7:17 ` [ruby-core:120199] " ko1 (Koichi Sasada) via ruby-core
2024-12-12 19:58 ` [ruby-core:120224] " ko1 (Koichi Sasada) via ruby-core
2024-12-12 21:02 ` [ruby-core:120225] " Dan0042 (Daniel DeLorme) via ruby-core
2024-12-12 21:17 ` [ruby-core:120226] " ko1 (Koichi Sasada) via ruby-core

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).