mirror of
https://github.com/ruby/ruby.git
synced 2026-10-28 08:25:20 +08:00
Under the M:N thread scheduler, a Ruby thread in a non-main Ractor may be
resumed on a different native thread after it blocks. The C compiler
assumes that the native thread never changes within a function, so it may
compute the address of ruby_current_ec once (`__tls_get_addr` in
libruby.so) and reuse it after the switch. The address then refers to the
TLS of the previous native thread, which leads to a SEGV or to using
another thread's ec.
8.times.map do
Ractor.new do
50.times do
",,".split(/(,)/) { sleep 0.001 }
end
:ok
end
end.map(&:value)
#=> [BUG] Segmentation fault at 0x0000000000000030 (with Clang 21)
arm64 and ppc64le already use the out-of-line rb_current_ec()
(RB_THREAD_CURRENT_EC_NOINLINE), which prevents the compiler from caching
the address. Use it on all platforms, and remove the macro.
[Bug #22453]
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
27 lines
1.0 KiB
C
27 lines
1.0 KiB
C
#ifndef RUBY_THREAD_NONE_H
|
|
#define RUBY_THREAD_NONE_H
|
|
|
|
#define RB_NATIVETHREAD_LOCK_INIT (void)(0)
|
|
#define RB_NATIVETHREAD_COND_INIT (void)(0)
|
|
|
|
// no-thread impl doesn't use TLS but define this to avoid using tls key
|
|
// based implementation in vm.c
|
|
#define RB_THREAD_LOCAL_SPECIFIER
|
|
|
|
// This model brings its own scheduler stubs (thread_none.c) instead of the
|
|
// common one; thread.c keys off this.
|
|
#define RB_THREAD_SCHED_NONE 1
|
|
|
|
// The scheduler's types are shared with the threaded platforms: the code that
|
|
// reads them (vm_sync.c, ractor.c, thread.c) is compiled for every thread
|
|
// model, so it needs the real fields even here, where nothing ever runs
|
|
// concurrently and they all stay zero. thread_none.c only ever passes the
|
|
// structs around, never looks inside them.
|
|
#include "thread_sched.h"
|
|
|
|
RUBY_EXTERN struct rb_execution_context_struct *ruby_current_ec;
|
|
NOINLINE(struct rb_execution_context_struct *rb_current_ec_noinline(void)); // for assertions
|
|
struct rb_execution_context_struct *rb_current_ec(void);
|
|
|
|
#endif /* RUBY_THREAD_NONE_H */
|