<feed xmlns='http://www.w3.org/2005/Atom'>
<title>ruby.git/thread_pthread_mn.c, branch v4.0.4</title>
<subtitle>The Ruby Programming Language</subtitle>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/'/>
<entry>
<title>merge revision(s) 08372635f7ec09f7115bd254246ebd637499651c: [Backport #21926]</title>
<updated>2026-03-16T18:57:04+00:00</updated>
<author>
<name>Takashi Kokubun</name>
<email>takashikkbn@gmail.com</email>
</author>
<published>2026-03-16T18:57:04+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=a601b899a35c796775309dca01a6d5e64be14c44'/>
<id>a601b899a35c796775309dca01a6d5e64be14c44</id>
<content type='text'>
	Fix race condition right after ubf registration

	Fixes [Bug #21926]
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
	Fix race condition right after ubf registration

	Fixes [Bug #21926]
</pre>
</div>
</content>
</entry>
<entry>
<title>Fix M:N threads under OpenBSD</title>
<updated>2026-03-12T00:41:59+00:00</updated>
<author>
<name>John Hawthorn</name>
<email>john@hawthorn.email</email>
</author>
<published>2026-03-08T19:32:39+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=764a245ef9e305a515a8be54b87c8b7b3ac45af0'/>
<id>764a245ef9e305a515a8be54b87c8b7b3ac45af0</id>
<content type='text'>
OpenBSD requires MAP_STACK for memory regions used as thread stacks.
However it seems to error with "Invalid argument" unless the permissions
include both PROT_READ | PROT_WRITE.

We should be able to satisft this by re-mmapping over our reserved stack
region to get the MAP_STACK flag. As a (very minor) bonus, this applies
MAP_STACK only to the machine stack region, not the VM region.

Co-authored-by: Jeremy Evans &lt;code@jeremyevans.net&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
OpenBSD requires MAP_STACK for memory regions used as thread stacks.
However it seems to error with "Invalid argument" unless the permissions
include both PROT_READ | PROT_WRITE.

We should be able to satisft this by re-mmapping over our reserved stack
region to get the MAP_STACK flag. As a (very minor) bonus, this applies
MAP_STACK only to the machine stack region, not the VM region.

Co-authored-by: Jeremy Evans &lt;code@jeremyevans.net&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>Map M:N thread stack chunks initially as PROT_NONE</title>
<updated>2026-03-05T19:08:14+00:00</updated>
<author>
<name>John Hawthorn</name>
<email>john@hawthorn.email</email>
</author>
<published>2026-02-24T02:40:29+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=33e5d3894fcddeb16518fdd0512fda097e7039fe'/>
<id>33e5d3894fcddeb16518fdd0512fda097e7039fe</id>
<content type='text'>
Previously we initially mapped the full 512MB chunk as
PROT_READ|PROD_WRITE and then set a guard page to PROT_NONE the first
time a new thread stack is needed. Usually that's okay as we don't touch
that memory until it is needed and so it doesn't count towards RSS.

However, on Linux even with vm.overcommit_memory=0 (the default) if on a
system (like a tiny cloud VM) with &lt;512MB of RAM+swap that would error
with.

    Thread#initialize': can't create Thread: Cannot allocate memory (ThreadError)

This changes the chunk to be mapped initially with PROT_NONE, then
instead of mapping the guard pages we map in the machine and VM stacks
using mprotect. This ensures we don't commit stack memory until it is
first used, and as a side benefit any stray pointers into unused stack
should segfault.

When a stack is freed/reused there is no change from the previous
behaviour, we just use madvise and leave the same regions in place.

[Bug #21944]
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Previously we initially mapped the full 512MB chunk as
PROT_READ|PROD_WRITE and then set a guard page to PROT_NONE the first
time a new thread stack is needed. Usually that's okay as we don't touch
that memory until it is needed and so it doesn't count towards RSS.

However, on Linux even with vm.overcommit_memory=0 (the default) if on a
system (like a tiny cloud VM) with &lt;512MB of RAM+swap that would error
with.

    Thread#initialize': can't create Thread: Cannot allocate memory (ThreadError)

This changes the chunk to be mapped initially with PROT_NONE, then
instead of mapping the guard pages we map in the machine and VM stacks
using mprotect. This ensures we don't commit stack memory until it is
first used, and as a side benefit any stray pointers into unused stack
should segfault.

When a stack is freed/reused there is no change from the previous
behaviour, we just use madvise and leave the same regions in place.

[Bug #21944]
</pre>
</div>
</content>
</entry>
<entry>
<title>move th-&gt;event_serial to rb_thread_sched_item (#15500)</title>
<updated>2025-12-12T19:24:40+00:00</updated>
<author>
<name>Luke Gruber</name>
<email>luke.gruber@shopify.com</email>
</author>
<published>2025-12-12T19:24:40+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=7909ce2a839ba1c3e134239189e6aa2de3b6b630'/>
<id>7909ce2a839ba1c3e134239189e6aa2de3b6b630</id>
<content type='text'>
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
</pre>
</div>
</content>
</entry>
<entry>
<title>use `ractor_sched_lock`</title>
<updated>2025-12-12T06:32:03+00:00</updated>
<author>
<name>Koichi Sasada</name>
<email>ko1@atdot.net</email>
</author>
<published>2025-12-12T06:01:27+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=5ef4f88d5e0cea9a36702f8165a4d47a11c2a703'/>
<id>5ef4f88d5e0cea9a36702f8165a4d47a11c2a703</id>
<content type='text'>
instead of using `rb_native_mutex_lock` directly.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
instead of using `rb_native_mutex_lock` directly.
</pre>
</div>
</content>
</entry>
<entry>
<title>Fix thread scheduler issue with thread_sched_wait_events (#15392)</title>
<updated>2025-12-04T21:51:11+00:00</updated>
<author>
<name>Luke Gruber</name>
<email>luke.gruber@shopify.com</email>
</author>
<published>2025-12-04T21:51:11+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=8d8159e7d87e4fd1594ce2fad3d2653e47fb1026'/>
<id>8d8159e7d87e4fd1594ce2fad3d2653e47fb1026</id>
<content type='text'>
Fix race between timer thread dequeuing waiting thread and thread
skipping sleeping due to being dequeued. We now use `th-&gt;event_serial` which
is protected by `thread_sched_lock`. When a thread is put on timer thread's waiting
list, the event serial is saved on the item. The timer thread checks
that the saved serial is the same as current thread's serial before
calling `thread_sched_to_ready`.

The following script (taken from a test in `test_thread.rb` used to crash on
scheduler debug assertions. It would likely crash in non-debug mode as well.

```ruby
def assert_nil(val)
  if val != nil
    raise "Expected #{val} to be nil"
  end
end

def assert_equal(expected, actual)
  if expected != actual
    raise "Expected #{expected} to be #{actual}"
  end
end

def test_join2
  ok = false
  t1 = Thread.new { ok = true; sleep }
  Thread.pass until ok
  Thread.pass until t1.stop?
  t2 = Thread.new do
    Thread.pass while ok
    t1.join(0.01)
  end
  t3 = Thread.new do
    ok = false
    t1.join
  end
  assert_nil(t2.value)
  t1.wakeup
  assert_equal(t1, t3.value)
ensure
  t1&amp;.kill&amp;.join
  t2&amp;.kill&amp;.join
  t3&amp;.kill&amp;.join
end

rs = 30.times.map do
  Ractor.new do
    test_join2
  end
end
rs.each(&amp;:join)
```</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Fix race between timer thread dequeuing waiting thread and thread
skipping sleeping due to being dequeued. We now use `th-&gt;event_serial` which
is protected by `thread_sched_lock`. When a thread is put on timer thread's waiting
list, the event serial is saved on the item. The timer thread checks
that the saved serial is the same as current thread's serial before
calling `thread_sched_to_ready`.

The following script (taken from a test in `test_thread.rb` used to crash on
scheduler debug assertions. It would likely crash in non-debug mode as well.

```ruby
def assert_nil(val)
  if val != nil
    raise "Expected #{val} to be nil"
  end
end

def assert_equal(expected, actual)
  if expected != actual
    raise "Expected #{expected} to be #{actual}"
  end
end

def test_join2
  ok = false
  t1 = Thread.new { ok = true; sleep }
  Thread.pass until ok
  Thread.pass until t1.stop?
  t2 = Thread.new do
    Thread.pass while ok
    t1.join(0.01)
  end
  t3 = Thread.new do
    ok = false
    t1.join
  end
  assert_nil(t2.value)
  t1.wakeup
  assert_equal(t1, t3.value)
ensure
  t1&amp;.kill&amp;.join
  t2&amp;.kill&amp;.join
  t3&amp;.kill&amp;.join
end

rs = 30.times.map do
  Ractor.new do
    test_join2
  end
end
rs.each(&amp;:join)
```</pre>
</div>
</content>
</entry>
<entry>
<title>Fix `thread_sched_wait_events` race (#15067)</title>
<updated>2025-11-11T01:32:30+00:00</updated>
<author>
<name>Luke Gruber</name>
<email>luke.gruber@shopify.com</email>
</author>
<published>2025-11-11T01:32:30+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=d2c30a3bae908772c1de453aad8686000f6a5096'/>
<id>d2c30a3bae908772c1de453aad8686000f6a5096</id>
<content type='text'>
This race condition was found when calling `Thread#join` with a timeout
inside a ractor. The race is between the polling thread waking up the
thread and the `ubf` getting called (`ubf_event_waiting`). The error was
that the ubf or polling thread would set the thread as ready, but then
the other function would do the same.

Fixes [Bug #21614]</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
This race condition was found when calling `Thread#join` with a timeout
inside a ractor. The race is between the polling thread waking up the
thread and the `ubf` getting called (`ubf_event_waiting`). The error was
that the ubf or polling thread would set the thread as ready, but then
the other function would do the same.

Fixes [Bug #21614]</pre>
</div>
</content>
</entry>
<entry>
<title>mn timer thread: force wakeups for timeouts</title>
<updated>2025-10-30T20:44:25+00:00</updated>
<author>
<name>Andre Muta</name>
<email>andremuta@gmail.com</email>
</author>
<published>2025-10-30T03:55:40+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=0531fa4d6fea100f69f0bac9e03973fe49ecd570'/>
<id>0531fa4d6fea100f69f0bac9e03973fe49ecd570</id>
<content type='text'>
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
</pre>
</div>
</content>
</entry>
<entry>
<title>Adjust snt &lt; max_cpu calculation</title>
<updated>2025-08-21T18:37:07+00:00</updated>
<author>
<name>John Hawthorn</name>
<email>john@hawthorn.email</email>
</author>
<published>2025-08-20T05:22:51+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=7ac16eff311f9bc762586bda9540d82e8eb7f135'/>
<id>7ac16eff311f9bc762586bda9540d82e8eb7f135</id>
<content type='text'>
[Bug #20146]

Previously we dealt with the main Ractor not being enabled for M:N by
incrementing snt_cnt++. This worked for comparing against ractor count,
but meant that we always had one less SNT than was specified by
RUBY_MAX_CPU.

This was notably a problem for RUBY_MAX_CPU=1, which would cause Ractors
to hang.

This commit instead of adjusting snt, adjusts a
"schedulable_ractor_cnt". This way snt_cnt will actually reach
RUBY_MAX_CPU.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
[Bug #20146]

Previously we dealt with the main Ractor not being enabled for M:N by
incrementing snt_cnt++. This worked for comparing against ractor count,
but meant that we always had one less SNT than was specified by
RUBY_MAX_CPU.

This was notably a problem for RUBY_MAX_CPU=1, which would cause Ractors
to hang.

This commit instead of adjusting snt, adjusts a
"schedulable_ractor_cnt". This way snt_cnt will actually reach
RUBY_MAX_CPU.
</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>
</feed>
