<feed xmlns='http://www.w3.org/2005/Atom'>
<title>ruby.git/test/ruby/test_iseq.rb, branch v4.0.2</title>
<subtitle>The Ruby Programming Language</subtitle>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/'/>
<entry>
<title>Ractor.shareable_proc</title>
<updated>2025-09-23T18:59:03+00:00</updated>
<author>
<name>Koichi Sasada</name>
<email>ko1@atdot.net</email>
</author>
<published>2025-07-17T06:38:54+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=55b1ba3bf276ba82173bd961fb8e0f08bf4182a6'/>
<id>55b1ba3bf276ba82173bd961fb8e0f08bf4182a6</id>
<content type='text'>
call-seq:
  Ractor.sharable_proc(self: nil){} -&gt; sharable proc

It returns shareable Proc object. The Proc object is
shareable and the self in a block will be replaced with
the value passed via `self:` keyword.

In a shareable Proc, the outer variables should
* (1) refer shareable objects
* (2) be not be overwritten

```ruby
  a = 42
  Ractor.shareable_proc{ p a }
  #=&gt; OK

  b = 43
  Ractor.shareable_proc{ p b; b = 44 }
  #=&gt; Ractor::IsolationError because 'b' is reassigned in the block.

  c = 44
  Ractor.shareable_proc{ p c }
  #=&gt; Ractor::IsolationError because 'c' will be reassigned outside of the block.
  c = 45

  d = 45
  d = 46 if cond
  Ractor.shareable_proc{ p d }
  #=&gt; Ractor::IsolationError because 'd' was reassigned outside of the block.
```

The last `d`'s case can be relaxed in a future version.

The above check will be done in a static analysis at compile time,
so the reflection feature such as `Binding#local_varaible_set`
can not be detected.

```ruby
  e = 42
  shpr = Ractor.shareable_proc{ p e } #=&gt; OK
  binding.local_variable_set(:e, 43)
  shpr.call #=&gt; 42 (returns captured timing value)
```

Ractor.sharaeble_lambda is also introduced.
[Feature #21550]
[Feature #21557]
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
call-seq:
  Ractor.sharable_proc(self: nil){} -&gt; sharable proc

It returns shareable Proc object. The Proc object is
shareable and the self in a block will be replaced with
the value passed via `self:` keyword.

In a shareable Proc, the outer variables should
* (1) refer shareable objects
* (2) be not be overwritten

```ruby
  a = 42
  Ractor.shareable_proc{ p a }
  #=&gt; OK

  b = 43
  Ractor.shareable_proc{ p b; b = 44 }
  #=&gt; Ractor::IsolationError because 'b' is reassigned in the block.

  c = 44
  Ractor.shareable_proc{ p c }
  #=&gt; Ractor::IsolationError because 'c' will be reassigned outside of the block.
  c = 45

  d = 45
  d = 46 if cond
  Ractor.shareable_proc{ p d }
  #=&gt; Ractor::IsolationError because 'd' was reassigned outside of the block.
```

The last `d`'s case can be relaxed in a future version.

The above check will be done in a static analysis at compile time,
so the reflection feature such as `Binding#local_varaible_set`
can not be detected.

```ruby
  e = 42
  shpr = Ractor.shareable_proc{ p e } #=&gt; OK
  binding.local_variable_set(:e, 43)
  shpr.call #=&gt; 42 (returns captured timing value)
```

Ractor.sharaeble_lambda is also introduced.
[Feature #21550]
[Feature #21557]
</pre>
</div>
</content>
</entry>
<entry>
<title>Fix memory leak in Prism's RubyVM::InstructionSequence.new</title>
<updated>2025-06-03T14:00:15+00:00</updated>
<author>
<name>Peter Zhu</name>
<email>peter@peterzhu.ca</email>
</author>
<published>2025-06-02T19:02:59+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=34b407a4a89e69dd04f692e2b29efa2816d4664a'/>
<id>34b407a4a89e69dd04f692e2b29efa2816d4664a</id>
<content type='text'>
[Bug #21394]

There are two ways to make RubyVM::InstructionSequence.new raise which
would cause the options-&gt;scopes to leak memory:

1. Passing in any (non T_FILE) object where the to_str raises.
2. Passing in a T_FILE object where String#initialize_dup raises. This is
   because rb_io_path dups the string.

Example 1:

    10.times do
      100_000.times do
        RubyVM::InstructionSequence.new(nil)
      rescue TypeError
      end

      puts `ps -o rss= -p #{$$}`
    end

Before:

    13392
    17104
    20256
    23920
    27264
    30432
    33584
    36752
    40032
    43232

After:

    9392
    11072
    11648
    11648
    11648
    11712
    11712
    11712
    11744
    11744

Example 2:

    require "tempfile"

    MyError = Class.new(StandardError)
    String.prepend(Module.new do
      def initialize_dup(_)
        if $raise_on_dup
          raise MyError
        else
          super
        end
      end
    end)

    Tempfile.create do |f|
      10.times do
        100_000.times do
          $raise_on_dup = true
          RubyVM::InstructionSequence.new(f)
        rescue MyError
        else
          raise "MyError was not raised during RubyVM::InstructionSequence.new"
        end

        puts `ps -o rss= -p #{$$}`
      ensure
        $raise_on_dup = false
      end
    end

Before:

    14080
    18512
    22000
    25184
    28320
    31600
    34736
    37904
    41088
    44256

After:

    12016
    12464
    12880
    12880
    12880
    12912
    12912
    12912
    12912
    12912
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
[Bug #21394]

There are two ways to make RubyVM::InstructionSequence.new raise which
would cause the options-&gt;scopes to leak memory:

1. Passing in any (non T_FILE) object where the to_str raises.
2. Passing in a T_FILE object where String#initialize_dup raises. This is
   because rb_io_path dups the string.

Example 1:

    10.times do
      100_000.times do
        RubyVM::InstructionSequence.new(nil)
      rescue TypeError
      end

      puts `ps -o rss= -p #{$$}`
    end

Before:

    13392
    17104
    20256
    23920
    27264
    30432
    33584
    36752
    40032
    43232

After:

    9392
    11072
    11648
    11648
    11648
    11712
    11712
    11712
    11744
    11744

Example 2:

    require "tempfile"

    MyError = Class.new(StandardError)
    String.prepend(Module.new do
      def initialize_dup(_)
        if $raise_on_dup
          raise MyError
        else
          super
        end
      end
    end)

    Tempfile.create do |f|
      10.times do
        100_000.times do
          $raise_on_dup = true
          RubyVM::InstructionSequence.new(f)
        rescue MyError
        else
          raise "MyError was not raised during RubyVM::InstructionSequence.new"
        end

        puts `ps -o rss= -p #{$$}`
      ensure
        $raise_on_dup = false
      end
    end

Before:

    14080
    18512
    22000
    25184
    28320
    31600
    34736
    37904
    41088
    44256

After:

    12016
    12464
    12880
    12880
    12880
    12912
    12912
    12912
    12912
    12912
</pre>
</div>
</content>
</entry>
<entry>
<title>Fix test_loading_kwargs_memory_leak</title>
<updated>2025-06-02T19:45:16+00:00</updated>
<author>
<name>Peter Zhu</name>
<email>peter@peterzhu.ca</email>
</author>
<published>2025-06-02T15:51:03+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=685c8ca9af892f562f64b54dbee73bb9a1999b90'/>
<id>685c8ca9af892f562f64b54dbee73bb9a1999b90</id>
<content type='text'>
The test fails with:

    TestISeq#test_loading_kwargs_memory_leak [test/ruby/test_iseq.rb:882]:
    pid 18222 exit 1
    | -:2:in '&lt;main&gt;': undefined method 'iseq_to_binary' for main (NoMethodError)
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
The test fails with:

    TestISeq#test_loading_kwargs_memory_leak [test/ruby/test_iseq.rb:882]:
    pid 18222 exit 1
    | -:2:in '&lt;main&gt;': undefined method 'iseq_to_binary' for main (NoMethodError)
</pre>
</div>
</content>
</entry>
<entry>
<title>compile.c: Handle anonymous variables in `outer_variable_cmp`</title>
<updated>2025-06-02T11:41:28+00:00</updated>
<author>
<name>Jean Boussier</name>
<email>jean.boussier@gmail.com</email>
</author>
<published>2025-05-24T08:02:35+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=ff222ac27afe712ef6ec2bb74c81cdde1a1fa176'/>
<id>ff222ac27afe712ef6ec2bb74c81cdde1a1fa176</id>
<content type='text'>
[Bug #21370]
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
[Bug #21370]
</pre>
</div>
</content>
</entry>
<entry>
<title>`Ractor::Port`</title>
<updated>2025-05-30T19:01:33+00:00</updated>
<author>
<name>Koichi Sasada</name>
<email>ko1@atdot.net</email>
</author>
<published>2025-05-26T18:58:04+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=ef2bb61018cd9ccb5b61a3d91911e04a773da4a7'/>
<id>ef2bb61018cd9ccb5b61a3d91911e04a773da4a7</id>
<content type='text'>
* Added `Ractor::Port`
  * `Ractor::Port#receive` (support multi-threads)
  * `Rcator::Port#close`
  * `Ractor::Port#closed?`
* Added some methods
  * `Ractor#join`
  * `Ractor#value`
  * `Ractor#monitor`
  * `Ractor#unmonitor`
* Removed some methods
  * `Ractor#take`
  * `Ractor.yield`
* Change the spec
  * `Racotr.select`

You can wait for multiple sequences of messages with `Ractor::Port`.

```ruby
ports = 3.times.map{ Ractor::Port.new }
ports.map.with_index do |port, ri|
  Ractor.new port,ri do |port, ri|
    3.times{|i| port &lt;&lt; "r#{ri}-#{i}"}
  end
end

p ports.each{|port| pp 3.times.map{port.receive}}

```

In this example, we use 3 ports, and 3 Ractors send messages to them respectively.
We can receive a series of messages from each port.

You can use `Ractor#value` to get the last value of a Ractor's block:

```ruby
result = Ractor.new do
  heavy_task()
end.value
```

You can wait for the termination of a Ractor with `Ractor#join` like this:

```ruby
Ractor.new do
  some_task()
end.join
```

`#value` and `#join` are similar to `Thread#value` and `Thread#join`.

To implement `#join`, `Ractor#monitor` (and `Ractor#unmonitor`) is introduced.

This commit changes `Ractor.select()` method.
It now only accepts ports or Ractors, and returns when a port receives a message or a Ractor terminates.

We removes `Ractor.yield` and `Ractor#take` because:
* `Ractor::Port` supports most of similar use cases in a simpler manner.
* Removing them significantly simplifies the code.

We also change the internal thread scheduler code (thread_pthread.c):
* During barrier synchronization, we keep the `ractor_sched` lock to avoid deadlocks.
  This lock is released by `rb_ractor_sched_barrier_end()`
  which is called at the end of operations that require the barrier.
* fix potential deadlock issues by checking interrupts just before setting UBF.

https://bugs.ruby-lang.org/issues/21262
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
* Added `Ractor::Port`
  * `Ractor::Port#receive` (support multi-threads)
  * `Rcator::Port#close`
  * `Ractor::Port#closed?`
* Added some methods
  * `Ractor#join`
  * `Ractor#value`
  * `Ractor#monitor`
  * `Ractor#unmonitor`
* Removed some methods
  * `Ractor#take`
  * `Ractor.yield`
* Change the spec
  * `Racotr.select`

You can wait for multiple sequences of messages with `Ractor::Port`.

```ruby
ports = 3.times.map{ Ractor::Port.new }
ports.map.with_index do |port, ri|
  Ractor.new port,ri do |port, ri|
    3.times{|i| port &lt;&lt; "r#{ri}-#{i}"}
  end
end

p ports.each{|port| pp 3.times.map{port.receive}}

```

In this example, we use 3 ports, and 3 Ractors send messages to them respectively.
We can receive a series of messages from each port.

You can use `Ractor#value` to get the last value of a Ractor's block:

```ruby
result = Ractor.new do
  heavy_task()
end.value
```

You can wait for the termination of a Ractor with `Ractor#join` like this:

```ruby
Ractor.new do
  some_task()
end.join
```

`#value` and `#join` are similar to `Thread#value` and `Thread#join`.

To implement `#join`, `Ractor#monitor` (and `Ractor#unmonitor`) is introduced.

This commit changes `Ractor.select()` method.
It now only accepts ports or Ractors, and returns when a port receives a message or a Ractor terminates.

We removes `Ractor.yield` and `Ractor#take` because:
* `Ractor::Port` supports most of similar use cases in a simpler manner.
* Removing them significantly simplifies the code.

We also change the internal thread scheduler code (thread_pthread.c):
* During barrier synchronization, we keep the `ractor_sched` lock to avoid deadlocks.
  This lock is released by `rb_ractor_sched_barrier_end()`
  which is called at the end of operations that require the barrier.
* fix potential deadlock issues by checking interrupts just before setting UBF.

https://bugs.ruby-lang.org/issues/21262
</pre>
</div>
</content>
</entry>
<entry>
<title>Omit tests using ISeq#to_binary under coverage measurement</title>
<updated>2025-05-01T05:15:55+00:00</updated>
<author>
<name>Yusuke Endoh</name>
<email>mame@ruby-lang.org</email>
</author>
<published>2025-05-01T04:37:28+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=e8ad728209ee22136e61054fea74096b49088b8a'/>
<id>e8ad728209ee22136e61054fea74096b49088b8a</id>
<content type='text'>
... because ISeq#to_binary does not work
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
... because ISeq#to_binary does not work
</pre>
</div>
</content>
</entry>
<entry>
<title>Avoid allocation for anonymous positional splat with no arguments</title>
<updated>2025-03-27T20:59:03+00:00</updated>
<author>
<name>Jeremy Evans</name>
<email>code@jeremyevans.net</email>
</author>
<published>2025-01-18T16:54:28+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=43683e1e9d8f7f6ed3fb4a48598190a0993f8f66'/>
<id>43683e1e9d8f7f6ed3fb4a48598190a0993f8f66</id>
<content type='text'>
Anonymous positional splats cannot be directly accessed, they can
only be passed as splats to other methods.  So if an anonymous
positional splat would be empty, you can use a shared frozen
empty array to save an allocation.

```ruby
def a(*) end
a()
```

This is similar to how anonymous empty keyword splats are optimized,
except those use `nil` instead of a shared empty frozen hash.

This updates the allocation tests to check that the array allocations
are avoided where possible.

It also makes a small change to test_iseq.rb to ensure an unfrozen
hash is passed as the value of an anonymous splat parameter.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Anonymous positional splats cannot be directly accessed, they can
only be passed as splats to other methods.  So if an anonymous
positional splat would be empty, you can use a shared frozen
empty array to save an allocation.

```ruby
def a(*) end
a()
```

This is similar to how anonymous empty keyword splats are optimized,
except those use `nil` instead of a shared empty frozen hash.

This updates the allocation tests to check that the array allocations
are avoided where possible.

It also makes a small change to test_iseq.rb to ensure an unfrozen
hash is passed as the value of an anonymous splat parameter.
</pre>
</div>
</content>
</entry>
<entry>
<title>Have `ast` live longer in ISeq.compile_file to fix GC stress crash</title>
<updated>2025-03-12T19:00:26+00:00</updated>
<author>
<name>Alan Wu</name>
<email>XrXr@users.noreply.github.com</email>
</author>
<published>2025-03-11T17:14:13+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=9b9661883b1e2cc85b1341d804b106885432d2bd'/>
<id>9b9661883b1e2cc85b1341d804b106885432d2bd</id>
<content type='text'>
Previously, live range of `ast_value` ended on the call right before
rb_ast_dispose(), which led to premature collection and use-after-free.

We observed this crashing on -O3, -DVM_CHECK_MODE, with GCC 11.4.0 on
Ubuntu.

Co-authored-by: Aaron Patterson &lt;tenderlove@ruby-lang.org&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Previously, live range of `ast_value` ended on the call right before
rb_ast_dispose(), which led to premature collection and use-after-free.

We observed this crashing on -O3, -DVM_CHECK_MODE, with GCC 11.4.0 on
Ubuntu.

Co-authored-by: Aaron Patterson &lt;tenderlove@ruby-lang.org&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>RubyVM::InstructionSequence.of Thread::Backtrace::Location</title>
<updated>2024-10-16T18:31:26+00:00</updated>
<author>
<name>Kevin Newton</name>
<email>kddnewton@gmail.com</email>
</author>
<published>2024-10-15T14:06:50+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=5eca11ca5e5503e41b7ec81f03e63c59556c038c'/>
<id>5eca11ca5e5503e41b7ec81f03e63c59556c038c</id>
<content type='text'>
This would be useful for debugging.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
This would be useful for debugging.
</pre>
</div>
</content>
</entry>
<entry>
<title>Prevent warnings: assigned but unused variable</title>
<updated>2024-09-15T01:05:57+00:00</updated>
<author>
<name>Yusuke Endoh</name>
<email>mame@ruby-lang.org</email>
</author>
<published>2024-09-15T01:05:57+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=bc13ec735b33771c279e67e4143fa8e98a8252fd'/>
<id>bc13ec735b33771c279e67e4143fa8e98a8252fd</id>
<content type='text'>
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
</pre>
</div>
</content>
</entry>
</feed>
