Ruby 4.1.0dev (2026-08-15 revision 6cc5c920b029a686ef6fbf93ffb11f57fb312dee)
thread_pthread.h (6cc5c920b029a686ef6fbf93ffb11f57fb312dee)
1#ifndef RUBY_THREAD_PTHREAD_H
2#define RUBY_THREAD_PTHREAD_H
3/**********************************************************************
4
5 thread_pthread.h -
6
7 $Author$
8
9 Copyright (C) 2004-2007 Koichi Sasada
10
11**********************************************************************/
12
13#ifdef HAVE_PTHREAD_NP_H
14#include <pthread_np.h>
15#endif
16
17#define RB_NATIVETHREAD_LOCK_INIT PTHREAD_MUTEX_INITIALIZER
18#define RB_NATIVETHREAD_COND_INIT PTHREAD_COND_INITIALIZER
19
20// TLS can not be accessed across .so on arm64 and perhaps ppc64le too.
21#if defined(__arm64__) || defined(__aarch64__) || defined(__powerpc64__)
22# define RB_THREAD_CURRENT_EC_NOINLINE
23#endif
24
25// How a thread_sched_wait_events() wait ended. "unavailable" (could not be
26// registered) is not "the event fired": the caller must fall back, not proceed.
27enum thread_sched_wait_result {
28 thread_sched_wait_event, // an event the caller asked for fired
29 thread_sched_wait_timeout, // the timeout expired before any event
30 thread_sched_wait_unavailable, // not registered; the caller must fall back
31};
32
33// this data should be protected by timer_th.waiting_lock
35 enum thread_sched_waiting_flag {
36 thread_sched_waiting_none = 0x00,
37 thread_sched_waiting_timeout = 0x01,
38 thread_sched_waiting_io_read = 0x02,
39 thread_sched_waiting_io_write = 0x08,
40 thread_sched_waiting_io_force = 0x40, // ignore readable
41 } flags;
42
43 struct {
44 // should be compat with hrtime.h
45#ifdef MY_RUBY_BUILD_MAY_TIME_TRAVEL
46 int128_t timeout;
47#else
48 uint64_t timeout;
49#endif
50 uint32_t event_serial;
51 int fd; // -1 for timeout only
52 int result;
53 } data;
54
55 // connected to timer_th.waiting (ordered by timeout)
56 struct ccan_list_node node;
57
58 // connected to rb_fd_waiters.waiters of data.fd
59 struct ccan_list_node fd_node;
60};
61
62// One entry per fd with waiters; fds stay dense, so a table indexed by fd fits.
63// Entries live in fixed chunks: growing must not move a live list head.
65 struct ccan_list_head waiters; // rb_thread_sched_waiting.fd_node
66
67 // The io flags currently armed in epoll/kqueue for this fd: the union of
68 // what its waiters asked for.
69 uint32_t armed_flags;
70
71 // Bumped on full disarm. Events carry the generation they were armed with,
72 // so one queued before the fd was disarmed (and reused) is recognised.
73 uint32_t generation;
74};
75
76// per-Thread scheduler helper data
78 struct {
79 struct ccan_list_node ubf;
80
81 // connected to ractor->threads.sched.reqdyq
82 // locked by ractor->threads.sched.lock
83 struct ccan_list_node readyq;
84 // Indicates whether thread is on the readyq.
85 // There is no clear relationship between this and th->status.
86 bool is_ready;
87
88 // connected to vm->ractor.sched.timeslice_threads
89 // locked by vm->ractor.sched.lock
90 struct ccan_list_node timeslice_threads;
91
92 // connected to vm->ractor.sched.running_threads
93 // locked by vm->ractor.sched.lock
94 struct ccan_list_node running_threads;
95
96 } node;
97
98 struct rb_thread_sched_waiting waiting_reason;
99 uint32_t event_serial;
100
101 bool malloc_stack;
102 void *context_stack;
103 size_t context_stack_size;
104 struct coroutine_context *context;
105};
106
107struct rb_native_thread {
108 rb_atomic_t serial;
109 struct rb_vm_struct *vm;
110
111 rb_nativethread_id_t thread_id;
112
113#ifdef RB_THREAD_T_HAS_NATIVE_ID
114 int tid;
115#endif
116
117 struct rb_thread_struct *running_thread;
118
119 // to control native thread
120#if defined(__GLIBC__) || defined(__FreeBSD__)
121 union
122#else
123 /*
124 * assume the platform condvars are badly implemented and have a
125 * "memory" of which mutex they're associated with
126 */
127 struct
128#endif
129 {
130 rb_nativethread_cond_t intr; /* th->interrupt_lock */
131 rb_nativethread_cond_t readyq; /* use sched->lock */
132 } cond;
133
134#ifdef USE_SIGALTSTACK
135 void *altstack;
136#endif
137
138 struct coroutine_context *nt_context;
139 int dedicated;
140
141 // set when this thread came back from a blocking region with no room left
142 // in the shared pool; it ends when it next asks for work
143 bool retiring;
144
145 // A terminating coroutine records its context here before its final
146 // transfer; this nt's loop reclaims it. (Not via coroutine_transfer()'s
147 // return value: its meaning differs between the amd64 asm and ucontext.)
148 struct coroutine_context *dead_co;
149};
150
151#undef except
152#undef try
153#undef leave
154#undef finally
155
156// per-Ractor
157struct rb_thread_sched {
158 rb_nativethread_lock_t lock_;
159#if VM_CHECK_MODE
160 struct rb_thread_struct *lock_owner;
161#endif
162 struct rb_thread_struct *running; // running thread or NULL
163 // Most recently running thread or NULL. If this thread wakes up before the newly running
164 // thread completes the transfer of control, it can interrupt and resume running.
165 // The new thread clears this field when it takes control.
166 struct rb_thread_struct *runnable_hot_th;
167 int runnable_hot_th_waiting;
168 bool is_running;
169 bool is_running_timeslice;
170 bool enable_mn_threads;
171
172 struct ccan_list_head readyq;
173 int readyq_cnt;
174 // ractor scheduling
175 // When not linked in vm->ractor.sched.grq, this node is kept
176 // self-linked (ccan_list_node_init), so "linked?" can be read off the
177 // node itself: enqueuers assert it, and direct transfers cancel an
178 // outstanding entry (see ractor_sched_cancel_enq).
179 struct ccan_list_node grq_node;
180};
181
182struct rb_thread_context;
183
184// A coroutine (M:N) thread's teardown runs coroutine_thread_terminated
185// instead of the dedicated-thread path in thread_start_func_2; see the
186// comments there and in thread_pthread_mn.c. th->sched.context is cleared in
187// that epilogue, so this also reads as "did not tear down yet".
188// (Only meaningful when USE_MN_THREADS -- gate uses accordingly; the macro
189// itself is a plain pointer test and always compiles.)
190#define th_has_coroutine(th) ((th)->sched.context != NULL)
191
192#ifdef RB_THREAD_LOCAL_SPECIFIER
193 NOINLINE(void rb_current_ec_set(struct rb_execution_context_struct *));
194
195 # ifdef RB_THREAD_CURRENT_EC_NOINLINE
196 NOINLINE(struct rb_execution_context_struct *rb_current_ec(void));
197 # else
198 RUBY_EXTERN RB_THREAD_LOCAL_SPECIFIER struct rb_execution_context_struct *ruby_current_ec;
199
200 // for RUBY_DEBUG_LOG()
201 RUBY_EXTERN RB_THREAD_LOCAL_SPECIFIER rb_atomic_t ruby_nt_serial;
202 #define RUBY_NT_SERIAL 1
203 # endif
204#else
205typedef pthread_key_t native_tls_key_t;
206
207static inline void *
208native_tls_get(native_tls_key_t key)
209{
210 // return value should be checked by caller
211 return pthread_getspecific(key);
212}
213
214static inline void
215native_tls_set(native_tls_key_t key, void *ptr)
216{
217 if (UNLIKELY(pthread_setspecific(key, ptr) != 0)) {
218 rb_bug("pthread_setspecific error");
219 }
220}
221
222RUBY_EXTERN native_tls_key_t ruby_current_ec_key;
223#endif
224
225struct rb_ractor_struct;
226void rb_ractor_sched_wait(struct rb_execution_context_struct *ec, struct rb_ractor_struct *cr, rb_unblock_function_t *ubf, void *ptr);
227void rb_ractor_sched_wakeup(struct rb_ractor_struct *r, struct rb_thread_struct *th);
228
229#endif /* RUBY_THREAD_PTHREAD_H */
std::atomic< unsigned > rb_atomic_t
Type that is eligible for atomic operations.
Definition atomic.h:69
#define RUBY_EXTERN
Declaration of externally visible global variables.
Definition dllexport.h:45
void rb_unblock_function_t(void *)
This is the type of UBFs.
Definition thread.h:336