summaryrefslogtreecommitdiff
path: root/thread.c
AgeCommit message (Collapse)Author
2025-12-20Small documentation adjustments for new/updated features (#15634)Victor Shepelev
* Document Range#to_set * Update Thread#raise and Fiber#raise signatures and docs * Add reference to String#strip to character_selectors.rdoc * Update *nil docs when calling methods * Enhance Array#find and #rfind docs * Add a notice to Kernel#raise about cause:
2025-12-18Store ractor_id directly on ECJohn Hawthorn
This is easier to access as ec->ractor_id instead of pointer-chasing through ec->thread->ractor->ractor_id Co-authored-by: Luke Gruber <luke.gru@gmail.com>
2025-12-16Rename fiber_serial into ec_serialJean Boussier
Since it now live in the EC.
2025-12-16Store the fiber_serial in the EC to allow inliningJean Boussier
Mutexes spend a significant amount of time in `rb_fiber_serial` because it can't be inlined (except with LTO). The fiber struct is opaque the so function can't be defined as inlineable. Ideally the while fiber struct would not be opaque to the rest of Ruby core, but it's tricky to do. Instead we can store the fiber serial in the execution context itself, and make its access cheaper: ``` $ hyperfine './miniruby-baseline --yjit /tmp/mut.rb' './miniruby-inline-serial --yjit /tmp/mut.rb' Benchmark 1: ./miniruby-baseline --yjit /tmp/mut.rb Time (mean ± σ): 4.011 s ± 0.084 s [User: 3.977 s, System: 0.011 s] Range (min … max): 3.950 s … 4.245 s 10 runs Benchmark 2: ./miniruby-inline-serial --yjit /tmp/mut.rb Time (mean ± σ): 3.495 s ± 0.150 s [User: 3.448 s, System: 0.009 s] Range (min … max): 3.340 s … 3.869 s 10 runs Summary ./miniruby-inline-serial --yjit /tmp/mut.rb ran 1.15 ± 0.05 times faster than ./miniruby-baseline --yjit /tmp/mut.rb ``` ```ruby i = 10_000_000 mut = Mutex.new while i > 0 i -= 1 mut.synchronize { } mut.synchronize { } mut.synchronize { } mut.synchronize { } mut.synchronize { } mut.synchronize { } mut.synchronize { } mut.synchronize { } mut.synchronize { } mut.synchronize { } end ```
2025-12-12Fewer calls to `GET_EC()` and `GET_THREAD()` (#15506)Luke Gruber
The changes are to `io.c` and `thread.c`. I changed the API of 2 exported thread functions from `internal/thread.h` that didn't look like they had any use in C extensions: * rb_thread_wait_for_single_fd * rb_thread_io_wait I didn't change the following exported internal function because it's used in C extensions: * rb_thread_fd_select I added a comment to note that this function, although internal, is used in C extensions.
2025-12-06Yield to scheduler if interrupts are pending. (#14700)Samuel Williams
2025-12-05Allow rb_thread_call_with_gvl() to work when thread already has GVLKeenan Brock
[Feature #20750] Co-authored-by: Benoit Daloze <eregontp@gmail.com>
2025-12-05Correctly handle `Process.fork` with an active `Fiber.scheduler`. (#15385)Sharon Rosner
In the child process, nullify the current fiber scheduler and set the current fiber to blocking.
2025-12-01Nullify scheduler during `terminate_atfork_i`. (#15354)Samuel Williams
2025-11-27Remove unneeded trailing semicolonsS-H-GAMELINKS
2025-11-20Use a serial to keep track of Mutex-owning FiberJohn Hawthorn
Previously this held a pointer to the Fiber itself, which requires marking it (which was only implemented recently, prior to that it was buggy). Using a monotonically increasing integer instead allows us to avoid having a free function and keeps everything simpler. My main motivations in making this change are that the root fiber lazily allocates self, which makes the writebarrier implementation challenging to do correctly, and wanting to avoid sending Mutexes to the remembered set when locked by a short-lived Fiber.
2025-11-10[DOC] Clarify `Thread#kill` documentation. (#15132)Luke Gruber
Mention that it is asynchronous and that the killed thread can still run a small amount of ruby code before exiting.
2025-10-04Free the native thread of the main thread on FREE_AT_EXITPeter Zhu
2025-09-25Properly unlock locked mutexes on thread cleanup.Luke Gruber
Mutexes were being improperly unlocked on thread cleanup. This bug was introduced in 050a8954395. We must keep a reference from the mutex to the thread, because if the fiber is collected before the mutex is, then we cannot unlink it from the thread in `mutex_free`. If it's not unlinked from the the thread when it's freed, it causes bugs in `rb_thread_unlock_all_locking_mutexes`. We now mark the fiber when a mutex is locked, and the thread is marked as well. However, a fiber can still be freed in the same GC cycle as the mutex, so the reference to the thread is still needed. The reason we need to mark the fiber is that `mutex_owned_p()` has an ABA issue where if the fiber is collected while it's locked, a new fiber could be allocated at the same memory address and we could get false positives. Fixes [Bug #21342] Co-authored-by: John Hawthorn <john@hawthorn.email>
2025-09-24Ractor.shareable_procKoichi Sasada
call-seq: Ractor.sharable_proc(self: nil){} -> 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 } #=> OK b = 43 Ractor.shareable_proc{ p b; b = 44 } #=> Ractor::IsolationError because 'b' is reassigned in the block. c = 44 Ractor.shareable_proc{ p c } #=> Ractor::IsolationError because 'c' will be reassigned outside of the block. c = 45 d = 45 d = 46 if cond Ractor.shareable_proc{ p d } #=> 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 } #=> OK binding.local_variable_set(:e, 43) shpr.call #=> 42 (returns captured timing value) ``` Ractor.sharaeble_lambda is also introduced. [Feature #21550] [Feature #21557]
2025-07-24Support `cause:` in `Thread#raise` and `Fiber#raise`. (#13967)Samuel Williams
* Add support for `cause:` argument to `Fiber#raise` and `Thread#raise`. The implementation behaviour is consistent with `Kernel#raise` and `Exception#initialize` methods, allowing the `cause:` argument to be passed to `Fiber#raise` and `Thread#raise`. This change ensures that the `cause:` argument is handled correctly, providing a more consistent and expected behavior when raising exceptions in fibers and threads. [Feature #21360] * Shared specs for Fiber/Thread/Kernel raise. --------- Co-authored-by: Samuel Williams <samuel.williams@shopify.com>
2025-07-10Fix rb_eSystemExit raised in RactorJohn Hawthorn
[Bug #21505] Previously `Ractor.new { exit }.join` would hang because SystemExit was special cased. This commit updates this to take the same path as other exceptions, which wraps the exception in a Ractor::RemoteError and does not end up exiting the main Ractor. I don't know if that's what this should do, but I think it's a reasonable behaviour as calling exit() in a Ractor is odd. in 'Ractor#join': thrown by remote Ractor. (Ractor::RemoteError) from -e:1:in '<main>' in 'Kernel#exit': exit (SystemExit) from -e:1:in 'block in <main>'
2025-06-28Fix race condition in signal handler query (#13712)Erik Berlin
* Fix race condition in signal handler query * Initialize signal lock dynamically and reset after fork * Fix signal handler mutex initialization conditions
2025-06-27Fix the unknown warning group on wasmNobuyoshi Nakada
2025-06-24Allow wakeup mutex to be used in trap context. (#13684)Samuel Williams
2025-06-17* adjust indentNobuyoshi Nakada
Notes: Merged: https://github.com/ruby/ruby/pull/13634
2025-06-14Fix blocking operation cancellation. (#13614)Samuel Williams
Expose `rb_thread_resolve_unblock_function` internally. Notes: Merged-By: ioquatix <samuel@codeotaku.com>
2025-06-12Add a new_thread flag to rb_interrupt_execJohn Hawthorn
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. Notes: Merged: https://github.com/ruby/ruby/pull/13531
2025-06-12Free rb_native_thread memory at forkPeter Zhu
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. Notes: Merged: https://github.com/ruby/ruby/pull/13591
2025-06-06Fix `blocking_operation_wait` use-after-free bug.Samuel Williams
Notes: Merged: https://github.com/ruby/ruby/pull/13437
2025-06-06`rb_io_blocking_operation_exit` should not execute with pending interrupts.Samuel Williams
Notes: Merged: https://github.com/ruby/ruby/pull/13437
2025-06-06Handle spurious wakeups in `Thread#join`. (#13532)Samuel Williams
Notes: Merged-By: ioquatix <samuel@codeotaku.com>
2025-06-06[Bug #21400] Fix rb_bug() when killing current root fiber in non-main thread ↵Luke Gruber
(#13526) Fixes the following: ```ruby Thread.new { Fiber.current.kill }.join ``` Notes: Merged-By: ioquatix <samuel@codeotaku.com>
2025-06-04Suppress dangling pointer warning by gccNobuyoshi Nakada
`__has_warning` is clang, not gcc. Notes: Merged: https://github.com/ruby/ruby/pull/13509
2025-06-02Fix compatibility with fiber schedulers that don't implement ↵Samuel Williams
`#fiber_interrupt`. (#13492) Notes: Merged-By: ioquatix <samuel@codeotaku.com>
2025-05-31`Ractor::Port`Koichi Sasada
* 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 Notes: Merged: https://github.com/ruby/ruby/pull/13445
2025-05-25Use RB_VM_LOCKINGNobuyoshi Nakada
Notes: Merged: https://github.com/ruby/ruby/pull/13439
2025-05-23Fix warning on cygwinDaisuke Fujimura (fd0)
Notes: Merged: https://github.com/ruby/ruby/pull/13425
2025-05-23Allow `IO#close` to interrupt IO operations on fibers using ↵Samuel Williams
`fiber_interrupt` hook. (#12839) Notes: Merged-By: ioquatix <samuel@codeotaku.com>
2025-05-20Use atomic load to read interrupt maskJohn Hawthorn
Notes: Merged: https://github.com/ruby/ruby/pull/13357
2025-05-15Use atomics for system_working globalJohn Hawthorn
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). Notes: Merged: https://github.com/ruby/ruby/pull/13333
2025-05-15Force reset running time in timer interruptJohn Hawthorn
Co-authored-by: Ivo Anjo <ivo.anjo@datadoghq.com> Co-authored-by: Luke Gruber <luke.gru@gmail.com> Notes: Merged: https://github.com/ruby/ruby/pull/12094
2025-05-15Align styles [ci skip]Nobuyoshi Nakada
2025-05-15Ensure that forked process do not see invalid blocking operations. (#13343)Samuel Williams
Notes: Merged-By: ioquatix <samuel@codeotaku.com>
2025-05-13Get ractor message passing working with > 1 thread sending/receiving values ↵Luke Gruber
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] Notes: Merged: https://github.com/ruby/ruby/pull/12633
2025-05-13Make `waiting_fd` behaviour per-IO. (#13127)Samuel Williams
- `rb_thread_fd_close` is deprecated and now a no-op. - IO operations (including close) no longer take a vm-wide lock. Notes: Merged-By: ioquatix <samuel@codeotaku.com>
2025-05-08Clean up Ractor cache after forkAaron Patterson
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> Notes: Merged: https://github.com/ruby/ruby/pull/12982
2025-04-19Fix style [ci skip]Nobuyoshi Nakada
2025-04-19Ensure `struct rb_io` is passed through to `thread.c`. (#13134)Samuel Williams
Notes: Merged-By: ioquatix <samuel@codeotaku.com>
2025-04-14Expose `ruby_thread_has_gvl_p`.Samuel Williams
Notes: Merged: https://github.com/ruby/ruby/pull/11975
2025-04-09Fix coverage measurement for negative line numbersYusuke Endoh
Fixes [Bug #21220] Co-Authored-By: Mike Bourgeous <mike@mikebourgeous.com> Co-Authored-By: Jean Boussier <jean.boussier@gmail.com> Notes: Merged: https://github.com/ruby/ruby/pull/13089
2025-03-31Initialize ractor thgroup in `thread_do_start_proc`Jean Boussier
Followup: https://github.com/ruby/ruby/pull/13013 Notes: Merged: https://github.com/ruby/ruby/pull/13016
2025-03-31ractor: don't inherit the default thread groupJean Boussier
[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. Notes: Merged: https://github.com/ruby/ruby/pull/13013
2025-03-25Reset thread interrupt lock on forkJohn Hawthorn
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> Notes: Merged: https://github.com/ruby/ruby/pull/12981
2025-02-14[Bug #21127] Thread deadlock does not display backtraces (#12721)Masataka Pocke Kuwabara
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> Notes: Merged-By: pocke <p.ck.t22@gmail.com>