Ruby 4.1.0dev (2026-03-06 revision 9aca729140424bbf465c11ab8ab53e5cc6602c01)
ractor_core.h (9aca729140424bbf465c11ab8ab53e5cc6602c01)
1#include "internal/gc.h"
2#include "ruby/ruby.h"
3#include "ruby/ractor.h"
4#include "vm_core.h"
5#include "id_table.h"
6#include "vm_debug.h"
7
8#ifndef RACTOR_CHECK_MODE
9#define RACTOR_CHECK_MODE (VM_CHECK_MODE || RUBY_DEBUG) && (SIZEOF_UINT64_T == SIZEOF_VALUE)
10#endif
11
12// experimental flag because it is not sure it is the common pattern
13#define RUBY_TYPED_FROZEN_SHAREABLE_NO_REC RUBY_FL_FINALIZE
14
16 // ractor lock
17 rb_nativethread_lock_t lock;
18
19#if RACTOR_CHECK_MODE > 0
20 VALUE locked_by;
21#endif
22
23#ifndef RUBY_THREAD_PTHREAD_H
24 rb_nativethread_cond_t wakeup_cond;
25#endif
26
27 // incoming messages
28 struct ractor_queue *recv_queue;
29
30 // waiting threads for receiving
31 struct ccan_list_head waiters;
32
33 // ports
34 VALUE default_port_value;
35 struct st_table *ports;
36 size_t next_port_id;
37
38 // monitors
39 struct ccan_list_head monitors;
40
41 // value
42 rb_ractor_t *successor;
43 VALUE legacy;
44 bool legacy_exc;
45};
46
47// created
48// | ready to run
49// ====================== inserted to vm->ractor
50// v
51// blocking <---+ all threads are blocking
52// | |
53// v |
54// running -----+
55// | all threads are terminated.
56// ====================== removed from vm->ractor
57// v
58// terminated
59//
60// status is protected by VM lock (global state)
61enum ractor_status {
62 ractor_created,
63 ractor_running,
64 ractor_blocking,
65 ractor_terminated,
66};
67
69 struct rb_ractor_pub pub;
70 struct rb_ractor_sync sync;
71
72 // thread management
73 struct {
74 struct ccan_list_head set;
75 unsigned int cnt;
76 unsigned int blocking_cnt;
77 unsigned int sleeper;
78 struct rb_thread_sched sched;
79 rb_execution_context_t *running_ec;
80 rb_thread_t *main;
81 } threads;
82
83 VALUE thgroup_default;
84
85 VALUE name;
86 VALUE loc;
87
88 enum ractor_status status_;
89
90 struct ccan_list_node vmlr_node;
91
92 // ractor local data
93
94 rb_serial_t next_ec_serial;
95
96 st_table *local_storage;
97 struct rb_id_table *idkey_local_storage;
98 VALUE local_storage_store_lock;
99
100 VALUE r_stdin;
101 VALUE r_stdout;
102 VALUE r_stderr;
103 VALUE verbose;
104 VALUE debug;
105
106 bool malloc_gc_disabled;
107 bool main_ractor;
108 void *newobj_cache;
109}; // rb_ractor_t is defined in vm_core.h
110
111
112static inline VALUE
113rb_ractor_self(const rb_ractor_t *r)
114{
115 return r->pub.self;
116}
117
118rb_ractor_t *rb_ractor_main_alloc(void);
119void rb_ractor_main_setup(rb_vm_t *vm, rb_ractor_t *main_ractor, rb_thread_t *main_thread);
120void rb_ractor_atexit(rb_execution_context_t *ec, VALUE result);
121void rb_ractor_atexit_exception(rb_execution_context_t *ec);
122void rb_ractor_teardown(rb_execution_context_t *ec);
123void rb_ractor_receive_parameters(rb_execution_context_t *ec, rb_ractor_t *g, int len, VALUE *ptr);
124void rb_ractor_send_parameters(rb_execution_context_t *ec, rb_ractor_t *g, VALUE args);
125
126VALUE rb_thread_create_ractor(rb_ractor_t *g, VALUE args, VALUE proc); // defined in thread.c
127
128int rb_ractor_living_thread_num(const rb_ractor_t *);
129VALUE rb_ractor_thread_list(void);
130bool rb_ractor_p(VALUE rv);
131
132void rb_ractor_living_threads_init(rb_ractor_t *r);
133void rb_ractor_living_threads_insert(rb_ractor_t *r, rb_thread_t *th);
134void rb_ractor_living_threads_remove(rb_ractor_t *r, rb_thread_t *th);
135void rb_ractor_blocking_threads_inc(rb_ractor_t *r, const char *file, int line); // TODO: file, line only for RUBY_DEBUG_LOG
136void rb_ractor_blocking_threads_dec(rb_ractor_t *r, const char *file, int line); // TODO: file, line only for RUBY_DEBUG_LOG
137
138void rb_ractor_vm_barrier_interrupt_running_thread(rb_ractor_t *r);
139void rb_ractor_terminate_interrupt_main_thread(rb_ractor_t *r);
140void rb_ractor_terminate_all(void);
141bool rb_ractor_main_p_(void);
142void rb_ractor_atfork(rb_vm_t *vm, rb_thread_t *th);
143void rb_ractor_terminate_atfork(rb_vm_t *vm, rb_ractor_t *th);
144VALUE rb_ractor_require(VALUE feature, bool silent);
145VALUE rb_ractor_autoload_load(VALUE space, ID id);
146
147VALUE rb_ractor_ensure_shareable(VALUE obj, VALUE name);
148st_table *rb_ractor_targeted_hooks(rb_ractor_t *cr);
149
150RUBY_SYMBOL_EXPORT_BEGIN
151void rb_ractor_finish_marking(void);
152
153bool rb_ractor_shareable_p_continue(VALUE obj);
154
155// THIS FUNCTION SHOULD NOT CALL WHILE INCREMENTAL MARKING!!
156// This function is for T_DATA::free_func
157void rb_ractor_local_storage_delkey(rb_ractor_local_key_t key);
158
159RUBY_SYMBOL_EXPORT_END
160
161static inline bool
162rb_ractor_main_p(void)
163{
164 if (ruby_single_main_ractor) {
165 return true;
166 }
167 else {
168 return rb_ractor_main_p_();
169 }
170}
171
172static inline bool
173rb_ractor_status_p(rb_ractor_t *r, enum ractor_status status)
174{
175 return r->status_ == status;
176}
177
178static inline void
179rb_ractor_sleeper_threads_inc(rb_ractor_t *r)
180{
181 r->threads.sleeper++;
182}
183
184static inline void
185rb_ractor_sleeper_threads_dec(rb_ractor_t *r)
186{
187 r->threads.sleeper--;
188}
189
190static inline void
191rb_ractor_sleeper_threads_clear(rb_ractor_t *r)
192{
193 r->threads.sleeper = 0;
194}
195
196static inline int
197rb_ractor_sleeper_thread_num(rb_ractor_t *r)
198{
199 return r->threads.sleeper;
200}
201
202static inline void
203rb_ractor_thread_switch(rb_ractor_t *cr, rb_thread_t *th, bool always_reset)
204{
205 RUBY_DEBUG_LOG("th:%d->%u%s",
206 cr->threads.running_ec ? (int)rb_th_serial(cr->threads.running_ec->thread_ptr) : -1,
207 rb_th_serial(th), cr->threads.running_ec == th->ec ? " (same)" : "");
208
209 if (cr->threads.running_ec != th->ec || always_reset) {
210 th->running_time_us = 0;
211 }
212
213 if (cr->threads.running_ec != th->ec) {
214 if (0) {
215 ruby_debug_printf("rb_ractor_thread_switch ec:%p->%p\n",
216 (void *)cr->threads.running_ec, (void *)th->ec);
217 }
218 }
219 else {
220 return;
221 }
222
223 cr->threads.running_ec = th->ec;
224
225 VM_ASSERT(cr == GET_RACTOR());
226}
227
228#define rb_ractor_set_current_ec(cr, ec) rb_ractor_set_current_ec_(cr, ec, __FILE__, __LINE__)
229#ifdef RB_THREAD_LOCAL_SPECIFIER
230void rb_current_ec_set(rb_execution_context_t *ec);
231#endif
232
233static inline void
234rb_ractor_set_current_ec_(rb_ractor_t *cr, rb_execution_context_t *ec, const char *file, int line)
235{
236#ifdef RB_THREAD_LOCAL_SPECIFIER
237 rb_current_ec_set(ec);
238#else
239 native_tls_set(ruby_current_ec_key, ec);
240#endif
241 RUBY_DEBUG_LOG2(file, line, "ec:%p->%p", (void *)cr->threads.running_ec, (void *)ec);
242 VM_ASSERT(ec == NULL || cr->threads.running_ec != ec);
243 cr->threads.running_ec = ec;
244}
245
246void rb_vm_ractor_blocking_cnt_inc(rb_vm_t *vm, rb_ractor_t *cr, const char *file, int line);
247void rb_vm_ractor_blocking_cnt_dec(rb_vm_t *vm, rb_ractor_t *cr, const char *file, int line);
248
249static inline uint32_t
250rb_ractor_id(const rb_ractor_t *r)
251{
252 return r->pub.id;
253}
254
255static inline void
256rb_ractor_targeted_hooks_incr(rb_ractor_t *cr)
257{
258 cr->pub.targeted_hooks_cnt++;
259}
260
261static inline void
262rb_ractor_targeted_hooks_decr(rb_ractor_t *cr)
263{
264 RUBY_ASSERT(cr->pub.targeted_hooks_cnt > 0);
265 cr->pub.targeted_hooks_cnt--;
266}
267
268static inline unsigned int
269rb_ractor_targeted_hooks_cnt(rb_ractor_t *cr)
270{
271 return cr->pub.targeted_hooks_cnt;
272}
273
274#if RACTOR_CHECK_MODE > 0
275# define RACTOR_BELONGING_ID(obj) (*(uint32_t *)(((uintptr_t)(obj)) + rb_gc_obj_slot_size(obj)))
276
277uint32_t rb_ractor_current_id(void);
278
279static inline void
280rb_ractor_setup_belonging_to(VALUE obj, uint32_t rid)
281{
282 RACTOR_BELONGING_ID(obj) = rid;
283}
284
285static inline uint32_t
286rb_ractor_belonging(VALUE obj)
287{
288 if (SPECIAL_CONST_P(obj) || RB_OBJ_SHAREABLE_P(obj)) {
289 return 0;
290 }
291 else {
292 return RACTOR_BELONGING_ID(obj);
293 }
294}
295
296extern bool rb_ractor_ignore_belonging_flag;
297
298static inline VALUE
299rb_ractor_confirm_belonging(VALUE obj)
300{
301 if (rb_ractor_ignore_belonging_flag) return obj;
302
303 uint32_t id = rb_ractor_belonging(obj);
304
305 if (id == 0) {
306 if (UNLIKELY(!rb_ractor_shareable_p(obj))) {
307 rp(obj);
308 rb_bug("id == 0 but not shareable");
309 }
310 }
311 else if (UNLIKELY(id != rb_ractor_current_id())) {
312 if (rb_ractor_shareable_p(obj)) {
313 // ok
314 }
315 else {
316 rp(obj);
317 rb_bug("rb_ractor_confirm_belonging object-ractor id:%u, current-ractor id:%u", id, rb_ractor_current_id());
318 }
319 }
320 return obj;
321}
322
323static inline void
324rb_ractor_ignore_belonging(bool flag)
325{
326 rb_ractor_ignore_belonging_flag = flag;
327}
328
329#else
330#define rb_ractor_confirm_belonging(obj) obj
331#define rb_ractor_ignore_belonging(flag) (0)
332#endif
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
Definition assert.h:219
#define SPECIAL_CONST_P
Old name of RB_SPECIAL_CONST_P.
int len
Length of the buffer.
Definition io.h:8
static bool rb_ractor_shareable_p(VALUE obj)
Queries if multiple Ractors can share the passed object or not.
Definition ractor.h:249
#define RB_OBJ_SHAREABLE_P(obj)
Queries if the passed object has previously classified as shareable or not.
Definition ractor.h:235
Definition st.h:79
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
Definition value.h:52
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40