This commit introduces Ractor mechanism to run Ruby program in
parallel. See doc/ractor.md for more details about Ractor.
See ticket [Feature #17100] to see the implementation details
and discussions.
[Feature #17100]
This commit does not complete the implementation. You can find
many bugs on using Ractor. Also the specification will be changed
so that this feature is experimental. You will see a warning when
you make the first Ractor with `Ractor.new`.
I hope this feature can help programmers from thread-safety issues.
After 5e86b005c0, I now think ANYARGS is
dangerous and should be extinct. This commit deletes ANYARGS from
rb_ensure, which also revealed many arity / type mismatches.
* internal.h (UNALIGNED_MEMBER_ACCESS, UNALIGNED_MEMBER_PTR):
moved from eval_intern.h.
* compile.c iseq.c, vm.c: use UNALIGNED_MEMBER_PTR for `entries`
in `struct iseq_catch_table`.
* vm_eval.c, vm_insnhelper.c: use UNALIGNED_MEMBER_PTR for `body`
in `rb_method_definition_t`.
The true bug fork_gen was hiding was rb_mutex_abandon_locking_mutex
failing to unconditionally clear the waitq of mutexes it was
waiting on. So we fix rb_mutex_abandon_locking_mutex, instead,
and eliminate rb_mutex_cleanup_keeping_mutexes.
This commit was tested heavily on a single-core Pentium-M which
was my most reliable reproducer of the "crash.rb" script from
[Bug #15383]
[Bug #14578] [Bug #15383]
Note: [Bug #15430] turned out to be an entirely different
problem: RLIMIT_NPROC limit was hit on the CI VMs.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@66489 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
... when clearing waitq. Otherwise, we risk redundantly clearing
valid waiters in future calls to `mutex_ptr`.
Note: I am not sure if this fixes [Bug #15430], and even if it
did, fork_gen is a belt-and-suspenders redundancy for [Bug #15383]
which wastes one word for every Mutex object.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@66477 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
rb_mutex_abandon_all functions fine when passed a NULL value,
so let the compiler deal with the complexity of the branch
instead of the person reading the code.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@66476 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Mutexes need to remain locked after forking.
This fixes "[BUG] invalid keeping_mutexes: Attempt to unlock a
mutex which is locked by another thread" and should
fix test_fork_while_parent_locked failures in CI
[ruby-core:90581] [Bug #15424]
[ruby-core:90595] [Bug #15430]
Fixes: r66230 ("handle mutexes held by parent threads in children")
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@66438 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Especially over checking argc then calling rb_scan_args just to
raise an ArgumentError.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@66238 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Mutexes may be held by threads which only exist in the parent
process, so their waitqueues may be populated with references
to other dead threads. We must reset them at fork.
I am a moron for introducing this bug :<
[ruby-core:90312] [Bug #15383]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@66230 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Patch by Lars Kanis. [Fix GH-1949]
* Describe the impact of Queue#close to Queue#deq .
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@64756 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This let us avoid looping in rb_szqueue_max_set, saves us
some lines of code and reduces binary size slightly
(numbers from 32-bit x86):
text data bss dec hex filename
before: 91272 392 156 91820 166ac thread.o
after: 91200 392 156 91748 16664 thread.o
Inspiration from this taken from the FUTEX_WAKE op
of the Linux futex(2) syscall.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@64542 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Relying on "struct timespec" was too annoying API-wise and
used more stack space. "double" was a bit wacky w.r.t rounding
in the past, so now we'll switch to using a 64-bit type.
Unsigned 64-bit integer is able to give us over nearly 585
years of range with nanoseconds. This range is good enough
for the Linux kernel internal time representation, so it
ought to be good enough for us.
This reduces the stack usage of functions while GVL is held
(and thus subject to marking) on x86-64 Linux (with ppoll):
rb_wait_for_single_fd 120 => 104
do_select 120 => 88
[ruby-core:88582] [Misc #15014]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@64533 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Following ko1's lead in r59192, this gets rid of non-obvious
assignments which happen inside macros.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@64490 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
According to r52446, it is only necessary for the current item (@i),
not the `@nxt` parameter for list_for_each_safe.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@64486 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This is needed to reliably fix ConditionVariable#wait on Thread#kill
[Bug #14999] because there is still a chance an interrupt could fire
and prevent lock acquisition after an ensure statement.
Arguably, rb_mutex_lock itself should be uninterruptible, but that
already prevents bootstraptest/test_thread.rb from completing and
probably breaks existing use cases.
For reference, POSIX expressly forbids EINTR from pthread_mutex_lock.
[ruby-core:88556] [Bug #14999]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@64476 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
GCC is smart enough to optimize these away, but my attention
span is too short :{
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@64474 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
We do not want to risk switching threads before going to sleep
because it can cause unexpected wakeups and put us in an
unexpected state when used with ConditionVariable.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@64464 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
* This reverts commit 647fc1227a4146ecbfeb0d59358abc8d99cd8ae6:
"thread_sync.c (rb_mutex_synchronize): only unlock if we own the mutex"
* Let's try to preserve the semantics of always being locked inside
Mutex#synchronize, even if an exception interrupts ConditionVariable#wait.
* As discussed on [Bug #14999].
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@64448 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
If an exception is raised inside Mutex#sleep (via ConditionVariable#wait),
we cannot guarantee we can own the mutex in the ensure callback.
However, who owns the mutex at that point does not matter. What
matters is the Mutex is usable after an exception occurs.
* thread_sync.c (rb_mutex_synchronize): only unlock if we own the mutex
* spec/ruby/library/conditionvariable/wait_spec.rb: only test lock
usability after thread kill. Who owns the lock at any
particular moment is an implementation detail which we cannot
easily guarantee.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@64441 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This reverts commit d7ddbff295 (r64436)
Seems worthless at preventing CI failures
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@64440 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Calling rb_mutex_sleep directly should avoid
thread-switching/interrupt checking which can lead to occasional
failures.
Unfortunately, this means overriding Mutex#sleep is no longer
supported. Will let this commit run for a bit see if CI failures
from ConditionVariable specs continue...
cf. https://rubyci.org/logs/rubyci.s3.amazonaws.com/ubuntu/ruby-trunk/log/20180817T213003Z.fail.html.gz
[ruby-core:88524] [Bug #14999]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@64436 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
We (the thread acquiring the mutex) need to acquire the mutex
before being killed to work with ConditionVariable#wait.
Thus we reinstate the acquire-immediately-after-sleeping logic
from pre-r63711 while still retaining the
acquire-after-checking-for-interrupts logic from r63711.
This regression was introduced in
commit 501069b8a4 (r63711)
("thread_sync.c (rb_mutex_lock): fix deadlock") for
[Bug #14841]
[ruby-core:88503] [Bug #14999] [Bug #14841]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@64398 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
`trunk@42862` dropped example's last line.
e334bb2ce5 (diff-8783a9b452e430bcf0d7b0c6e34f1db0L144)e334bb2ce5 (diff-38e7b9d781319cfbc49445f8f6625b8aR195)
This brings no output.
```queue_example1.rb
queue = Queue.new
producer = Thread.new do
5.times do |i|
sleep rand(i) # simulate expense
queue << i
puts "#{i} produced"
end
end
consumer = Thread.new do
5.times do |i|
value = queue.pop
sleep rand(i/2) # simulate expense
puts "consumed #{value}"
end
end
```
```queue_example2.rb
queue = Queue.new
producer = Thread.new do
5.times do |i|
sleep rand(i) # simulate expense
queue << i
puts "#{i} produced"
end
end
consumer = Thread.new do
5.times do |i|
value = queue.pop
sleep rand(i/2) # simulate expense
puts "consumed #{value}"
end
end
consumer.join
```
$ ruby queue_example1.rb
$
$ ruby queue_example2.rb
0 produced
1 produced
consumed 0
consumed 1
2 produced
consumed 2
3 produced
consumed 3
4 produced
consumed 4
$
Co-Authored-By: Sanemat <o.gata.ken@gmail.com>
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@64058 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
* include/ruby/ruby.h (UNREACHABLE_RETURN): UNREACHABLE at the end
of non-void functions.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@64025 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Otherwise the condition variable waiter list will always
be empty, which is wrong :x
[Bug #14725] [Bug #14634]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@63309 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
I may store ->ec in rb_mutex_t instead of ->th to support green
thread. For now, "volatile" is useless for thread-safety
anyways and we're not dealing with *jmp or signals with mutex.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@63282 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
I forgot to clean these up in r63215
* thread_sync.c (szqueue_list, queue_list, condvar_list): remove
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@63281 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Instead of maintaining linked-lists to store all
rb_queue/rb_szqueue/rb_condvar structs; store only a fork_gen
serial number to simplify management of these items.
This reduces initialization costs and avoids the up-front cost
of resetting all Queue/SizedQueue/ConditionVariable objects at
fork while saving 8 bytes per-structure on 64-bit. There are no
savings on 32-bit.
* thread.c (rb_thread_atfork_internal): remove rb_thread_sync_reset_all call
* thread_sync.c (rb_thread_sync_reset_all): remove
* thread_sync.c (queue_live): remove
* thread_sync.c (queue_free): remove
* thread_sync.c (struct rb_queue): s/live/fork_gen/
* thread_sync.c (queue_data_type): use default free
* thread_sync.c (queue_alloc): remove list_add
* thread_sync.c (queue_fork_check): new function
* thread_sync.c (queue_ptr): call queue_fork_check
* thread_sync.c (szqueue_free): remove
* thread_sync.c (szqueue_data_type): use default free
* thread_sync.c (szqueue_alloc): remove list_add
* thread_sync.c (szqueue_ptr): check fork_gen via queue_fork_check
* thread_sync.c (struct rb_condvar): s/live/fork_gen/
* thread_sync.c (condvar_free): remove
* thread_sync.c (cv_data_type): use default free
* thread_sync.c (condvar_ptr): check fork_gen
* thread_sync.c (condvar_alloc): remove list_add
[ruby-core:86316] [Bug #14634]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@63215 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
rb_ensure is insufficient cleanup for fork and we must
reinitialize all waitqueues in the child process.
Unfortunately this increases the footprint of ConditionVariable,
Queue and SizedQueue by 8 bytes on 32-bit (16 bytes on 64-bit).
[ruby-core:86316] [Bug #14634]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62934 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
We must not maintain references to threads in the parent process
in any mutexes held by the child process.
* thread_sync.c (rb_mutex_cleanup_keeping_mutexes): new function
* thread.c (rb_thread_atfork): cleanup keeping mutexes
[ruby-core:85940] [Bug #14578]
Fixes: r58604 (commit 3586c9e087)
("reduce rb_mutex_t size from 160 to 80 bytes on 64-bit")
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62668 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This results in fewer conversion on common modern systems with
support for clock_gettime, pthread_cond_timedwait and ppoll.
gettimeofday is declared obsolete by POSIX.1-2008, so it is yet
another reason to move away from it. This also appears to result
in the reduction of compatibility code required for dealing
with inconsistent implementations of "struct timeval".tv_sec
In the future, this will also result in fewer conversions for
kqueue and pselect if we elect to use them.
[ruby-core:85416] [Feature #14452]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62272 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
When Queue#closed? is true, using push/enq/<< will raise an
exception, and not return nil.
* thread_sync.c: fix documentation for Queue#close
Thanks to John Anderson. [ruby-core:81749] [Misc #13673]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@59165 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
The performance improvement increases as the number of waiters
increases, due to avoiding the O(n) behavior of rb_ary_delete on
the waiting thread. Uncontended queues and condition variables
performance is not altered significantly.
Function entry cost is slightly increased for ConditionVariable,
since the data pointer is separately allocated and not embedded
into the RVALUE slot.
[ruby-core:81235] [Feature #13552]
name |trunk |built
----------------------|------:|------:
vm_thread_condvar1 | 0.858| 0.858
vm_thread_condvar2 | 1.003| 0.804
vm_thread_queue | 0.131| 0.129
vm_thread_sized_queue | 0.265| 0.251
vm_thread_sized_queue2| 0.892| 0.859
vm_thread_sized_queue3| 0.879| 0.845
vm_thread_sized_queue4| 0.599| 0.486
Speedup ratio: compare with the result of `trunk' (greater is better)
name |built
----------------------|------:
vm_thread_condvar1 | 0.999
vm_thread_condvar2 | 1.246
vm_thread_queue | 1.020
vm_thread_sized_queue | 1.057
vm_thread_sized_queue2| 1.039
vm_thread_sized_queue3| 1.041
vm_thread_sized_queue4| 1.233
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@58805 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
We will reuse this struct for ConditionVariable, Queue, and SizedQueue,
so it is no longer Mutex-specific.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@58804 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
We can use existing RVALUE flags to avoid adding a 4-byte
integer to store a boolean flag. This integer cost us 8 bytes
due to default (lack of) struct packing on x86-64
* thread_sync.c (MUTEX_ALLOW_TRAP): define as FL_USER1
(struct rb_mutex_struct): remove allow_trap
(rb_mutex_lock): adjust for flag check
(rb_mutex_allow_trap): adjust for flag set/unset
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@58608 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Instead of relying on a native condition variable and mutex for
every Ruby Mutex object, use a doubly linked-list to implement a
waiter queue in the Mutex. The immediate benefit of this is
reducing the size of every Mutex object, as some projects have
many objects requiring synchronization.
In the future, this technique using a linked-list and on-stack
list node (struct mutex_waiter) should allow us to easily
transition to M:N threading model, as we can avoid the native
thread dependency to implement Mutex.
We already do something similar for autoload in variable.c,
and this was inspired by the Linux kernel wait queue (as
ccan/list is inspired by the Linux kernel linked-list).
Finaly, there are big performance improvements for Mutex
benchmarks, especially in contended cases:
measure target: real
name |trunk |built
----------------|------:|------:
loop_whileloop2 | 0.149| 0.148
vm2_mutex* | 0.893| 0.651
vm_thread_mutex1| 0.809| 0.624
vm_thread_mutex2| 2.608| 0.628
vm_thread_mutex3| 28.227| 0.881
Speedup ratio: compare with the result of `trunk' (greater is better)
name |built
----------------|------:
loop_whileloop2 | 1.002
vm2_mutex* | 1.372
vm_thread_mutex1| 1.297
vm_thread_mutex2| 4.149
vm_thread_mutex3| 32.044
Tested on AMD FX-8320 8-core at 3.5GHz
* thread_sync.c (struct mutex_waiter): new on-stack struct
(struct rb_mutex_struct): remove native lock/cond, use ccan/list
(rb_mutex_num_waiting): new function for debug_deadlock_check
(mutex_free): remove native_*_destroy
(mutex_alloc): initialize waitq, remove native_*_initialize
(rb_mutex_trylock): remove native_mutex_{lock,unlock}
(lock_func): remove
(lock_interrupt): remove
(rb_mutex_lock): rewrite waiting path to use native_sleep + ccan/list
(rb_mutex_unlock_th): rewrite to wake up from native_sleep
using rb_threadptr_interrupt
(rb_mutex_abandon_all): empty waitq
* thread.c (debug_deadlock_check): update for new struct
(rb_check_deadlock): ditto
[ruby-core:80913] [Feature #13517]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@58604 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
* thread_sync.c (rb_szqueue_clear): fix class name in RDoc
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@58511 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
* thread_sync.c: allow spurious wakeup to check Queue status just after trap.
[Bug #12405]
* test/thread/test_queue.rb: add a test for it.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@57479 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
* thread.c, thread_sync.c: define new function
rb_thread_sleep_deadly_allow_spurious_wakeup() and use it instead of
using sleep_forever() directly.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@57478 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
* thread_sync.c (Init_thread_sync): Remove confusing doc comments,
which are picked up by rdoc unexpectedly, from Queue and
SizedQueue. [Fix GH-1450]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@56285 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
* thread_sync.c (alias_global_const): extract from a macro.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@56023 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
* *.c: rename rb_funcall2 to rb_funcallv, except for extensions
which are/will be/may be gems. [Fix GH-1406]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55773 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
* thread.c (rb_thread_atfork, rb_thread_atfork_before_exec): do
nothing unless working fork is available.
* thread_sync.c (rb_mutex_abandon_all): define only if working
fork is available.
* thread_sync.c (rb_mutex_abandon_keeping_mutexes): ditto.
* thread_sync.c (rb_mutex_abandon_locking_mutex): ditto.
* thread_win32.c (gvl_init): never used.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@54960 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
* thread_sync.c (ALIAS_GLOBAL_CONST): define global constant
aliases unconditionally. same constants are not defined at this
time.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@54826 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
"Qfalse" and "(VALUE)0" are equivalent, and do not matter
for an ignored argument, either.
Additionally, there's no reason to cast the pointer to
rb_thread_sleep_deadly when we already have a queue_sleep
wrapper.
* thread_sync.c (queue_do_pop): avoid cast with Qfalse
(rb_szqueue_push): ditto, use queue_sleep wrapper
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@53413 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
We do not want to waste space by exposing globals to other
objects.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@53364 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
NULL checking is finished Before call of memsize functions.
See r52979.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@52986 b2dd03c8-39d4-4d8f-98ff-823fe69b080e