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).
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]
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>
[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.
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>
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>
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>
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]
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;
| ~~~~~~~~~~~~~^~~~~~~~~~~~
* 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
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]
* Speed up chunkypng benchmark
Since d037c5196a14c03e72746ccdf0437b5dd4f80a69 we're seeing a slowdown
in ChunkyPNG benchmarks in YJIT bench. This patch addresses the
slowdown. Making the thread volatile speeds up the benchmark by 2 or 3%
on my machine.
```
before: ruby 3.4.0dev (2024-07-02T18:48:43Z master b2b8306b46) [x86_64-linux]
after: ruby 3.4.0dev (2024-07-02T20:07:44Z speed-chunkypng 418334dba9) [x86_64-linux]
---------- ----------- ---------- ---------- ---------- ------------- ------------
bench before (ms) stddev (%) after (ms) stddev (%) after 1st itr before/after
chunky-png 1000.2 0.1 980.6 0.1 1.02 1.02
---------- ----------- ---------- ---------- ---------- ------------- ------------
Legend:
- after 1st itr: ratio of before/after time for the first benchmarking iteration.
- before/after: ratio of before/after time. Higher is better for after. Above 1 represents a speedup.
Output:
./data/output_015.csv
```
* Update thread.c
Co-authored-by: Alan Wu <XrXr@users.noreply.github.com>
---------
Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@gmail.com>
Co-authored-by: Alan Wu <XrXr@users.noreply.github.com>
At 7afc16aa48beb093b06eb978bc430f90dd771690, now `BLOCKING_REGION`
contains `setjmp` call in `RB_VM_SAVE_MACHINE_CONTEXT`. By this
change, variables in blocks for this macro may be clobbered.
There's an exhaustive explanation of this in the linked redmine bug, but
the short version is as follows:
blocking_region_begin can spill callee-saved registers to the stack for
its own use. That means they're not saved to ec->machine by the call to
setjmp, since by that point they're already on the stack and new,
different values are in the real registers. ec->machine's end-of-stack
pointer is also bumped to accomodate this, BUT, after
blocking_region_begin returns, that points past the end of the stack!
As far as C is concerned, that's fine; the callee-saved registers are
restored when blocking_region_begin returns. But, if another thread
triggers GC, it is relying on finding references to Ruby objects by
walking the stack region pointed to by ec->machine.
If the C code in exec; subsequently does things that use that stack
memory, then the value will be overwritten and the GC might prematurely
collect something it shouldn't.
[Bug #20493]
Previously it would bypass the `FL_ABLE` check, but
since shapes introduction, it started having a different
behavior than `OBJ_FREEZE`, as it would onyl set the `FL_FREEZE`
flag, but not update the shape.
I have no indication of this causing a bug yet, but it seems
like a trap waiting to happen.