Commit graph

1187 commits

Author SHA1 Message Date
Erik Berlin
eab4a0bc8d
Fix race condition in signal handler query (#13712)
* Fix race condition in signal handler query

* Initialize signal lock dynamically and reset after fork

* Fix signal handler mutex initialization conditions
2025-06-28 13:55:59 +09:00
Nobuyoshi Nakada
fe9a3be296
Fix the unknown warning group on wasm 2025-06-27 16:29:11 +09:00
Samuel Williams
ba68343d3a
Allow wakeup mutex to be used in trap context. (#13684) 2025-06-24 05:55:07 +00:00
Nobuyoshi Nakada
a60bf9e693
* adjust indent 2025-06-17 12:30:18 +09:00
Samuel Williams
68625a23d6
Fix blocking operation cancellation. (#13614)
Expose `rb_thread_resolve_unblock_function` internally.
2025-06-14 12:32:51 +09:00
John Hawthorn
a34fcf401b Add a new_thread flag to rb_interrupt_exec
Previously rb_ractor_interrupt_exec would use an intermediate function
to create a new thread with the actual target function, replacing the
data being passed in with a piece of malloc memory holding the "next"
function and the original data.

Because of this, passing rb_interrupt_exec_flag_value_data to
rb_ractor_interrupt_exec didn't have the intended effect of allowing
data to be passed in and marked.

This commit adds a rb_interrupt_exec_flag_new_thread flag, which
both simplifies the implementation and allows the original data to be
marked.
2025-06-12 13:13:55 -07:00
Peter Zhu
6e36841dbd Free rb_native_thread memory at fork
We never freed any resources of rb_native_thread at fork because it would
cause it to hang. This is because it called rb_native_cond_destroy for
condition variables.  We can't call rb_native_cond_destroy here because
according to the specs of pthread_cond_destroy:

    Attempting to destroy a condition variable upon which other threads
    are currently blocked results in undefined behavior.

Specifically, glibc's pthread_cond_destroy waits on all the other listeners.
Since after forking all the threads are dead, the condition variable's
listeners will never wake up, so it will hang forever.

This commit changes it to only free the memory and none of the condition
variables.
2025-06-12 15:23:50 -04:00
Samuel Williams
ead14b19aa Fix blocking_operation_wait use-after-free bug. 2025-06-06 13:13:16 +09:00
Samuel Williams
81a23c5793 rb_io_blocking_operation_exit should not execute with pending interrupts. 2025-06-06 13:13:16 +09:00
Samuel Williams
f0cf4dce65
Handle spurious wakeups in Thread#join. (#13532) 2025-06-06 09:38:57 +09:00
Luke Gruber
54ef6c312a
[Bug #21400] Fix rb_bug() when killing current root fiber in non-main thread (#13526)
Fixes the following:

```ruby
Thread.new { Fiber.current.kill }.join
```
2025-06-06 09:31:45 +09:00
Nobuyoshi Nakada
9fddb8d954
Suppress dangling pointer warning by gcc
`__has_warning` is clang, not gcc.
2025-06-04 15:31:40 +09:00
Samuel Williams
9a29252830
Fix compatibility with fiber schedulers that don't implement #fiber_interrupt. (#13492) 2025-06-02 18:50:23 +09:00
Koichi Sasada
ef2bb61018 Ractor::Port
* 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 << "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
2025-05-31 04:01:33 +09:00
Nobuyoshi Nakada
aad9fa2853
Use RB_VM_LOCKING 2025-05-25 15:22:43 +09:00
Daisuke Fujimura (fd0)
70f8f7c4b1 Fix warning on cygwin 2025-05-23 20:49:19 +09:00
Samuel Williams
73c9d6ccaa
Allow IO#close to interrupt IO operations on fibers using fiber_interrupt hook. (#12839) 2025-05-23 14:55:05 +09:00
John Hawthorn
05e0e7223a Use atomic load to read interrupt mask 2025-05-20 09:56:31 -07:00
John Hawthorn
d67d169aea Use atomics for system_working global
Although it almost certainly works in this case, volatile is best not
used for multi-threaded code. Using atomics instead avoids warnings from
TSan.

This also simplifies some logic, as system_working was previously only
ever assigned to 1, so --system_working <= 0 should always return true
(unless it underflowed).
2025-05-15 15:18:10 -07:00
John Hawthorn
d845da05e8 Force reset running time in timer interrupt
Co-authored-by: Ivo Anjo <ivo.anjo@datadoghq.com>
Co-authored-by: Luke Gruber <luke.gru@gmail.com>
2025-05-15 14:44:26 -07:00
Nobuyoshi Nakada
2e3f81838c
Align styles [ci skip] 2025-05-15 17:48:40 +09:00
Samuel Williams
87261c2d95
Ensure that forked process do not see invalid blocking operations. (#13343) 2025-05-15 15:50:15 +09:00
Luke Gruber
1d4822a175 Get ractor message passing working with > 1 thread sending/receiving values in same ractor
Rework ractors so that any ractor action (Ractor.receive, Ractor#send, Ractor.yield, Ractor#take,
Ractor.select) will operate on the thread that called the action. It will put that thread to sleep if
it's a blocking function and it needs to put it to sleep, and the awakening action (Ractor.yield,
Ractor#send) will wake up the blocked thread.

Before this change every blocking ractor action was associated with the ractor struct and its fields.
If a ractor called Ractor.receive, its wait status was wait_receiving, and when another ractor calls
r.send on it, it will look for that status in the ractor struct fields and wake it up. The problem was that
what if 2 threads call blocking ractor actions in the same ractor. Imagine if 1 thread has called Ractor.receive
and another r.take. Then, when a different ractor calls r.send on it, it doesn't know which ruby thread is associated
to which ractor action, so what ruby thread should it schedule? This change moves some fields onto the ruby thread
itself so that ruby threads are the ones that have ractor blocking statuses, and threads are then specifically scheduled
when unblocked.

Fixes [#17624]
Fixes [#21037]
2025-05-13 13:23:57 -07:00
Samuel Williams
425fa0aeb5
Make waiting_fd behaviour per-IO. (#13127)
- `rb_thread_fd_close` is deprecated and now a no-op.
- IO operations (including close) no longer take a vm-wide lock.
2025-05-13 19:02:03 +09:00
Aaron Patterson
f7ff380998 Clean up Ractor cache after fork
Ractors created in a parent process should be properly shut down in the
child process.  They need their cache cleared and status set to
"terminated"

Co-authored-by: John Hawthorn <john@hawthorn.email>
2025-05-08 10:53:28 -07:00
Nobuyoshi Nakada
c218862d3c
Fix style [ci skip] 2025-04-19 22:02:10 +09:00
Samuel Williams
20a1c1dc6b
Ensure struct rb_io is passed through to thread.c. (#13134) 2025-04-19 09:55:16 +09:00
Samuel Williams
4e970c5d5a Expose ruby_thread_has_gvl_p. 2025-04-14 18:28:09 +09:00
Yusuke Endoh
0d6263bd41 Fix coverage measurement for negative line numbers
Fixes [Bug #21220]

Co-Authored-By: Mike Bourgeous <mike@mikebourgeous.com>
Co-Authored-By: Jean Boussier <jean.boussier@gmail.com>
2025-04-09 23:45:54 +09:00
Jean Boussier
532b9246d3 Initialize ractor thgroup in thread_do_start_proc
Followup: https://github.com/ruby/ruby/pull/13013
2025-03-31 11:14:34 +02:00
Jean Boussier
5e421ce8d9 ractor: don't inherit the default thread group
[Bug #17506]

`Thread.current.group` isn't shareable so it shouldn't be inherited
by the main thread of a new Ractor.

This cause an extra allocation when spawning a ractor, which could
be elided with a bit of extra work, but not sure if it's worth
the effort.
2025-03-31 10:25:52 +02:00
John Hawthorn
310c00a137 Reset thread interrupt lock on fork
If a thread was holding this lock before fork, it will not exist in the
child process. We should re-initialize these locks as we do with the VM
locks when forking.

Co-authored-by: Aaron Patterson <tenderlove@ruby-lang.org>
2025-03-25 19:14:26 -07:00
Masataka Pocke Kuwabara
0cab608d3a
[Bug #21127] Thread deadlock does not display backtraces (#12721)
Previously, Ruby displayed backtraces for each thread on deadlock. However, it has not been shown since Ruby 3.0.
It should display the backtrace for debugging.

Co-authored-by: Jeremy Evans <code@jeremyevans.net>
2025-02-14 16:31:58 +09:00
Nobuyoshi Nakada
4a67ef09cc
[Feature #21116] Extract RJIT as a third-party gem 2025-02-13 18:01:03 +09:00
Nobuyoshi Nakada
aca0b92c2f prev_mn_schedulable might be clobbered by longjmp 2025-01-30 18:19:53 +09:00
tomoya ishida
2d3d7a74e9
[DOC] Fix wrong call-seq format (#12662) 2025-01-29 21:53:00 +09:00
Nobuyoshi Nakada
9a5ad1b558
Fix -Wsign-compare warning on mingw 2024-12-26 18:21:05 +09:00
Nobuyoshi Nakada
57f6329ba7 Check RUBY_THREAD_TIMESLICE value 2024-12-18 11:12:34 +09:00
Aaron Patterson
fffef9aa5d Add an environment variable for controlling the default Thread quantum
This commit adds an environment variable `RUBY_THREAD_TIMESLICE` for
specifying the default thread quantum in milliseconds.  You can adjust
this variable to tune throughput, which is especially useful on
multithreaded systems that are mixing CPU bound work and IO bound work.

The default quantum remains 100ms.

[Feature #20861]

Co-Authored-By: John Hawthorn <john@hawthorn.email>
2024-12-12 16:04:49 -08:00
Samuel Williams
9c268302bf
Introduce Fiber::Scheduler#blocking_operation_wait. (#12016)
Redirect `rb_nogvl` blocking operations to the fiber scheduler if possible
to prevent stalling the event loop.

[Feature #20876]
2024-11-20 19:40:17 +13:00
Koichi Sasada
97aaf6f760 introduce rb_ec_check_ints()
to avoid TLS issue with N:M threads.
2024-11-08 18:02:46 +09:00
Koichi Sasada
c8297c3eed interrupt_exec
introduce
- rb_threadptr_interrupt_exec
- rb_ractor_interrupt_exec

to intercept the thread/ractor execution.
2024-11-08 18:02:46 +09:00
Samuel Williams
048bb1e176
ubf_th appears to be unused. (#11994) 2024-11-07 02:28:47 +00:00
Samuel Williams
3b9896acfc
Revert "Introduce Fiber Scheduler blocking_region hook. (#11963)" (#12013)
This reverts some of commit 87fb44dff6.

We will rename and propose a slightly different interface.
2024-11-06 22:19:40 +13:00
Nobuyoshi Nakada
24f7829abf
Fix the conditional macro name [ci skip]
`RUBY_VM_CRITICAL_SECTION` is not used anywhere.
2024-11-02 12:04:52 +09:00
Samuel Williams
87fb44dff6
Introduce Fiber Scheduler blocking_region hook. (#11963) 2024-10-31 17:26:37 +13:00
KJ Tsanaktsidis
e08d5239b6 Ensure fiber scheduler is woken up when close interrupts read
If one thread is reading and another closes that socket, the close
blocks waiting for the read to abort cleanly. This ensures that Ruby is
totally done with the file descriptor _BEFORE_ we tell the OS to close
and potentially re-use it.

When the read is correctly terminated, the close should be unblocked.
That currently works if closing is happening on a thread, but if it's
happening on a fiber with a fiber scheduler, it does NOT work.

This patch ensures that if the close happened in a fiber scheduled
thread, that the scheduler is notified that the fiber is unblocked.

[Bug #20723]
2024-09-17 10:11:44 +10:00
Peter Zhu
c996f4091f Ignore -Wdangling-pointer in rb_gc_set_stack_end
Fixes this compiler warning:

    thread.c:4530:18: warning: storing the address of local variable ‘stack_end’ in ‘*stack_end_p’ [-Wdangling-pointer=]
    4530 |     *stack_end_p = &stack_end;
          |     ~~~~~~~~~~~~~^~~~~~~~~~~~
2024-09-13 10:06:57 -04:00
JP Camara
b5f1291015 The Timeout::Error example no longer works consistently
* This PR from the timeout gem (https://github.com/ruby/timeout/pull/30) made it so you have to handle_interrupt on Timeout::ExitException instead of Timeout::Error

* Efficiency changes to the gem (one shared thread) mean you can't consistently handle timeout errors using handle_timeout: https://github.com/ruby/timeout/issues/41
2024-09-09 09:10:06 +09:00
Jeremy Evans
7f1fe5f091 Raise a TypeError for Thread#thread_variable{?,_get} for non-symbol
Previously, a TypeError was not raised if there were no thread
variables, because the conversion to symbol was done after that
check.  Convert to symbol before checking for whether thread
variables are set to make the behavior consistent.

Fixes [Bug #20606]
2024-07-06 12:07:32 +02:00