ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
* [ruby-core:100816] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage
@ 2020-11-12 16:55 ko1
  2020-11-12 17:07 ` [ruby-core:100817] " ko1
                   ` (17 more replies)
  0 siblings, 18 replies; 19+ messages in thread
From: ko1 @ 2020-11-12 16:55 UTC (permalink / raw)
  To: ruby-core

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

----------------------------------------
Feature #17323: Ractor::LVar to provide ractor-local storage
https://bugs.ruby-lang.org/issues/17323

* Author: ko1 (Koichi Sasada)
* Status: Open
* Priority: Normal
----------------------------------------
Ruby supports thread and fiber local storage:

* `Thread#[sym]` provides *Fiber* local storage
* `Thread#thread_variable_get(sym)

These APIs can access other threads/fibers like that:

```ruby
th = Thread.new{
  Thread.current.thread_variable_set(:a, 10)
}
th.join
# access from main thread to child thread
p th.thread_variable_get(:a)
```

To make Ractor local storage, this kind of feature should not be allowed to protect isolation.

This ticket propose alternative API `Ractor::LVar` that allows to provide Ractor local variable.

```ruby
LV1 = Ractor::LVar.new

p LV1.value #=> nil # default value
LV1.value = 'hello' # can set unshareable objects because LVar is ractor local.

Ractor.new do
  LV1.value = 'world' # set Ractor local variable
end.take

p LV1.value #=> 'hello'


# Lvar.new can accept default_proc which should be isolated Proc.

LV2 = Ractor::LVar.new{ "x" * 4 }
p LV2.value #=> "xxxx"
LV2.value = 'yyy'

Ractor.new do
  p LV2.value #=> 'xxx'
end

p LV2.value #=> 'yyy'
```

This API doesn't support accessing from other ractors.

`Ractor::LVar` is from `Ractor::TVar`, but I have no strong opinion about it.
For example, `Ractor::LocalVariable` is longer and clearer.

Implementation: https://github.com/ruby/ruby/pull/3762




-- 
https://bugs.ruby-lang.org/

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

* [ruby-core:100817] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage
  2020-11-12 16:55 [ruby-core:100816] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage ko1
@ 2020-11-12 17:07 ` ko1
  2020-11-12 18:24 ` [ruby-core:100818] " marcandre-ruby-core
                   ` (16 subsequent siblings)
  17 siblings, 0 replies; 19+ messages in thread
From: ko1 @ 2020-11-12 17:07 UTC (permalink / raw)
  To: ruby-core

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


Another advantages compare with current API is,

* we don't need to care about variable name.
* we can make ractor-local constants and instance variables like that:

```ruby
class Fib
  attr_reader :cache

  def initialize
    @cache = Ractor::LVar.new{ {} }
  end

  private def _fib n
    if n < 2
      1
    else
      fib(n-1) + fib(n-2)
    end
  end

  def fib n
    if v = @cache.value[n]
      v
    else
      ans = _fib(n)
      @cache.value[n] = ans
    end
  end
end

fiboner = Fib.new
p fiboner.fib(10) #=> 89
pp fiboner.cache.value
#=> {1=>1, 0=>1, 2=>2, 3=>3, 4=>5, 5=>8, 6=>13, 7=>21, 8=>34, 9=>55, 10=>89}

Ractor.new fiboner do |f2|
  p f2.fib(5) #=> 8
  p f2.cache.value #=> {1=>1, 0=>1, 2=>2, 3=>3, 4=>5, 5=>8}
end.take
```


----------------------------------------
Feature #17323: Ractor::LVar to provide ractor-local storage
https://bugs.ruby-lang.org/issues/17323#change-88457

* Author: ko1 (Koichi Sasada)
* Status: Open
* Priority: Normal
----------------------------------------
Ruby supports thread and fiber local storage:

* `Thread#[sym]` provides *Fiber* local storage
* `Thread#thread_variable_get(sym)

These APIs can access other threads/fibers like that:

```ruby
th = Thread.new{
  Thread.current.thread_variable_set(:a, 10)
}
th.join
# access from main thread to child thread
p th.thread_variable_get(:a)
```

To make Ractor local storage, this kind of feature should not be allowed to protect isolation.

This ticket propose alternative API `Ractor::LVar` that allows to provide Ractor local variable.

```ruby
LV1 = Ractor::LVar.new

p LV1.value #=> nil # default value
LV1.value = 'hello' # can set unshareable objects because LVar is ractor local.

Ractor.new do
  LV1.value = 'world' # set Ractor local variable
end.take

p LV1.value #=> 'hello'


# Lvar.new can accept default_proc which should be isolated Proc.

LV2 = Ractor::LVar.new{ "x" * 4 }
p LV2.value #=> "xxxx"
LV2.value = 'yyy'

Ractor.new do
  p LV2.value #=> 'xxx'
end

p LV2.value #=> 'yyy'
```

This API doesn't support accessing from other ractors.

`Ractor::LVar` is from `Ractor::TVar`, but I have no strong opinion about it.
For example, `Ractor::LocalVariable` is longer and clearer.

Implementation: https://github.com/ruby/ruby/pull/3762




-- 
https://bugs.ruby-lang.org/

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

* [ruby-core:100818] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage
  2020-11-12 16:55 [ruby-core:100816] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage ko1
  2020-11-12 17:07 ` [ruby-core:100817] " ko1
@ 2020-11-12 18:24 ` marcandre-ruby-core
  2020-11-12 18:44 ` [ruby-core:100819] " ko1
                   ` (15 subsequent siblings)
  17 siblings, 0 replies; 19+ messages in thread
From: marcandre-ruby-core @ 2020-11-12 18:24 UTC (permalink / raw)
  To: ruby-core

Issue #17323 has been updated by marcandre (Marc-Andre Lafortune).


I'm curious for better use cases.

The above example is not very convincing:

- there's no much use in sending this object to a Ractor to start with
- deep-copying the cache is probably faster than having to recalculate part of it
- more importantly, this would probably be the wrong solution if we had `SharedHash`:

```ruby
class SharedHash
  def initialize(initial = {})
    @ractor = Ractor.new(initial) do |hash|
      loop do
        case Ractor.receive
        in [:read, key, default] then Ractor.yield(hash.fetch(key, default))
        in [:write, key, value]  then hash[key] = value
        in :inspect |
           :to_s |
           :to_h => cmd          then Ractor.yield(hash.send(cmd))
        else raise ArgumentError
        end
      end
    end
  end

  def [](key)
    @ractor << [:read, key, nil]
    @ractor.take
  end

  def []=(key, value)
    @ractor << [:write, key, value]
    value
  end

  def inspect
    @ractor << :inspect
    @ractor.take
  end
end

class Fib
  attr_reader :cache

  def initialize
    @cache = SharedHash.new
  end

  private def _fib n
    if n < 2
      1
    else
      fib(n-1) + fib(n-2)
    end
  end

  def fib n
    @cache[n] ||= _fib(n)
  end
end


fiboner = Fib.new
p fiboner.fib(10) #=> 89
pp fiboner.cache
#=> {1=>1, 0=>1, 2=>2, 3=>3, 4=>5, 5=>8, 6=>13, 7=>21, 8=>34, 9=>55, 10=>89}

Ractor.new fiboner do |f2|
  p f2.fib(5) #=> 8 already cached!gi
  p f2.cache #=> {1=>1, 0=>1, 2=>2, 3=>3, 4=>5, 5=>8, 6=>13, 7=>21, 8=>34, 9=>55, 10=>89}
end.take
```

If the "start from a clear cache" is actually the right idea, than a solution could look like:

```ruby
class Fib
  attr_reader :cache

  def initialize
    @cache = {}
  end

  def initialize_copy(_)
    @cache = {}
  end

  private def _fib n
    if n < 2
      1
    else
      fib(n-1) + fib(n-2)
    end
  end

  def fib n
    @cache[n] ||= _fib(n)
  end
end

fiboner = Fib.new
p fiboner.fib(10) #=> 89
pp fiboner.cache
#=> {1=>1, 0=>1, 2=>2, 3=>3, 4=>5, 5=>8, 6=>13, 7=>21, 8=>34, 9=>55, 10=>89}

Ractor.new fiboner.dup, move: true do |f2|
  p f2.fib(5) #=> 8
  p f2.cache.value #=> {1=>1, 0=>1, 2=>2, 3=>3, 4=>5, 5=>8}
end.take
```

Above does not work yet, depends on #17286...

It seems to me that having a way to define how to deep-copy an object might be important.

----------------------------------------
Feature #17323: Ractor::LVar to provide ractor-local storage
https://bugs.ruby-lang.org/issues/17323#change-88458

* Author: ko1 (Koichi Sasada)
* Status: Open
* Priority: Normal
----------------------------------------
Ruby supports thread and fiber local storage:

* `Thread#[sym]` provides *Fiber* local storage
* `Thread#thread_variable_get(sym)

These APIs can access other threads/fibers like that:

```ruby
th = Thread.new{
  Thread.current.thread_variable_set(:a, 10)
}
th.join
# access from main thread to child thread
p th.thread_variable_get(:a)
```

To make Ractor local storage, this kind of feature should not be allowed to protect isolation.

This ticket propose alternative API `Ractor::LVar` that allows to provide Ractor local variable.

```ruby
LV1 = Ractor::LVar.new

p LV1.value #=> nil # default value
LV1.value = 'hello' # can set unshareable objects because LVar is ractor local.

Ractor.new do
  LV1.value = 'world' # set Ractor local variable
end.take

p LV1.value #=> 'hello'


# Lvar.new can accept default_proc which should be isolated Proc.

LV2 = Ractor::LVar.new{ "x" * 4 }
p LV2.value #=> "xxxx"
LV2.value = 'yyy'

Ractor.new do
  p LV2.value #=> 'xxx'
end

p LV2.value #=> 'yyy'
```

This API doesn't support accessing from other ractors.

`Ractor::LVar` is from `Ractor::TVar`, but I have no strong opinion about it.
For example, `Ractor::LocalVariable` is longer and clearer.

Implementation: https://github.com/ruby/ruby/pull/3762




-- 
https://bugs.ruby-lang.org/

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

* [ruby-core:100819] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage
  2020-11-12 16:55 [ruby-core:100816] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage ko1
  2020-11-12 17:07 ` [ruby-core:100817] " ko1
  2020-11-12 18:24 ` [ruby-core:100818] " marcandre-ruby-core
@ 2020-11-12 18:44 ` ko1
  2020-11-13 19:53 ` [ruby-core:100842] " daniel
                   ` (14 subsequent siblings)
  17 siblings, 0 replies; 19+ messages in thread
From: ko1 @ 2020-11-12 18:44 UTC (permalink / raw)
  To: ruby-core

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


marcandre (Marc-Andre Lafortune) wrote in #note-2:
> - deep-copying the cache is probably faster than having to recalculate part of it
> - more importantly, this would probably be the wrong solution if we had `SharedHash`:

it depends on the problem. per-ractor cache is faster to access because there are no synchronization overhead.

Anyway, cache is not a good example.

I wrote it in GH thread https://github.com/ruby/ruby/pull/3762#issuecomment-726227262


> I'm not sure it is a feasible example, but we can set separate configuration between ractors.

> Another idea is to provide unshareable, but similar to global variables, such as Random::DEFAULT which is discussed on https://bugs.ruby-lang.org/issues/17322.
> We can use LVar to implement Ractor::default.

Maybe we can study with the usage of `Thread#thread_variable_get(sym)`.

but not so many https://gist.github.com/ko1/c00020a2c06dceaf9fd5d930e721651e


----------------------------------------
Feature #17323: Ractor::LVar to provide ractor-local storage
https://bugs.ruby-lang.org/issues/17323#change-88459

* Author: ko1 (Koichi Sasada)
* Status: Open
* Priority: Normal
----------------------------------------
Ruby supports thread and fiber local storage:

* `Thread#[sym]` provides *Fiber* local storage
* `Thread#thread_variable_get(sym)

These APIs can access other threads/fibers like that:

```ruby
th = Thread.new{
  Thread.current.thread_variable_set(:a, 10)
}
th.join
# access from main thread to child thread
p th.thread_variable_get(:a)
```

To make Ractor local storage, this kind of feature should not be allowed to protect isolation.

This ticket propose alternative API `Ractor::LVar` that allows to provide Ractor local variable.

```ruby
LV1 = Ractor::LVar.new

p LV1.value #=> nil # default value
LV1.value = 'hello' # can set unshareable objects because LVar is ractor local.

Ractor.new do
  LV1.value = 'world' # set Ractor local variable
end.take

p LV1.value #=> 'hello'


# Lvar.new can accept default_proc which should be isolated Proc.

LV2 = Ractor::LVar.new{ "x" * 4 }
p LV2.value #=> "xxxx"
LV2.value = 'yyy'

Ractor.new do
  p LV2.value #=> 'xxx'
end

p LV2.value #=> 'yyy'
```

This API doesn't support accessing from other ractors.

`Ractor::LVar` is from `Ractor::TVar`, but I have no strong opinion about it.
For example, `Ractor::LocalVariable` is longer and clearer.

Implementation: https://github.com/ruby/ruby/pull/3762




-- 
https://bugs.ruby-lang.org/

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

* [ruby-core:100842] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage
  2020-11-12 16:55 [ruby-core:100816] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage ko1
                   ` (2 preceding siblings ...)
  2020-11-12 18:44 ` [ruby-core:100819] " ko1
@ 2020-11-13 19:53 ` daniel
  2020-11-16  7:59 ` [ruby-core:100868] " ko1
                   ` (13 subsequent siblings)
  17 siblings, 0 replies; 19+ messages in thread
From: daniel @ 2020-11-13 19:53 UTC (permalink / raw)
  To: ruby-core

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


Would it be possible to somehow have ractor-local variables that are automatically dereferenced instead of having to append `.value` everywhere?

I'm thinking of cases like this where a class-level mutable constant (or classvar) makes it hard to make this code compatible with ractors.

```ruby
class X
  # original, not compatible with Ractor, so how do we fix?
  CACHE = {}

  # like this, except now we need to append .value to every CACHE access
  CACHE = Ractor::LVar.new{ {} }

  def initialize(value)
    @value = value
  end

  def analyzed
    # here, lookup of CACHE constant could automatically return the
    # ractor-local Hash inside the LVar instead of the LVar itself
    CACHE[@value] ||= analyze(@value)
  end
end
```

The problem with the example above is that it's too magical to have a variable or constant return an object different from what was assigned. So what I'm saying is that I'd like _something like_ this, that achieves the same effect.

I have the feeling that a different syntax would be needed, to differentiate it from assignment. Maybe `CONST: expr` could be used to lazily evaluate `expr` once per ractor; then it would make sense for `CONST` to return this value rather than the LVar used behind the scenes. The example above would become `CACHE: {}`. But of course introducing new syntax is not something to be done lightly.

----------------------------------------
Feature #17323: Ractor::LVar to provide ractor-local storage
https://bugs.ruby-lang.org/issues/17323#change-88484

* Author: ko1 (Koichi Sasada)
* Status: Open
* Priority: Normal
----------------------------------------
Ruby supports thread and fiber local storage:

* `Thread#[sym]` provides *Fiber* local storage
* `Thread#thread_variable_get(sym)

These APIs can access other threads/fibers like that:

```ruby
th = Thread.new{
  Thread.current.thread_variable_set(:a, 10)
}
th.join
# access from main thread to child thread
p th.thread_variable_get(:a)
```

To make Ractor local storage, this kind of feature should not be allowed to protect isolation.

This ticket propose alternative API `Ractor::LVar` that allows to provide Ractor local variable.

```ruby
LV1 = Ractor::LVar.new

p LV1.value #=> nil # default value
LV1.value = 'hello' # can set unshareable objects because LVar is ractor local.

Ractor.new do
  LV1.value = 'world' # set Ractor local variable
end.take

p LV1.value #=> 'hello'


# Lvar.new can accept default_proc which should be isolated Proc.

LV2 = Ractor::LVar.new{ "x" * 4 }
p LV2.value #=> "xxxx"
LV2.value = 'yyy'

Ractor.new do
  p LV2.value #=> 'xxx'
end

p LV2.value #=> 'yyy'
```

This API doesn't support accessing from other ractors.

`Ractor::LVar` is from `Ractor::TVar`, but I have no strong opinion about it.
For example, `Ractor::LocalVariable` is longer and clearer.

Implementation: https://github.com/ruby/ruby/pull/3762




-- 
https://bugs.ruby-lang.org/

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

* [ruby-core:100868] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage
  2020-11-12 16:55 [ruby-core:100816] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage ko1
                   ` (3 preceding siblings ...)
  2020-11-13 19:53 ` [ruby-core:100842] " daniel
@ 2020-11-16  7:59 ` ko1
  2020-12-06 23:20 ` [ruby-core:101271] " marcandre-ruby-core
                   ` (12 subsequent siblings)
  17 siblings, 0 replies; 19+ messages in thread
From: ko1 @ 2020-11-16  7:59 UTC (permalink / raw)
  To: ruby-core

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


Dan0042 (Daniel DeLorme) wrote in #note-4:
> The problem with the example above is that it's too magical to have a variable or constant return an object different from what was assigned.

Absolutely. By ractor's design, there is a possibility to provide "fork" model, which dup all constants at ractor creation. But we didn't choose (at least now).

> So what I'm saying is that I'd like _something like_ this, that achieves the same effect.

I understand the motivation. But not sure it is easy to use.

Adding `.value` is, it is clear that it is not access constants.
But not so clear it is ractor-local value.
It is disadvantage compare with traditional `Ractor.current[:sym]` approach.

(and I agree adding `.value` for each access is not easy)


----------------------------------------
Feature #17323: Ractor::LVar to provide ractor-local storage
https://bugs.ruby-lang.org/issues/17323#change-88515

* Author: ko1 (Koichi Sasada)
* Status: Open
* Priority: Normal
----------------------------------------
Ruby supports thread and fiber local storage:

* `Thread#[sym]` provides *Fiber* local storage
* `Thread#thread_variable_get(sym)

These APIs can access other threads/fibers like that:

```ruby
th = Thread.new{
  Thread.current.thread_variable_set(:a, 10)
}
th.join
# access from main thread to child thread
p th.thread_variable_get(:a)
```

To make Ractor local storage, this kind of feature should not be allowed to protect isolation.

This ticket propose alternative API `Ractor::LVar` that allows to provide Ractor local variable.

```ruby
LV1 = Ractor::LVar.new

p LV1.value #=> nil # default value
LV1.value = 'hello' # can set unshareable objects because LVar is ractor local.

Ractor.new do
  LV1.value = 'world' # set Ractor local variable
end.take

p LV1.value #=> 'hello'


# Lvar.new can accept default_proc which should be isolated Proc.

LV2 = Ractor::LVar.new{ "x" * 4 }
p LV2.value #=> "xxxx"
LV2.value = 'yyy'

Ractor.new do
  p LV2.value #=> 'xxx'
end

p LV2.value #=> 'yyy'
```

This API doesn't support accessing from other ractors.

`Ractor::LVar` is from `Ractor::TVar`, but I have no strong opinion about it.
For example, `Ractor::LocalVariable` is longer and clearer.

Implementation: https://github.com/ruby/ruby/pull/3762




-- 
https://bugs.ruby-lang.org/

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

* [ruby-core:101271] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage
  2020-11-12 16:55 [ruby-core:100816] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage ko1
                   ` (4 preceding siblings ...)
  2020-11-16  7:59 ` [ruby-core:100868] " ko1
@ 2020-12-06 23:20 ` marcandre-ruby-core
  2020-12-07  4:26 ` [ruby-core:101275] " marcandre-ruby-core
                   ` (11 subsequent siblings)
  17 siblings, 0 replies; 19+ messages in thread
From: marcandre-ruby-core @ 2020-12-06 23:20 UTC (permalink / raw)
  To: ruby-core

Issue #17323 has been updated by marcandre (Marc-Andre Lafortune).


I'm having difficulties because we don't have Ractor-local storage...

I'd like to implement a Ractor compatible `SharedQueue`.

I'd like to do it without going through a bridge Ractor, so I'm trying to refine `Ractor` to add `channel`s as in #17365.

To do that, I need a Ractor-local "saved messages queue", but there is currently no API for that.

I guess I'll have to roll my own using `Thread.main.thread_variable_get` and wrap the `set` inside a global Mutex...

----------------------------------------
Feature #17323: Ractor::LVar to provide ractor-local storage
https://bugs.ruby-lang.org/issues/17323#change-88951

* Author: ko1 (Koichi Sasada)
* Status: Open
* Priority: Normal
----------------------------------------
Ruby supports thread and fiber local storage:

* `Thread#[sym]` provides *Fiber* local storage
* `Thread#thread_variable_get(sym)`

These APIs can access other threads/fibers like that:

```ruby
th = Thread.new{
  Thread.current.thread_variable_set(:a, 10)
}
th.join
# access from main thread to child thread
p th.thread_variable_get(:a)
```

To make Ractor local storage, this kind of feature should not be allowed to protect isolation.

This ticket propose alternative API `Ractor::LVar` that allows to provide Ractor local variable.

```ruby
LV1 = Ractor::LVar.new

p LV1.value #=> nil # default value
LV1.value = 'hello' # can set unshareable objects because LVar is ractor local.

Ractor.new do
  LV1.value = 'world' # set Ractor local variable
end.take

p LV1.value #=> 'hello'


# Lvar.new can accept default_proc which should be isolated Proc.

LV2 = Ractor::LVar.new{ "x" * 4 }
p LV2.value #=> "xxxx"
LV2.value = 'yyy'

Ractor.new do
  p LV2.value #=> 'xxx'
end

p LV2.value #=> 'yyy'
```

This API doesn't support accessing from other ractors.

`Ractor::LVar` is from `Ractor::TVar`, but I have no strong opinion about it.
For example, `Ractor::LocalVariable` is longer and clearer.

Implementation: https://github.com/ruby/ruby/pull/3762




-- 
https://bugs.ruby-lang.org/

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

* [ruby-core:101275] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage
  2020-11-12 16:55 [ruby-core:100816] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage ko1
                   ` (5 preceding siblings ...)
  2020-12-06 23:20 ` [ruby-core:101271] " marcandre-ruby-core
@ 2020-12-07  4:26 ` marcandre-ruby-core
  2020-12-10  7:06 ` [ruby-core:101363] " matz
                   ` (10 subsequent siblings)
  17 siblings, 0 replies; 19+ messages in thread
From: marcandre-ruby-core @ 2020-12-07  4:26 UTC (permalink / raw)
  To: ruby-core

Issue #17323 has been updated by marcandre (Marc-Andre Lafortune).


In the meantime, I create the gem `ractor-local_variable`.

Code here: https://github.com/ractor-tools/ractor-local_variable/

Of course, `Mutex` is Ractor local... I used a `Ractor` as a basic mutex, there's no other way, right?

----------------------------------------
Feature #17323: Ractor::LVar to provide ractor-local storage
https://bugs.ruby-lang.org/issues/17323#change-88955

* Author: ko1 (Koichi Sasada)
* Status: Open
* Priority: Normal
----------------------------------------
Ruby supports thread and fiber local storage:

* `Thread#[sym]` provides *Fiber* local storage
* `Thread#thread_variable_get(sym)`

These APIs can access other threads/fibers like that:

```ruby
th = Thread.new{
  Thread.current.thread_variable_set(:a, 10)
}
th.join
# access from main thread to child thread
p th.thread_variable_get(:a)
```

To make Ractor local storage, this kind of feature should not be allowed to protect isolation.

This ticket propose alternative API `Ractor::LVar` that allows to provide Ractor local variable.

```ruby
LV1 = Ractor::LVar.new

p LV1.value #=> nil # default value
LV1.value = 'hello' # can set unshareable objects because LVar is ractor local.

Ractor.new do
  LV1.value = 'world' # set Ractor local variable
end.take

p LV1.value #=> 'hello'


# Lvar.new can accept default_proc which should be isolated Proc.

LV2 = Ractor::LVar.new{ "x" * 4 }
p LV2.value #=> "xxxx"
LV2.value = 'yyy'

Ractor.new do
  p LV2.value #=> 'xxx'
end

p LV2.value #=> 'yyy'
```

This API doesn't support accessing from other ractors.

`Ractor::LVar` is from `Ractor::TVar`, but I have no strong opinion about it.
For example, `Ractor::LocalVariable` is longer and clearer.

Implementation: https://github.com/ruby/ruby/pull/3762




-- 
https://bugs.ruby-lang.org/

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

* [ruby-core:101363] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage
  2020-11-12 16:55 [ruby-core:100816] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage ko1
                   ` (6 preceding siblings ...)
  2020-12-07  4:26 ` [ruby-core:101275] " marcandre-ruby-core
@ 2020-12-10  7:06 ` matz
  2020-12-10 10:50 ` [ruby-core:101379] " marcandre-ruby-core
                   ` (9 subsequent siblings)
  17 siblings, 0 replies; 19+ messages in thread
From: matz @ 2020-12-10  7:06 UTC (permalink / raw)
  To: ruby-core

Issue #17323 has been updated by matz (Yukihiro Matsumoto).


I prefer Thread-like Ractor local storage, e.g. `Ractor.current[key]`.

Matz.


----------------------------------------
Feature #17323: Ractor::LVar to provide ractor-local storage
https://bugs.ruby-lang.org/issues/17323#change-89070

* Author: ko1 (Koichi Sasada)
* Status: Open
* Priority: Normal
----------------------------------------
Ruby supports thread and fiber local storage:

* `Thread#[sym]` provides *Fiber* local storage
* `Thread#thread_variable_get(sym)`

These APIs can access other threads/fibers like that:

```ruby
th = Thread.new{
  Thread.current.thread_variable_set(:a, 10)
}
th.join
# access from main thread to child thread
p th.thread_variable_get(:a)
```

To make Ractor local storage, this kind of feature should not be allowed to protect isolation.

This ticket propose alternative API `Ractor::LVar` that allows to provide Ractor local variable.

```ruby
LV1 = Ractor::LVar.new

p LV1.value #=> nil # default value
LV1.value = 'hello' # can set unshareable objects because LVar is ractor local.

Ractor.new do
  LV1.value = 'world' # set Ractor local variable
end.take

p LV1.value #=> 'hello'


# Lvar.new can accept default_proc which should be isolated Proc.

LV2 = Ractor::LVar.new{ "x" * 4 }
p LV2.value #=> "xxxx"
LV2.value = 'yyy'

Ractor.new do
  p LV2.value #=> 'xxx'
end

p LV2.value #=> 'yyy'
```

This API doesn't support accessing from other ractors.

`Ractor::LVar` is from `Ractor::TVar`, but I have no strong opinion about it.
For example, `Ractor::LocalVariable` is longer and clearer.

Implementation: https://github.com/ruby/ruby/pull/3762




-- 
https://bugs.ruby-lang.org/

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

* [ruby-core:101379] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage
  2020-11-12 16:55 [ruby-core:100816] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage ko1
                   ` (7 preceding siblings ...)
  2020-12-10  7:06 ` [ruby-core:101363] " matz
@ 2020-12-10 10:50 ` marcandre-ruby-core
  2020-12-10 11:43 ` [ruby-core:101382] " eregontp
                   ` (8 subsequent siblings)
  17 siblings, 0 replies; 19+ messages in thread
From: marcandre-ruby-core @ 2020-12-10 10:50 UTC (permalink / raw)
  To: ruby-core

Issue #17323 has been updated by marcandre (Marc-Andre Lafortune).


Will there be a thread-safe way to initialize this storage (other then from the Ractor block)?

Because we can't use a `Mutex` to synchronize as there is no way to initialize the `Mutex` safely either...

Example: A gem needs Ractor-local storage to function. How can they initialize it in a thread-safe manner (other than by asking users of the gem to call `MyLib.initialize_ractor` from the ractor creation block)?

----------------------------------------
Feature #17323: Ractor::LVar to provide ractor-local storage
https://bugs.ruby-lang.org/issues/17323#change-89138

* Author: ko1 (Koichi Sasada)
* Status: Open
* Priority: Normal
----------------------------------------
Ruby supports thread and fiber local storage:

* `Thread#[sym]` provides *Fiber* local storage
* `Thread#thread_variable_get(sym)`

These APIs can access other threads/fibers like that:

```ruby
th = Thread.new{
  Thread.current.thread_variable_set(:a, 10)
}
th.join
# access from main thread to child thread
p th.thread_variable_get(:a)
```

To make Ractor local storage, this kind of feature should not be allowed to protect isolation.

This ticket propose alternative API `Ractor::LVar` that allows to provide Ractor local variable.

```ruby
LV1 = Ractor::LVar.new

p LV1.value #=> nil # default value
LV1.value = 'hello' # can set unshareable objects because LVar is ractor local.

Ractor.new do
  LV1.value = 'world' # set Ractor local variable
end.take

p LV1.value #=> 'hello'


# Lvar.new can accept default_proc which should be isolated Proc.

LV2 = Ractor::LVar.new{ "x" * 4 }
p LV2.value #=> "xxxx"
LV2.value = 'yyy'

Ractor.new do
  p LV2.value #=> 'xxx'
end

p LV2.value #=> 'yyy'
```

This API doesn't support accessing from other ractors.

`Ractor::LVar` is from `Ractor::TVar`, but I have no strong opinion about it.
For example, `Ractor::LocalVariable` is longer and clearer.

Implementation: https://github.com/ruby/ruby/pull/3762




-- 
https://bugs.ruby-lang.org/

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

* [ruby-core:101382] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage
  2020-11-12 16:55 [ruby-core:100816] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage ko1
                   ` (8 preceding siblings ...)
  2020-12-10 10:50 ` [ruby-core:101379] " marcandre-ruby-core
@ 2020-12-10 11:43 ` eregontp
  2020-12-10 12:20 ` [ruby-core:101388] " marcandre-ruby-core
                   ` (7 subsequent siblings)
  17 siblings, 0 replies; 19+ messages in thread
From: eregontp @ 2020-12-10 11:43 UTC (permalink / raw)
  To: ruby-core

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


marcandre (Marc-Andre Lafortune) wrote in #note-10:
> Will there be a thread-safe way to initialize this storage (other then from the Ractor block)?

One possibility, when using `Ractor::LVar.new` would be:
```ruby
LV1 = Ractor::LVar.new do
  initial value
end
```
(not unlike Java's `ThreadLocal.withInitial(() -> { ... })`)

For a library, the `Ractor.current[key]` form seems inconvenient as it would need to generate some key, instead of referencing an Ractor::LVar.new object.

----------------------------------------
Feature #17323: Ractor::LVar to provide ractor-local storage
https://bugs.ruby-lang.org/issues/17323#change-89140

* Author: ko1 (Koichi Sasada)
* Status: Open
* Priority: Normal
----------------------------------------
Ruby supports thread and fiber local storage:

* `Thread#[sym]` provides *Fiber* local storage
* `Thread#thread_variable_get(sym)`

These APIs can access other threads/fibers like that:

```ruby
th = Thread.new{
  Thread.current.thread_variable_set(:a, 10)
}
th.join
# access from main thread to child thread
p th.thread_variable_get(:a)
```

To make Ractor local storage, this kind of feature should not be allowed to protect isolation.

This ticket propose alternative API `Ractor::LVar` that allows to provide Ractor local variable.

```ruby
LV1 = Ractor::LVar.new

p LV1.value #=> nil # default value
LV1.value = 'hello' # can set unshareable objects because LVar is ractor local.

Ractor.new do
  LV1.value = 'world' # set Ractor local variable
end.take

p LV1.value #=> 'hello'


# Lvar.new can accept default_proc which should be isolated Proc.

LV2 = Ractor::LVar.new{ "x" * 4 }
p LV2.value #=> "xxxx"
LV2.value = 'yyy'

Ractor.new do
  p LV2.value #=> 'xxx'
end

p LV2.value #=> 'yyy'
```

This API doesn't support accessing from other ractors.

`Ractor::LVar` is from `Ractor::TVar`, but I have no strong opinion about it.
For example, `Ractor::LocalVariable` is longer and clearer.

Implementation: https://github.com/ruby/ruby/pull/3762




-- 
https://bugs.ruby-lang.org/

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

* [ruby-core:101388] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage
  2020-11-12 16:55 [ruby-core:100816] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage ko1
                   ` (9 preceding siblings ...)
  2020-12-10 11:43 ` [ruby-core:101382] " eregontp
@ 2020-12-10 12:20 ` marcandre-ruby-core
  2020-12-21 20:58 ` [ruby-core:101607] " ko1
                   ` (6 subsequent siblings)
  17 siblings, 0 replies; 19+ messages in thread
From: marcandre-ruby-core @ 2020-12-10 12:20 UTC (permalink / raw)
  To: ruby-core

Issue #17323 has been updated by marcandre (Marc-Andre Lafortune).


Eregon (Benoit Daloze) wrote in #note-11:
> For a library, the `Ractor.current[key]` form seems inconvenient as it would need to generate some key, instead of referencing an Ractor::LVar.new object.

I agree using a key does not sound that convenient, but it is a well known API. It's also not too hard to find a key. Instead of storing a `LVar` in `MyGem::Something::REGISTRY`, one can use `:'MyGem::Something::REGISTRY'` as key, there's not much of a difference except it needs to be fully scoped. I don't see other uses than globals for ractor-local storage, right?

The thread-safefy is imo a bigger issue: if there's an easy way (here use `||=` and ignore thread safety) that is almsot safe, and a really hard way that is safe, I fear that the easy way will be taken most of the time.



----------------------------------------
Feature #17323: Ractor::LVar to provide ractor-local storage
https://bugs.ruby-lang.org/issues/17323#change-89147

* Author: ko1 (Koichi Sasada)
* Status: Open
* Priority: Normal
----------------------------------------
Ruby supports thread and fiber local storage:

* `Thread#[sym]` provides *Fiber* local storage
* `Thread#thread_variable_get(sym)`

These APIs can access other threads/fibers like that:

```ruby
th = Thread.new{
  Thread.current.thread_variable_set(:a, 10)
}
th.join
# access from main thread to child thread
p th.thread_variable_get(:a)
```

To make Ractor local storage, this kind of feature should not be allowed to protect isolation.

This ticket propose alternative API `Ractor::LVar` that allows to provide Ractor local variable.

```ruby
LV1 = Ractor::LVar.new

p LV1.value #=> nil # default value
LV1.value = 'hello' # can set unshareable objects because LVar is ractor local.

Ractor.new do
  LV1.value = 'world' # set Ractor local variable
end.take

p LV1.value #=> 'hello'


# Lvar.new can accept default_proc which should be isolated Proc.

LV2 = Ractor::LVar.new{ "x" * 4 }
p LV2.value #=> "xxxx"
LV2.value = 'yyy'

Ractor.new do
  p LV2.value #=> 'xxx'
end

p LV2.value #=> 'yyy'
```

This API doesn't support accessing from other ractors.

`Ractor::LVar` is from `Ractor::TVar`, but I have no strong opinion about it.
For example, `Ractor::LocalVariable` is longer and clearer.

Implementation: https://github.com/ruby/ruby/pull/3762




-- 
https://bugs.ruby-lang.org/

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

* [ruby-core:101607] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage
  2020-11-12 16:55 [ruby-core:100816] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage ko1
                   ` (10 preceding siblings ...)
  2020-12-10 12:20 ` [ruby-core:101388] " marcandre-ruby-core
@ 2020-12-21 20:58 ` ko1
  2021-01-06 15:09 ` [ruby-core:101959] " dsisnero
                   ` (5 subsequent siblings)
  17 siblings, 0 replies; 19+ messages in thread
From: ko1 @ 2020-12-21 20:58 UTC (permalink / raw)
  To: ruby-core

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

Status changed from Open to Closed

Traditional style `Ractor#[]/#[]=` are introduced.
35471a948739ca13b85fe900871e081d553f68e6



----------------------------------------
Feature #17323: Ractor::LVar to provide ractor-local storage
https://bugs.ruby-lang.org/issues/17323#change-89395

* Author: ko1 (Koichi Sasada)
* Status: Closed
* Priority: Normal
----------------------------------------
Ruby supports thread and fiber local storage:

* `Thread#[sym]` provides *Fiber* local storage
* `Thread#thread_variable_get(sym)`

These APIs can access other threads/fibers like that:

```ruby
th = Thread.new{
  Thread.current.thread_variable_set(:a, 10)
}
th.join
# access from main thread to child thread
p th.thread_variable_get(:a)
```

To make Ractor local storage, this kind of feature should not be allowed to protect isolation.

This ticket propose alternative API `Ractor::LVar` that allows to provide Ractor local variable.

```ruby
LV1 = Ractor::LVar.new

p LV1.value #=> nil # default value
LV1.value = 'hello' # can set unshareable objects because LVar is ractor local.

Ractor.new do
  LV1.value = 'world' # set Ractor local variable
end.take

p LV1.value #=> 'hello'


# Lvar.new can accept default_proc which should be isolated Proc.

LV2 = Ractor::LVar.new{ "x" * 4 }
p LV2.value #=> "xxxx"
LV2.value = 'yyy'

Ractor.new do
  p LV2.value #=> 'xxx'
end

p LV2.value #=> 'yyy'
```

This API doesn't support accessing from other ractors.

`Ractor::LVar` is from `Ractor::TVar`, but I have no strong opinion about it.
For example, `Ractor::LocalVariable` is longer and clearer.

Implementation: https://github.com/ruby/ruby/pull/3762




-- 
https://bugs.ruby-lang.org/

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

* [ruby-core:101959] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage
  2020-11-12 16:55 [ruby-core:100816] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage ko1
                   ` (11 preceding siblings ...)
  2020-12-21 20:58 ` [ruby-core:101607] " ko1
@ 2021-01-06 15:09 ` dsisnero
  2021-01-06 15:11 ` [ruby-core:101960] " chris
                   ` (4 subsequent siblings)
  17 siblings, 0 replies; 19+ messages in thread
From: dsisnero @ 2021-01-06 15:09 UTC (permalink / raw)
  To: ruby-core

Issue #17323 has been updated by dsisnero (Dominic Sisneros).


I don't like the name because it shadows - implies MVAR, TVAR which are known in the functional programming world.  

----------------------------------------
Feature #17323: Ractor::LVar to provide ractor-local storage
https://bugs.ruby-lang.org/issues/17323#change-89812

* Author: ko1 (Koichi Sasada)
* Status: Closed
* Priority: Normal
----------------------------------------
Ruby supports thread and fiber local storage:

* `Thread#[sym]` provides *Fiber* local storage
* `Thread#thread_variable_get(sym)`

These APIs can access other threads/fibers like that:

```ruby
th = Thread.new{
  Thread.current.thread_variable_set(:a, 10)
}
th.join
# access from main thread to child thread
p th.thread_variable_get(:a)
```

To make Ractor local storage, this kind of feature should not be allowed to protect isolation.

This ticket propose alternative API `Ractor::LVar` that allows to provide Ractor local variable.

```ruby
LV1 = Ractor::LVar.new

p LV1.value #=> nil # default value
LV1.value = 'hello' # can set unshareable objects because LVar is ractor local.

Ractor.new do
  LV1.value = 'world' # set Ractor local variable
end.take

p LV1.value #=> 'hello'


# Lvar.new can accept default_proc which should be isolated Proc.

LV2 = Ractor::LVar.new{ "x" * 4 }
p LV2.value #=> "xxxx"
LV2.value = 'yyy'

Ractor.new do
  p LV2.value #=> 'xxx'
end

p LV2.value #=> 'yyy'
```

This API doesn't support accessing from other ractors.

`Ractor::LVar` is from `Ractor::TVar`, but I have no strong opinion about it.
For example, `Ractor::LocalVariable` is longer and clearer.

Implementation: https://github.com/ruby/ruby/pull/3762




-- 
https://bugs.ruby-lang.org/

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

* [ruby-core:101960] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage
  2020-11-12 16:55 [ruby-core:100816] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage ko1
                   ` (12 preceding siblings ...)
  2021-01-06 15:09 ` [ruby-core:101959] " dsisnero
@ 2021-01-06 15:11 ` chris
  2021-01-06 16:22 ` [ruby-core:101964] " dsisnero
                   ` (3 subsequent siblings)
  17 siblings, 0 replies; 19+ messages in thread
From: chris @ 2021-01-06 15:11 UTC (permalink / raw)
  To: ruby-core

Issue #17323 has been updated by chrisseaton (Chris Seaton).


> it shadows - implies MVAR, TVAR

I think that's the point isn't it? We have TVar (transactional), MVar (mutable), LVar (local), and matches ivar (instance.)

LVar is a bit overloaded that's true - left-value in terms of assignment - but most short names are.

----------------------------------------
Feature #17323: Ractor::LVar to provide ractor-local storage
https://bugs.ruby-lang.org/issues/17323#change-89813

* Author: ko1 (Koichi Sasada)
* Status: Closed
* Priority: Normal
----------------------------------------
Ruby supports thread and fiber local storage:

* `Thread#[sym]` provides *Fiber* local storage
* `Thread#thread_variable_get(sym)`

These APIs can access other threads/fibers like that:

```ruby
th = Thread.new{
  Thread.current.thread_variable_set(:a, 10)
}
th.join
# access from main thread to child thread
p th.thread_variable_get(:a)
```

To make Ractor local storage, this kind of feature should not be allowed to protect isolation.

This ticket propose alternative API `Ractor::LVar` that allows to provide Ractor local variable.

```ruby
LV1 = Ractor::LVar.new

p LV1.value #=> nil # default value
LV1.value = 'hello' # can set unshareable objects because LVar is ractor local.

Ractor.new do
  LV1.value = 'world' # set Ractor local variable
end.take

p LV1.value #=> 'hello'


# Lvar.new can accept default_proc which should be isolated Proc.

LV2 = Ractor::LVar.new{ "x" * 4 }
p LV2.value #=> "xxxx"
LV2.value = 'yyy'

Ractor.new do
  p LV2.value #=> 'xxx'
end

p LV2.value #=> 'yyy'
```

This API doesn't support accessing from other ractors.

`Ractor::LVar` is from `Ractor::TVar`, but I have no strong opinion about it.
For example, `Ractor::LocalVariable` is longer and clearer.

Implementation: https://github.com/ruby/ruby/pull/3762




-- 
https://bugs.ruby-lang.org/

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

* [ruby-core:101964] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage
  2020-11-12 16:55 [ruby-core:100816] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage ko1
                   ` (13 preceding siblings ...)
  2021-01-06 15:11 ` [ruby-core:101960] " chris
@ 2021-01-06 16:22 ` dsisnero
  2021-01-06 16:25 ` [ruby-core:101965] " chris
                   ` (2 subsequent siblings)
  17 siblings, 0 replies; 19+ messages in thread
From: dsisnero @ 2021-01-06 16:22 UTC (permalink / raw)
  To: ruby-core

Issue #17323 has been updated by dsisnero (Dominic Sisneros).


LVAR is not used much but it is used. http://lambda-the-ultimate.org/node/4823, http://composition.al/blog/2013/09/22/some-example-mvar-ivar-and-lvar-programs-in-haskell/ and it is not
local variable it is a lattice based monotomic container 

----------------------------------------
Feature #17323: Ractor::LVar to provide ractor-local storage
https://bugs.ruby-lang.org/issues/17323#change-89817

* Author: ko1 (Koichi Sasada)
* Status: Closed
* Priority: Normal
----------------------------------------
Ruby supports thread and fiber local storage:

* `Thread#[sym]` provides *Fiber* local storage
* `Thread#thread_variable_get(sym)`

These APIs can access other threads/fibers like that:

```ruby
th = Thread.new{
  Thread.current.thread_variable_set(:a, 10)
}
th.join
# access from main thread to child thread
p th.thread_variable_get(:a)
```

To make Ractor local storage, this kind of feature should not be allowed to protect isolation.

This ticket propose alternative API `Ractor::LVar` that allows to provide Ractor local variable.

```ruby
LV1 = Ractor::LVar.new

p LV1.value #=> nil # default value
LV1.value = 'hello' # can set unshareable objects because LVar is ractor local.

Ractor.new do
  LV1.value = 'world' # set Ractor local variable
end.take

p LV1.value #=> 'hello'


# Lvar.new can accept default_proc which should be isolated Proc.

LV2 = Ractor::LVar.new{ "x" * 4 }
p LV2.value #=> "xxxx"
LV2.value = 'yyy'

Ractor.new do
  p LV2.value #=> 'xxx'
end

p LV2.value #=> 'yyy'
```

This API doesn't support accessing from other ractors.

`Ractor::LVar` is from `Ractor::TVar`, but I have no strong opinion about it.
For example, `Ractor::LocalVariable` is longer and clearer.

Implementation: https://github.com/ruby/ruby/pull/3762




-- 
https://bugs.ruby-lang.org/

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

* [ruby-core:101965] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage
  2020-11-12 16:55 [ruby-core:100816] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage ko1
                   ` (14 preceding siblings ...)
  2021-01-06 16:22 ` [ruby-core:101964] " dsisnero
@ 2021-01-06 16:25 ` chris
  2021-01-07  6:33 ` [ruby-core:101970] " ko1
  2021-01-07 13:11 ` [ruby-core:101971] " eregontp
  17 siblings, 0 replies; 19+ messages in thread
From: chris @ 2021-01-06 16:25 UTC (permalink / raw)
  To: ruby-core

Issue #17323 has been updated by chrisseaton (Chris Seaton).


I don't think there's a massive overlap there, and there's no many letters we could use.

----------------------------------------
Feature #17323: Ractor::LVar to provide ractor-local storage
https://bugs.ruby-lang.org/issues/17323#change-89818

* Author: ko1 (Koichi Sasada)
* Status: Closed
* Priority: Normal
----------------------------------------
Ruby supports thread and fiber local storage:

* `Thread#[sym]` provides *Fiber* local storage
* `Thread#thread_variable_get(sym)`

These APIs can access other threads/fibers like that:

```ruby
th = Thread.new{
  Thread.current.thread_variable_set(:a, 10)
}
th.join
# access from main thread to child thread
p th.thread_variable_get(:a)
```

To make Ractor local storage, this kind of feature should not be allowed to protect isolation.

This ticket propose alternative API `Ractor::LVar` that allows to provide Ractor local variable.

```ruby
LV1 = Ractor::LVar.new

p LV1.value #=> nil # default value
LV1.value = 'hello' # can set unshareable objects because LVar is ractor local.

Ractor.new do
  LV1.value = 'world' # set Ractor local variable
end.take

p LV1.value #=> 'hello'


# Lvar.new can accept default_proc which should be isolated Proc.

LV2 = Ractor::LVar.new{ "x" * 4 }
p LV2.value #=> "xxxx"
LV2.value = 'yyy'

Ractor.new do
  p LV2.value #=> 'xxx'
end

p LV2.value #=> 'yyy'
```

This API doesn't support accessing from other ractors.

`Ractor::LVar` is from `Ractor::TVar`, but I have no strong opinion about it.
For example, `Ractor::LocalVariable` is longer and clearer.

Implementation: https://github.com/ruby/ruby/pull/3762




-- 
https://bugs.ruby-lang.org/

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

* [ruby-core:101970] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage
  2020-11-12 16:55 [ruby-core:100816] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage ko1
                   ` (15 preceding siblings ...)
  2021-01-06 16:25 ` [ruby-core:101965] " chris
@ 2021-01-07  6:33 ` ko1
  2021-01-07 13:11 ` [ruby-core:101971] " eregontp
  17 siblings, 0 replies; 19+ messages in thread
From: ko1 @ 2021-01-07  6:33 UTC (permalink / raw)
  To: ruby-core

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


`Ractor::LocalVariable` ?

----------------------------------------
Feature #17323: Ractor::LVar to provide ractor-local storage
https://bugs.ruby-lang.org/issues/17323#change-89822

* Author: ko1 (Koichi Sasada)
* Status: Closed
* Priority: Normal
----------------------------------------
Ruby supports thread and fiber local storage:

* `Thread#[sym]` provides *Fiber* local storage
* `Thread#thread_variable_get(sym)`

These APIs can access other threads/fibers like that:

```ruby
th = Thread.new{
  Thread.current.thread_variable_set(:a, 10)
}
th.join
# access from main thread to child thread
p th.thread_variable_get(:a)
```

To make Ractor local storage, this kind of feature should not be allowed to protect isolation.

This ticket propose alternative API `Ractor::LVar` that allows to provide Ractor local variable.

```ruby
LV1 = Ractor::LVar.new

p LV1.value #=> nil # default value
LV1.value = 'hello' # can set unshareable objects because LVar is ractor local.

Ractor.new do
  LV1.value = 'world' # set Ractor local variable
end.take

p LV1.value #=> 'hello'


# Lvar.new can accept default_proc which should be isolated Proc.

LV2 = Ractor::LVar.new{ "x" * 4 }
p LV2.value #=> "xxxx"
LV2.value = 'yyy'

Ractor.new do
  p LV2.value #=> 'xxx'
end

p LV2.value #=> 'yyy'
```

This API doesn't support accessing from other ractors.

`Ractor::LVar` is from `Ractor::TVar`, but I have no strong opinion about it.
For example, `Ractor::LocalVariable` is longer and clearer.

Implementation: https://github.com/ruby/ruby/pull/3762




-- 
https://bugs.ruby-lang.org/

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

* [ruby-core:101971] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage
  2020-11-12 16:55 [ruby-core:100816] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage ko1
                   ` (16 preceding siblings ...)
  2021-01-07  6:33 ` [ruby-core:101970] " ko1
@ 2021-01-07 13:11 ` eregontp
  17 siblings, 0 replies; 19+ messages in thread
From: eregontp @ 2021-01-07 13:11 UTC (permalink / raw)
  To: ruby-core

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


I was thinking `Ractor::Local.new` would be fine too, (e.g., Java has `new ThreadLocal()`).

----------------------------------------
Feature #17323: Ractor::LVar to provide ractor-local storage
https://bugs.ruby-lang.org/issues/17323#change-89823

* Author: ko1 (Koichi Sasada)
* Status: Closed
* Priority: Normal
----------------------------------------
Ruby supports thread and fiber local storage:

* `Thread#[sym]` provides *Fiber* local storage
* `Thread#thread_variable_get(sym)`

These APIs can access other threads/fibers like that:

```ruby
th = Thread.new{
  Thread.current.thread_variable_set(:a, 10)
}
th.join
# access from main thread to child thread
p th.thread_variable_get(:a)
```

To make Ractor local storage, this kind of feature should not be allowed to protect isolation.

This ticket propose alternative API `Ractor::LVar` that allows to provide Ractor local variable.

```ruby
LV1 = Ractor::LVar.new

p LV1.value #=> nil # default value
LV1.value = 'hello' # can set unshareable objects because LVar is ractor local.

Ractor.new do
  LV1.value = 'world' # set Ractor local variable
end.take

p LV1.value #=> 'hello'


# Lvar.new can accept default_proc which should be isolated Proc.

LV2 = Ractor::LVar.new{ "x" * 4 }
p LV2.value #=> "xxxx"
LV2.value = 'yyy'

Ractor.new do
  p LV2.value #=> 'xxx'
end

p LV2.value #=> 'yyy'
```

This API doesn't support accessing from other ractors.

`Ractor::LVar` is from `Ractor::TVar`, but I have no strong opinion about it.
For example, `Ractor::LocalVariable` is longer and clearer.

Implementation: https://github.com/ruby/ruby/pull/3762




-- 
https://bugs.ruby-lang.org/

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

end of thread, other threads:[~2021-01-07 13:11 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-11-12 16:55 [ruby-core:100816] [Ruby master Feature#17323] Ractor::LVar to provide ractor-local storage ko1
2020-11-12 17:07 ` [ruby-core:100817] " ko1
2020-11-12 18:24 ` [ruby-core:100818] " marcandre-ruby-core
2020-11-12 18:44 ` [ruby-core:100819] " ko1
2020-11-13 19:53 ` [ruby-core:100842] " daniel
2020-11-16  7:59 ` [ruby-core:100868] " ko1
2020-12-06 23:20 ` [ruby-core:101271] " marcandre-ruby-core
2020-12-07  4:26 ` [ruby-core:101275] " marcandre-ruby-core
2020-12-10  7:06 ` [ruby-core:101363] " matz
2020-12-10 10:50 ` [ruby-core:101379] " marcandre-ruby-core
2020-12-10 11:43 ` [ruby-core:101382] " eregontp
2020-12-10 12:20 ` [ruby-core:101388] " marcandre-ruby-core
2020-12-21 20:58 ` [ruby-core:101607] " ko1
2021-01-06 15:09 ` [ruby-core:101959] " dsisnero
2021-01-06 15:11 ` [ruby-core:101960] " chris
2021-01-06 16:22 ` [ruby-core:101964] " dsisnero
2021-01-06 16:25 ` [ruby-core:101965] " chris
2021-01-07  6:33 ` [ruby-core:101970] " ko1
2021-01-07 13:11 ` [ruby-core:101971] " eregontp

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 read-only IMAP folder(s) and NNTP newsgroup(s).