Ruby 2.7.0p0(2019-12-25revision647ee6f091eafcce70ffb75ddf7e121e192ab217)

thread_pthread.c
Go to the documentation of this file.
1 /* -*-c-*- */
2 /**********************************************************************
3
4 thread_pthread.c -
5
6 $Author$
7
8 Copyright (C) 2004-2007 Koichi Sasada
9
10 **********************************************************************/
11
12 #ifdef THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION
13
14 #include "gc.h"
15 #include "mjit.h"
16
17 #ifdef HAVE_SYS_RESOURCE_H
18 #include <sys/resource.h>
19 #endif
20 #ifdef HAVE_THR_STKSEGMENT
21 #include <thread.h>
22 #endif
23 #if HAVE_FCNTL_H
24 #include <fcntl.h>
25 #elif HAVE_SYS_FCNTL_H
26 #include <sys/fcntl.h>
27 #endif
28 #ifdef HAVE_SYS_PRCTL_H
29 #include <sys/prctl.h>
30 #endif
31 #if defined(HAVE_SYS_TIME_H)
32 #include <sys/time.h>
33 #endif
34 #if defined(__HAIKU__)
35 #include <kernel/OS.h>
36 #endif
37 #include <time.h>
38 #include <signal.h>
39
40 #if defined(HAVE_SYS_EVENTFD_H) && defined(HAVE_EVENTFD)
41 # define USE_EVENTFD (1)
42 # include <sys/eventfd.h>
43 #else
44 # define USE_EVENTFD (0)
45 #endif
46
47 #if defined(SIGVTALRM) && !defined(__CYGWIN__)
48 # define USE_UBF_LIST 1
49 #endif
50
51 /*
52 * UBF_TIMER and ubf_list both use SIGVTALRM.
53 *
54 * UBF_TIMER has NOTHING to do with thread timeslices (TIMER_INTERRUPT_MASK)
55 *
56 * UBF_TIMER is to close TOCTTOU signal race on programs where we
57 * cannot rely on GVL contention (vm->gvl.timer) to perform wakeups
58 * while a thread is doing blocking I/O on sockets or pipes. With
59 * rb_thread_call_without_gvl and similar functions:
60 *
61 * (1) Check interrupts.
62 * (2) release GVL.
63 * (2a) signal received
64 * (3) call func with data1 (blocks for a long time without ubf_timer)
65 * (4) acquire GVL.
66 * Other Ruby threads can not run in parallel any more.
67 * (5) Check interrupts.
68 *
69 * We need UBF_TIMER to break out of (3) if (2a) happens.
70 *
71 * ubf_list wakeups may be triggered on gvl_yield.
72 *
73 * If we have vm->gvl.timer (on GVL contention), we don't need UBF_TIMER
74 * as it can perform the same tasks while doing timeslices.
75 */
76 #define UBF_TIMER_NONE 0
77 #define UBF_TIMER_POSIX 1
78 #define UBF_TIMER_PTHREAD 2
79
80 #ifndef UBF_TIMER
81 # if defined(HAVE_TIMER_SETTIME) && defined(HAVE_TIMER_CREATE) && \
82 defined(CLOCK_MONOTONIC) && defined(USE_UBF_LIST)
83 /* preferred */
84 # define UBF_TIMER UBF_TIMER_POSIX
85 # elif defined(USE_UBF_LIST)
86 /* safe, but inefficient */
87 # define UBF_TIMER UBF_TIMER_PTHREAD
88 # else
89 /* we'll be racy without SIGVTALRM for ubf_list */
90 # define UBF_TIMER UBF_TIMER_NONE
91 # endif
92 #endif
93
94 enum rtimer_state {
95 /* alive, after timer_create: */
96 RTIMER_DISARM,
97 RTIMER_ARMING,
98 RTIMER_ARMED,
99
100 RTIMER_DEAD
101 };
102
103 #if UBF_TIMER == UBF_TIMER_POSIX
104 static const struct itimerspec zero;
105 static struct {
106 rb_atomic_t state; /* rtimer_state */
107 rb_pid_t owner;
108 timer_t timerid;
109 } timer_posix = {
110 /* .state = */ RTIMER_DEAD,
111 };
112
113 #elif UBF_TIMER == UBF_TIMER_PTHREAD
114 static void *timer_pthread_fn(void *);
115 static struct {
116 int low[2];
117 rb_atomic_t armed; /* boolean */
118 rb_pid_t owner;
119 pthread_t thid;
120 } timer_pthread = {
121 { -1, -1 },
122 };
123 #endif
124
125 void rb_native_mutex_lock(rb_nativethread_lock_t *lock);
126 void rb_native_mutex_unlock(rb_nativethread_lock_t *lock);
127 static int native_mutex_trylock(rb_nativethread_lock_t *lock);
128 void rb_native_mutex_initialize(rb_nativethread_lock_t *lock);
129 void rb_native_mutex_destroy(rb_nativethread_lock_t *lock);
130 void rb_native_cond_signal(rb_nativethread_cond_t *cond);
131 void rb_native_cond_broadcast(rb_nativethread_cond_t *cond);
132 void rb_native_cond_wait(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *mutex);
133 void rb_native_cond_initialize(rb_nativethread_cond_t *cond);
134 void rb_native_cond_destroy(rb_nativethread_cond_t *cond);
135 static void clear_thread_cache_altstack(void);
136 static void ubf_wakeup_all_threads(void);
137 static int ubf_threads_empty(void);
138 static int native_cond_timedwait(rb_nativethread_cond_t *, pthread_mutex_t *,
139 const rb_hrtime_t *abs);
140 static const rb_hrtime_t *sigwait_timeout(rb_thread_t *, int sigwait_fd,
141 const rb_hrtime_t *,
142 int *drained_p);
143 static void ubf_timer_disarm(void);
144 static void threadptr_trap_interrupt(rb_thread_t *);
145
146 #define TIMER_THREAD_CREATED_P() (signal_self_pipe.owner_process == getpid())
147
148 /* for testing, and in case we come across a platform w/o pipes: */
149 #define BUSY_WAIT_SIGNALS (0)
150
151 /*
152 * sigwait_th is the thread which owns sigwait_fd and sleeps on it
153 * (using ppoll). MJIT worker can be sigwait_th==0, so we initialize
154 * it to THREAD_INVALID at startup and fork time. It is the ONLY thread
155 * allowed to read from sigwait_fd, otherwise starvation can occur.
156 */
157 #define THREAD_INVALID ((const rb_thread_t *)-1)
158 static const rb_thread_t *sigwait_th;
159
160 #ifdef HAVE_SCHED_YIELD
161 #define native_thread_yield() (void)sched_yield()
162 #else
163 #define native_thread_yield() ((void)0)
164 #endif
165
166 #if defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) && \
167 defined(CLOCK_REALTIME) && defined(CLOCK_MONOTONIC) && \
168 defined(HAVE_CLOCK_GETTIME)
169 static pthread_condattr_t condattr_mono;
170 static pthread_condattr_t *condattr_monotonic = &condattr_mono;
171 #else
172 static const void *const condattr_monotonic = NULL;
173 #endif
174
175 /* 100ms. 10ms is too small for user level thread scheduling
176 * on recent Linux (tested on 2.6.35)
177 */
178 #define TIME_QUANTUM_MSEC (100)
179 #define TIME_QUANTUM_USEC (TIME_QUANTUM_MSEC * 1000)
180 #define TIME_QUANTUM_NSEC (TIME_QUANTUM_USEC * 1000)
181
182 static rb_hrtime_t native_cond_timeout(rb_nativethread_cond_t *, rb_hrtime_t);
183
184 /*
185 * Designate the next gvl.timer thread, favor the last thread in
186 * the waitq since it will be in waitq longest
187 */
188 static int
189 designate_timer_thread(rb_vm_t *vm)
190 {
191 native_thread_data_t *last;
192
193 last = list_tail(&vm->gvl.waitq, native_thread_data_t, node.ubf);
194 if (last) {
195 rb_native_cond_signal(&last->cond.gvlq);
196 return TRUE;
197 }
198 return FALSE;
199 }
200
201 /*
202 * We become designated timer thread to kick vm->gvl.owner
203 * periodically. Continue on old timeout if it expired.
204 */
205 static void
206 do_gvl_timer(rb_vm_t *vm, rb_thread_t *th)
207 {
208 static rb_hrtime_t abs;
209 native_thread_data_t *nd = &th->native_thread_data;
210
211 vm->gvl.timer = th;
212
213 /* take over wakeups from UBF_TIMER */
214 ubf_timer_disarm();
215
216 if (vm->gvl.timer_err == ETIMEDOUT) {
217 abs = native_cond_timeout(&nd->cond.gvlq, TIME_QUANTUM_NSEC);
218 }
219 vm->gvl.timer_err = native_cond_timedwait(&nd->cond.gvlq, &vm->gvl.lock, &abs);
220
221 ubf_wakeup_all_threads();
222 ruby_sigchld_handler(vm);
223 if (UNLIKELY(rb_signal_buff_size())) {
224 if (th == vm->main_thread) {
225 RUBY_VM_SET_TRAP_INTERRUPT(th->ec);
226 }
227 else {
228 threadptr_trap_interrupt(vm->main_thread);
229 }
230 }
231
232 /*
233 * Timeslice. Warning: the process may fork while this
234 * thread is contending for GVL:
235 */
236 if (vm->gvl.owner) timer_thread_function();
237 vm->gvl.timer = 0;
238 }
239
240 static void
241 gvl_acquire_common(rb_vm_t *vm, rb_thread_t *th)
242 {
243 if (vm->gvl.owner) {
244 native_thread_data_t *nd = &th->native_thread_data;
245
246 VM_ASSERT(th->unblock.func == 0 &&
247 "we must not be in ubf_list and GVL waitq at the same time");
248
249 list_add_tail(&vm->gvl.waitq, &nd->node.gvl);
250
251 do {
252 if (!vm->gvl.timer) {
253 do_gvl_timer(vm, th);
254 }
255 else {
256 rb_native_cond_wait(&nd->cond.gvlq, &vm->gvl.lock);
257 }
258 } while (vm->gvl.owner);
259
260 list_del_init(&nd->node.gvl);
261
262 if (vm->gvl.need_yield) {
263 vm->gvl.need_yield = 0;
264 rb_native_cond_signal(&vm->gvl.switch_cond);
265 }
266 }
267 else { /* reset timer if uncontended */
268 vm->gvl.timer_err = ETIMEDOUT;
269 }
270 vm->gvl.owner = th;
271 if (!vm->gvl.timer) {
272 if (!designate_timer_thread(vm) && !ubf_threads_empty()) {
273 rb_thread_wakeup_timer_thread(-1);
274 }
275 }
276 }
277
278 static void
279 gvl_acquire(rb_vm_t *vm, rb_thread_t *th)
280 {
281 rb_native_mutex_lock(&vm->gvl.lock);
282 gvl_acquire_common(vm, th);
283 rb_native_mutex_unlock(&vm->gvl.lock);
284 }
285
286 static const native_thread_data_t *
287 gvl_release_common(rb_vm_t *vm)
288 {
289 native_thread_data_t *next;
290 vm->gvl.owner = 0;
291 next = list_top(&vm->gvl.waitq, native_thread_data_t, node.ubf);
292 if (next) rb_native_cond_signal(&next->cond.gvlq);
293
294 return next;
295 }
296
297 static void
298 gvl_release(rb_vm_t *vm)
299 {
300 rb_native_mutex_lock(&vm->gvl.lock);
301 gvl_release_common(vm);
302 rb_native_mutex_unlock(&vm->gvl.lock);
303 }
304
305 static void
306 gvl_yield(rb_vm_t *vm, rb_thread_t *th)
307 {
308 const native_thread_data_t *next;
309
310 /*
311 * Perhaps other threads are stuck in blocking region w/o GVL, too,
312 * (perhaps looping in io_close_fptr) so we kick them:
313 */
314 ubf_wakeup_all_threads();
315 rb_native_mutex_lock(&vm->gvl.lock);
316 next = gvl_release_common(vm);
317
318 /* An another thread is processing GVL yield. */
319 if (UNLIKELY(vm->gvl.wait_yield)) {
320 while (vm->gvl.wait_yield)
321 rb_native_cond_wait(&vm->gvl.switch_wait_cond, &vm->gvl.lock);
322 }
323 else if (next) {
324 /* Wait until another thread task takes GVL. */
325 vm->gvl.need_yield = 1;
326 vm->gvl.wait_yield = 1;
327 while (vm->gvl.need_yield)
328 rb_native_cond_wait(&vm->gvl.switch_cond, &vm->gvl.lock);
329 vm->gvl.wait_yield = 0;
330 rb_native_cond_broadcast(&vm->gvl.switch_wait_cond);
331 }
332 else {
333 rb_native_mutex_unlock(&vm->gvl.lock);
334 native_thread_yield();
335 rb_native_mutex_lock(&vm->gvl.lock);
336 rb_native_cond_broadcast(&vm->gvl.switch_wait_cond);
337 }
338 gvl_acquire_common(vm, th);
339 rb_native_mutex_unlock(&vm->gvl.lock);
340 }
341
342 static void
343 gvl_init(rb_vm_t *vm)
344 {
345 rb_native_mutex_initialize(&vm->gvl.lock);
346 rb_native_cond_initialize(&vm->gvl.switch_cond);
347 rb_native_cond_initialize(&vm->gvl.switch_wait_cond);
348 list_head_init(&vm->gvl.waitq);
349 vm->gvl.owner = 0;
350 vm->gvl.timer = 0;
351 vm->gvl.timer_err = ETIMEDOUT;
352 vm->gvl.need_yield = 0;
353 vm->gvl.wait_yield = 0;
354 }
355
356 static void
357 gvl_destroy(rb_vm_t *vm)
358 {
359 /*
360 * only called once at VM shutdown (not atfork), another thread
361 * may still grab vm->gvl.lock when calling gvl_release at
362 * the end of thread_start_func_2
363 */
364 if (0) {
365 rb_native_cond_destroy(&vm->gvl.switch_wait_cond);
366 rb_native_cond_destroy(&vm->gvl.switch_cond);
367 rb_native_mutex_destroy(&vm->gvl.lock);
368 }
369 clear_thread_cache_altstack();
370 }
371
372 #if defined(HAVE_WORKING_FORK)
373 static void thread_cache_reset(void);
374 static void
375 gvl_atfork(rb_vm_t *vm)
376 {
377 thread_cache_reset();
378 gvl_init(vm);
379 gvl_acquire(vm, GET_THREAD());
380 }
381 #endif
382
383 #define NATIVE_MUTEX_LOCK_DEBUG 0
384
385 static void
386 mutex_debug(const char *msg, void *lock)
387 {
388 if (NATIVE_MUTEX_LOCK_DEBUG) {
389 int r;
390 static pthread_mutex_t dbglock = PTHREAD_MUTEX_INITIALIZER;
391
392 if ((r = pthread_mutex_lock(&dbglock)) != 0) {exit(EXIT_FAILURE);}
393 fprintf(stdout, "%s: %p\n", msg, lock);
394 if ((r = pthread_mutex_unlock(&dbglock)) != 0) {exit(EXIT_FAILURE);}
395 }
396 }
397
398 void
399 rb_native_mutex_lock(pthread_mutex_t *lock)
400 {
401 int r;
402 mutex_debug("lock", lock);
403 if ((r = pthread_mutex_lock(lock)) != 0) {
404 rb_bug_errno("pthread_mutex_lock", r);
405 }
406 }
407
408 void
409 rb_native_mutex_unlock(pthread_mutex_t *lock)
410 {
411 int r;
412 mutex_debug("unlock", lock);
413 if ((r = pthread_mutex_unlock(lock)) != 0) {
414 rb_bug_errno("pthread_mutex_unlock", r);
415 }
416 }
417
418 static inline int
419 native_mutex_trylock(pthread_mutex_t *lock)
420 {
421 int r;
422 mutex_debug("trylock", lock);
423 if ((r = pthread_mutex_trylock(lock)) != 0) {
424 if (r == EBUSY) {
425 return EBUSY;
426 }
427 else {
428 rb_bug_errno("pthread_mutex_trylock", r);
429 }
430 }
431 return 0;
432 }
433
434 void
435 rb_native_mutex_initialize(pthread_mutex_t *lock)
436 {
437 int r = pthread_mutex_init(lock, 0);
438 mutex_debug("init", lock);
439 if (r != 0) {
440 rb_bug_errno("pthread_mutex_init", r);
441 }
442 }
443
444 void
445 rb_native_mutex_destroy(pthread_mutex_t *lock)
446 {
447 int r = pthread_mutex_destroy(lock);
448 mutex_debug("destroy", lock);
449 if (r != 0) {
450 rb_bug_errno("pthread_mutex_destroy", r);
451 }
452 }
453
454 void
455 rb_native_cond_initialize(rb_nativethread_cond_t *cond)
456 {
457 int r = pthread_cond_init(cond, condattr_monotonic);
458 if (r != 0) {
459 rb_bug_errno("pthread_cond_init", r);
460 }
461 }
462
463 void
464 rb_native_cond_destroy(rb_nativethread_cond_t *cond)
465 {
466 int r = pthread_cond_destroy(cond);
467 if (r != 0) {
468 rb_bug_errno("pthread_cond_destroy", r);
469 }
470 }
471
472 /*
473 * In OS X 10.7 (Lion), pthread_cond_signal and pthread_cond_broadcast return
474 * EAGAIN after retrying 8192 times. You can see them in the following page:
475 *
476 * http://www.opensource.apple.com/source/Libc/Libc-763.11/pthreads/pthread_cond.c
477 *
478 * The following rb_native_cond_signal and rb_native_cond_broadcast functions
479 * need to retrying until pthread functions don't return EAGAIN.
480 */
481
482 void
483 rb_native_cond_signal(rb_nativethread_cond_t *cond)
484 {
485 int r;
486 do {
487 r = pthread_cond_signal(cond);
488 } while (r == EAGAIN);
489 if (r != 0) {
490 rb_bug_errno("pthread_cond_signal", r);
491 }
492 }
493
494 void
495 rb_native_cond_broadcast(rb_nativethread_cond_t *cond)
496 {
497 int r;
498 do {
499 r = pthread_cond_broadcast(cond);
500 } while (r == EAGAIN);
501 if (r != 0) {
502 rb_bug_errno("rb_native_cond_broadcast", r);
503 }
504 }
505
506 void
507 rb_native_cond_wait(rb_nativethread_cond_t *cond, pthread_mutex_t *mutex)
508 {
509 int r = pthread_cond_wait(cond, mutex);
510 if (r != 0) {
511 rb_bug_errno("pthread_cond_wait", r);
512 }
513 }
514
515 static int
516 native_cond_timedwait(rb_nativethread_cond_t *cond, pthread_mutex_t *mutex,
517 const rb_hrtime_t *abs)
518 {
519 int r;
520 struct timespec ts;
521
522 /*
523 * An old Linux may return EINTR. Even though POSIX says
524 * "These functions shall not return an error code of [EINTR]".
525 * http://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_cond_timedwait.html
526 * Let's hide it from arch generic code.
527 */
528 do {
529 r = pthread_cond_timedwait(cond, mutex, rb_hrtime2timespec(&ts, abs));
530 } while (r == EINTR);
531
532 if (r != 0 && r != ETIMEDOUT) {
533 rb_bug_errno("pthread_cond_timedwait", r);
534 }
535
536 return r;
537 }
538
539 static rb_hrtime_t
540 native_cond_timeout(rb_nativethread_cond_t *cond, const rb_hrtime_t rel)
541 {
542 if (condattr_monotonic) {
543 return rb_hrtime_add(rb_hrtime_now(), rel);
544 }
545 else {
546 struct timespec ts;
547
548 rb_timespec_now(&ts);
549 return rb_hrtime_add(rb_timespec2hrtime(&ts), rel);
550 }
551 }
552
553 #define native_cleanup_push pthread_cleanup_push
554 #define native_cleanup_pop pthread_cleanup_pop
555
556 static pthread_key_t ruby_native_thread_key;
557
558 static void
559 null_func(int i)
560 {
561 /* null */
562 }
563
564 static rb_thread_t *
565 ruby_thread_from_native(void)
566 {
567 return pthread_getspecific(ruby_native_thread_key);
568 }
569
570 static int
571 ruby_thread_set_native(rb_thread_t *th)
572 {
573 return pthread_setspecific(ruby_native_thread_key, th) == 0;
574 }
575
576 static void native_thread_init(rb_thread_t *th);
577
578 void
579 Init_native_thread(rb_thread_t *th)
580 {
581 #if defined(HAVE_PTHREAD_CONDATTR_SETCLOCK)
582 if (condattr_monotonic) {
583 int r = pthread_condattr_init(condattr_monotonic);
584 if (r == 0) {
585 r = pthread_condattr_setclock(condattr_monotonic, CLOCK_MONOTONIC);
586 }
587 if (r) condattr_monotonic = NULL;
588 }
589 #endif
590 pthread_key_create(&ruby_native_thread_key, NULL);
591 th->thread_id = pthread_self();
592 fill_thread_id_str(th);
593 native_thread_init(th);
594 posix_signal(SIGVTALRM, null_func);
595 }
596
597 static void
598 native_thread_init(rb_thread_t *th)
599 {
600 native_thread_data_t *nd = &th->native_thread_data;
601
602 #ifdef USE_UBF_LIST
603 list_node_init(&nd->node.ubf);
604 #endif
605 rb_native_cond_initialize(&nd->cond.gvlq);
606 if (&nd->cond.gvlq != &nd->cond.intr)
607 rb_native_cond_initialize(&nd->cond.intr);
608 ruby_thread_set_native(th);
609 }
610
611 #ifndef USE_THREAD_CACHE
612 #define USE_THREAD_CACHE 1
613 #endif
614
615 static void
616 native_thread_destroy(rb_thread_t *th)
617 {
618 native_thread_data_t *nd = &th->native_thread_data;
619
620 rb_native_cond_destroy(&nd->cond.gvlq);
621 if (&nd->cond.gvlq != &nd->cond.intr)
622 rb_native_cond_destroy(&nd->cond.intr);
623
624 /*
625 * prevent false positive from ruby_thread_has_gvl_p if that
626 * gets called from an interposing function wrapper
627 */
628 if (USE_THREAD_CACHE)
629 ruby_thread_set_native(0);
630 }
631
632 #if USE_THREAD_CACHE
633 static rb_thread_t *register_cached_thread_and_wait(void *);
634 #endif
635
636 #if defined HAVE_PTHREAD_GETATTR_NP || defined HAVE_PTHREAD_ATTR_GET_NP
637 #define STACKADDR_AVAILABLE 1
638 #elif defined HAVE_PTHREAD_GET_STACKADDR_NP && defined HAVE_PTHREAD_GET_STACKSIZE_NP
639 #define STACKADDR_AVAILABLE 1
640 #undef MAINSTACKADDR_AVAILABLE
641 #define MAINSTACKADDR_AVAILABLE 1
642 void *pthread_get_stackaddr_np(pthread_t);
643 size_t pthread_get_stacksize_np(pthread_t);
644 #elif defined HAVE_THR_STKSEGMENT || defined HAVE_PTHREAD_STACKSEG_NP
645 #define STACKADDR_AVAILABLE 1
646 #elif defined HAVE_PTHREAD_GETTHRDS_NP
647 #define STACKADDR_AVAILABLE 1
648 #elif defined __HAIKU__
649 #define STACKADDR_AVAILABLE 1
650 #endif
651
652 #ifndef MAINSTACKADDR_AVAILABLE
653 # ifdef STACKADDR_AVAILABLE
654 # define MAINSTACKADDR_AVAILABLE 1
655 # else
656 # define MAINSTACKADDR_AVAILABLE 0
657 # endif
658 #endif
659 #if MAINSTACKADDR_AVAILABLE && !defined(get_main_stack)
660 # define get_main_stack(addr, size) get_stack(addr, size)
661 #endif
662
663 #ifdef STACKADDR_AVAILABLE
664 /*
665 * Get the initial address and size of current thread's stack
666 */
667 static int
668 get_stack(void **addr, size_t *size)
669 {
670 #define CHECK_ERR(expr) \
671 {int err = (expr); if (err) return err;}
672 #ifdef HAVE_PTHREAD_GETATTR_NP /* Linux */
673 pthread_attr_t attr;
674 size_t guard = 0;
675 STACK_GROW_DIR_DETECTION;
676 CHECK_ERR(pthread_getattr_np(pthread_self(), &attr));
677 # ifdef HAVE_PTHREAD_ATTR_GETSTACK
678 CHECK_ERR(pthread_attr_getstack(&attr, addr, size));
679 STACK_DIR_UPPER((void)0, (void)(*addr = (char *)*addr + *size));
680 # else
681 CHECK_ERR(pthread_attr_getstackaddr(&attr, addr));
682 CHECK_ERR(pthread_attr_getstacksize(&attr, size));
683 # endif
684 # ifdef HAVE_PTHREAD_ATTR_GETGUARDSIZE
685 CHECK_ERR(pthread_attr_getguardsize(&attr, &guard));
686 *size -= guard;
687 # else
688 *size -= getpagesize();
689 # endif
690 pthread_attr_destroy(&attr);
691 #elif defined HAVE_PTHREAD_ATTR_GET_NP /* FreeBSD, DragonFly BSD, NetBSD */
692 pthread_attr_t attr;
693 CHECK_ERR(pthread_attr_init(&attr));
694 CHECK_ERR(pthread_attr_get_np(pthread_self(), &attr));
695 # ifdef HAVE_PTHREAD_ATTR_GETSTACK
696 CHECK_ERR(pthread_attr_getstack(&attr, addr, size));
697 # else
698 CHECK_ERR(pthread_attr_getstackaddr(&attr, addr));
699 CHECK_ERR(pthread_attr_getstacksize(&attr, size));
700 # endif
701 STACK_DIR_UPPER((void)0, (void)(*addr = (char *)*addr + *size));
702 pthread_attr_destroy(&attr);
703 #elif (defined HAVE_PTHREAD_GET_STACKADDR_NP && defined HAVE_PTHREAD_GET_STACKSIZE_NP) /* MacOS X */
704 pthread_t th = pthread_self();
705 *addr = pthread_get_stackaddr_np(th);
706 *size = pthread_get_stacksize_np(th);
707 #elif defined HAVE_THR_STKSEGMENT || defined HAVE_PTHREAD_STACKSEG_NP
708 stack_t stk;
709 # if defined HAVE_THR_STKSEGMENT /* Solaris */
710 CHECK_ERR(thr_stksegment(&stk));
711 # else /* OpenBSD */
712 CHECK_ERR(pthread_stackseg_np(pthread_self(), &stk));
713 # endif
714 *addr = stk.ss_sp;
715 *size = stk.ss_size;
716 #elif defined HAVE_PTHREAD_GETTHRDS_NP /* AIX */
717 pthread_t th = pthread_self();
718 struct __pthrdsinfo thinfo;
719 char reg[256];
720 int regsiz=sizeof(reg);
721 CHECK_ERR(pthread_getthrds_np(&th, PTHRDSINFO_QUERY_ALL,
722 &thinfo, sizeof(thinfo),
723 &reg, &regsiz));
724 *addr = thinfo.__pi_stackaddr;
725 /* Must not use thinfo.__pi_stacksize for size.
726 It is around 3KB smaller than the correct size
727 calculated by thinfo.__pi_stackend - thinfo.__pi_stackaddr. */
728 *size = thinfo.__pi_stackend - thinfo.__pi_stackaddr;
729 STACK_DIR_UPPER((void)0, (void)(*addr = (char *)*addr + *size));
730 #elif defined __HAIKU__
731 thread_info info;
732 STACK_GROW_DIR_DETECTION;
733 CHECK_ERR(get_thread_info(find_thread(NULL), &info));
734 *addr = info.stack_base;
735 *size = (uintptr_t)info.stack_end - (uintptr_t)info.stack_base;
736 STACK_DIR_UPPER((void)0, (void)(*addr = (char *)*addr + *size));
737 #else
738 #error STACKADDR_AVAILABLE is defined but not implemented.
739 #endif
740 return 0;
741 #undef CHECK_ERR
742 }
743 #endif
744
745 static struct {
746 rb_nativethread_id_t id;
747 size_t stack_maxsize;
748 VALUE *stack_start;
749 } native_main_thread;
750
751 #ifdef STACK_END_ADDRESS
752 extern void *STACK_END_ADDRESS;
753 #endif
754
755 enum {
756 RUBY_STACK_SPACE_LIMIT = 1024 * 1024, /* 1024KB */
757 RUBY_STACK_SPACE_RATIO = 5
758 };
759
760 static size_t
761 space_size(size_t stack_size)
762 {
763 size_t space_size = stack_size / RUBY_STACK_SPACE_RATIO;
764 if (space_size > RUBY_STACK_SPACE_LIMIT) {
765 return RUBY_STACK_SPACE_LIMIT;
766 }
767 else {
768 return space_size;
769 }
770 }
771
772 #ifdef __linux__
773 static __attribute__((noinline)) void
774 reserve_stack(volatile char *limit, size_t size)
775 {
776 # ifdef C_ALLOCA
777 # error needs alloca()
778 # endif
779 struct rlimit rl;
780 volatile char buf[0x100];
781 enum {stack_check_margin = 0x1000}; /* for -fstack-check */
782
783 STACK_GROW_DIR_DETECTION;
784
785 if (!getrlimit(RLIMIT_STACK, &rl) && rl.rlim_cur == RLIM_INFINITY)
786 return;
787
788 if (size < stack_check_margin) return;
789 size -= stack_check_margin;
790
791 size -= sizeof(buf); /* margin */
792 if (IS_STACK_DIR_UPPER()) {
793 const volatile char *end = buf + sizeof(buf);
794 limit += size;
795 if (limit > end) {
796 /* |<-bottom (=limit(a)) top->|
797 * | .. |<-buf 256B |<-end | stack check |
798 * | 256B | =size= | margin (4KB)|
799 * | =size= limit(b)->| 256B | |
800 * | | alloca(sz) | | |
801 * | .. |<-buf |<-limit(c) [sz-1]->0> | |
802 */
803 size_t sz = limit - end;
804 limit = alloca(sz);
805 limit[sz-1] = 0;
806 }
807 }
808 else {
809 limit -= size;
810 if (buf > limit) {
811 /* |<-top (=limit(a)) bottom->|
812 * | .. | 256B buf->| | stack check |
813 * | 256B | =size= | margin (4KB)|
814 * | =size= limit(b)->| 256B | |
815 * | | alloca(sz) | | |
816 * | .. | buf->| limit(c)-><0> | |
817 */
818 size_t sz = buf - limit;
819 limit = alloca(sz);
820 limit[0] = 0;
821 }
822 }
823 }
824 #else
825 # define reserve_stack(limit, size) ((void)(limit), (void)(size))
826 #endif
827
828 #undef ruby_init_stack
829 /* Set stack bottom of Ruby implementation.
830 *
831 * You must call this function before any heap allocation by Ruby implementation.
832 * Or GC will break living objects */
833 void
834 ruby_init_stack(volatile VALUE *addr)
835 {
836 native_main_thread.id = pthread_self();
837
838 #if MAINSTACKADDR_AVAILABLE
839 if (native_main_thread.stack_maxsize) return;
840 {
841 void* stackaddr;
842 size_t size;
843 if (get_main_stack(&stackaddr, &size) == 0) {
844 native_main_thread.stack_maxsize = size;
845 native_main_thread.stack_start = stackaddr;
846 reserve_stack(stackaddr, size);
847 goto bound_check;
848 }
849 }
850 #endif
851 #ifdef STACK_END_ADDRESS
852 native_main_thread.stack_start = STACK_END_ADDRESS;
853 #else
854 if (!native_main_thread.stack_start ||
855 STACK_UPPER((VALUE *)(void *)&addr,
856 native_main_thread.stack_start > addr,
857 native_main_thread.stack_start < addr)) {
858 native_main_thread.stack_start = (VALUE *)addr;
859 }
860 #endif
861 {
862 #if defined(HAVE_GETRLIMIT)
863 #if defined(PTHREAD_STACK_DEFAULT)
864 # if PTHREAD_STACK_DEFAULT < RUBY_STACK_SPACE*5
865 # error "PTHREAD_STACK_DEFAULT is too small"
866 # endif
867 size_t size = PTHREAD_STACK_DEFAULT;
868 #else
869 size_t size = RUBY_VM_THREAD_VM_STACK_SIZE;
870 #endif
871 size_t space;
872 int pagesize = getpagesize();
873 struct rlimit rlim;
874 STACK_GROW_DIR_DETECTION;
875 if (getrlimit(RLIMIT_STACK, &rlim) == 0) {
876 size = (size_t)rlim.rlim_cur;
877 }
878 addr = native_main_thread.stack_start;
879 if (IS_STACK_DIR_UPPER()) {
880 space = ((size_t)((char *)addr + size) / pagesize) * pagesize - (size_t)addr;
881 }
882 else {
883 space = (size_t)addr - ((size_t)((char *)addr - size) / pagesize + 1) * pagesize;
884 }
885 native_main_thread.stack_maxsize = space;
886 #endif
887 }
888
889 #if MAINSTACKADDR_AVAILABLE
890 bound_check:
891 #endif
892 /* If addr is out of range of main-thread stack range estimation, */
893 /* it should be on co-routine (alternative stack). [Feature #2294] */
894 {
895 void *start, *end;
896 STACK_GROW_DIR_DETECTION;
897
898 if (IS_STACK_DIR_UPPER()) {
899 start = native_main_thread.stack_start;
900 end = (char *)native_main_thread.stack_start + native_main_thread.stack_maxsize;
901 }
902 else {
903 start = (char *)native_main_thread.stack_start - native_main_thread.stack_maxsize;
904 end = native_main_thread.stack_start;
905 }
906
907 if ((void *)addr < start || (void *)addr > end) {
908 /* out of range */
909 native_main_thread.stack_start = (VALUE *)addr;
910 native_main_thread.stack_maxsize = 0; /* unknown */
911 }
912 }
913 }
914
915 #define CHECK_ERR(expr) \
916 {int err = (expr); if (err) {rb_bug_errno(#expr, err);}}
917
918 static int
919 native_thread_init_stack(rb_thread_t *th)
920 {
921 rb_nativethread_id_t curr = pthread_self();
922
923 if (pthread_equal(curr, native_main_thread.id)) {
924 th->ec->machine.stack_start = native_main_thread.stack_start;
925 th->ec->machine.stack_maxsize = native_main_thread.stack_maxsize;
926 }
927 else {
928 #ifdef STACKADDR_AVAILABLE
929 void *start;
930 size_t size;
931
932 if (get_stack(&start, &size) == 0) {
933 uintptr_t diff = (uintptr_t)start - (uintptr_t)&curr;
934 th->ec->machine.stack_start = (VALUE *)&curr;
935 th->ec->machine.stack_maxsize = size - diff;
936 }
937 #else
938 rb_raise(rb_eNotImpError, "ruby engine can initialize only in the main thread");
939 #endif
940 }
941
942 return 0;
943 }
944
945 #ifndef __CYGWIN__
946 #define USE_NATIVE_THREAD_INIT 1
947 #endif
948
949 static void *
950 thread_start_func_1(void *th_ptr)
951 {
952 rb_thread_t *th = th_ptr;
953 RB_ALTSTACK_INIT(void *altstack);
954 #if USE_THREAD_CACHE
955 thread_start:
956 #endif
957 {
958 #if !defined USE_NATIVE_THREAD_INIT
959 VALUE stack_start;
960 #endif
961
962 fill_thread_id_str(th);
963 #if defined USE_NATIVE_THREAD_INIT
964 native_thread_init_stack(th);
965 #endif
966 native_thread_init(th);
967 /* run */
968 #if defined USE_NATIVE_THREAD_INIT
969 thread_start_func_2(th, th->ec->machine.stack_start);
970 #else
971 thread_start_func_2(th, &stack_start);
972 #endif
973 }
974 #if USE_THREAD_CACHE
975 /* cache thread */
976 if ((th = register_cached_thread_and_wait(RB_ALTSTACK(altstack))) != 0) {
977 goto thread_start;
978 }
979 #else
980 RB_ALTSTACK_FREE(altstack);
981 #endif
982 return 0;
983 }
984
985 struct cached_thread_entry {
986 rb_nativethread_cond_t cond;
987 rb_nativethread_id_t thread_id;
988 rb_thread_t *th;
989 void *altstack;
990 struct list_node node;
991 };
992
993 #if USE_THREAD_CACHE
994 static rb_nativethread_lock_t thread_cache_lock = RB_NATIVETHREAD_LOCK_INIT;
995 static LIST_HEAD(cached_thread_head);
996
997 # if defined(HAVE_WORKING_FORK)
998 static void
999 thread_cache_reset(void)
1000 {
1001 rb_native_mutex_initialize(&thread_cache_lock);
1002 list_head_init(&cached_thread_head);
1003 }
1004 # endif
1005
1006 /*
1007 * number of seconds to cache for, I think 1-5s is sufficient to obviate
1008 * the need for thread pool in many network programs (taking into account
1009 * worst case network latency across the globe) without wasting memory
1010 */
1011 #ifndef THREAD_CACHE_TIME
1012 # define THREAD_CACHE_TIME ((rb_hrtime_t)3 * RB_HRTIME_PER_SEC)
1013 #endif
1014
1015 static rb_thread_t *
1016 register_cached_thread_and_wait(void *altstack)
1017 {
1018 rb_hrtime_t end = THREAD_CACHE_TIME;
1019 struct cached_thread_entry entry;
1020
1021 rb_native_cond_initialize(&entry.cond);
1022 entry.altstack = altstack;
1023 entry.th = NULL;
1024 entry.thread_id = pthread_self();
1025 end = native_cond_timeout(&entry.cond, end);
1026
1027 rb_native_mutex_lock(&thread_cache_lock);
1028 {
1029 list_add(&cached_thread_head, &entry.node);
1030
1031 native_cond_timedwait(&entry.cond, &thread_cache_lock, &end);
1032
1033 if (entry.th == NULL) { /* unused */
1034 list_del(&entry.node);
1035 }
1036 }
1037 rb_native_mutex_unlock(&thread_cache_lock);
1038
1039 rb_native_cond_destroy(&entry.cond);
1040 if (!entry.th) {
1041 RB_ALTSTACK_FREE(entry.altstack);
1042 }
1043
1044 return entry.th;
1045 }
1046 #else
1047 # if defined(HAVE_WORKING_FORK)
1048 static void thread_cache_reset(void) { }
1049 # endif
1050 #endif
1051
1052 static int
1053 use_cached_thread(rb_thread_t *th)
1054 {
1055 #if USE_THREAD_CACHE
1056 struct cached_thread_entry *entry;
1057
1058 rb_native_mutex_lock(&thread_cache_lock);
1059 entry = list_pop(&cached_thread_head, struct cached_thread_entry, node);
1060 if (entry) {
1061 entry->th = th;
1062 /* th->thread_id must be set before signal for Thread#name= */
1063 th->thread_id = entry->thread_id;
1064 fill_thread_id_str(th);
1065 rb_native_cond_signal(&entry->cond);
1066 }
1067 rb_native_mutex_unlock(&thread_cache_lock);
1068 return !!entry;
1069 #endif
1070 return 0;
1071 }
1072
1073 static void
1074 clear_thread_cache_altstack(void)
1075 {
1076 #if USE_THREAD_CACHE
1077 struct cached_thread_entry *entry;
1078
1079 rb_native_mutex_lock(&thread_cache_lock);
1080 list_for_each(&cached_thread_head, entry, node) {
1081 void MAYBE_UNUSED(*altstack) = entry->altstack;
1082 entry->altstack = 0;
1083 RB_ALTSTACK_FREE(altstack);
1084 }
1085 rb_native_mutex_unlock(&thread_cache_lock);
1086 #endif
1087 }
1088
1089 static int
1090 native_thread_create(rb_thread_t *th)
1091 {
1092 int err = 0;
1093
1094 if (use_cached_thread(th)) {
1095 thread_debug("create (use cached thread): %p\n", (void *)th);
1096 }
1097 else {
1098 pthread_attr_t attr;
1099 const size_t stack_size = th->vm->default_params.thread_machine_stack_size + th->vm->default_params.thread_vm_stack_size;
1100 const size_t space = space_size(stack_size);
1101
1102 th->ec->machine.stack_maxsize = stack_size - space;
1103
1104 CHECK_ERR(pthread_attr_init(&attr));
1105
1106 # ifdef PTHREAD_STACK_MIN
1107 thread_debug("create - stack size: %lu\n", (unsigned long)stack_size);
1108 CHECK_ERR(pthread_attr_setstacksize(&attr, stack_size));
1109 # endif
1110
1111 # ifdef HAVE_PTHREAD_ATTR_SETINHERITSCHED
1112 CHECK_ERR(pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED));
1113 # endif
1114 CHECK_ERR(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
1115
1116 err = pthread_create(&th->thread_id, &attr, thread_start_func_1, th);
1117 thread_debug("create: %p (%d)\n", (void *)th, err);
1118 /* should be done in the created thread */
1119 fill_thread_id_str(th);
1120 CHECK_ERR(pthread_attr_destroy(&attr));
1121 }
1122 return err;
1123 }
1124
1125 #if USE_NATIVE_THREAD_PRIORITY
1126
1127 static void
1128 native_thread_apply_priority(rb_thread_t *th)
1129 {
1130 #if defined(_POSIX_PRIORITY_SCHEDULING) && (_POSIX_PRIORITY_SCHEDULING > 0)
1131 struct sched_param sp;
1132 int policy;
1133 int priority = 0 - th->priority;
1134 int max, min;
1135 pthread_getschedparam(th->thread_id, &policy, &sp);
1136 max = sched_get_priority_max(policy);
1137 min = sched_get_priority_min(policy);
1138
1139 if (min > priority) {
1140 priority = min;
1141 }
1142 else if (max < priority) {
1143 priority = max;
1144 }
1145
1146 sp.sched_priority = priority;
1147 pthread_setschedparam(th->thread_id, policy, &sp);
1148 #else
1149 /* not touched */
1150 #endif
1151 }
1152
1153 #endif /* USE_NATIVE_THREAD_PRIORITY */
1154
1155 static int
1156 native_fd_select(int n, rb_fdset_t *readfds, rb_fdset_t *writefds, rb_fdset_t *exceptfds, struct timeval *timeout, rb_thread_t *th)
1157 {
1158 return rb_fd_select(n, readfds, writefds, exceptfds, timeout);
1159 }
1160
1161 static void
1162 ubf_pthread_cond_signal(void *ptr)
1163 {
1164 rb_thread_t *th = (rb_thread_t *)ptr;
1165 thread_debug("ubf_pthread_cond_signal (%p)\n", (void *)th);
1166 rb_native_cond_signal(&th->native_thread_data.cond.intr);
1167 }
1168
1169 static void
1170 native_cond_sleep(rb_thread_t *th, rb_hrtime_t *rel)
1171 {
1172 rb_nativethread_lock_t *lock = &th->interrupt_lock;
1173 rb_nativethread_cond_t *cond = &th->native_thread_data.cond.intr;
1174
1175 /* Solaris cond_timedwait() return EINVAL if an argument is greater than
1176 * current_time + 100,000,000. So cut up to 100,000,000. This is
1177 * considered as a kind of spurious wakeup. The caller to native_sleep
1178 * should care about spurious wakeup.
1179 *
1180 * See also [Bug #1341] [ruby-core:29702]
1181 * http://download.oracle.com/docs/cd/E19683-01/816-0216/6m6ngupgv/index.html
1182 */
1183 const rb_hrtime_t max = (rb_hrtime_t)100000000 * RB_HRTIME_PER_SEC;
1184
1185 GVL_UNLOCK_BEGIN(th);
1186 {
1187 rb_native_mutex_lock(lock);
1188 th->unblock.func = ubf_pthread_cond_signal;
1189 th->unblock.arg = th;
1190
1191 if (RUBY_VM_INTERRUPTED(th->ec)) {
1192 /* interrupted. return immediate */
1193 thread_debug("native_sleep: interrupted before sleep\n");
1194 }
1195 else {
1196 if (!rel) {
1197 rb_native_cond_wait(cond, lock);
1198 }
1199 else {
1200 rb_hrtime_t end;
1201
1202 if (*rel > max) {
1203 *rel = max;
1204 }
1205
1206 end = native_cond_timeout(cond, *rel);
1207 native_cond_timedwait(cond, lock, &end);
1208 }
1209 }
1210 th->unblock.func = 0;
1211
1212 rb_native_mutex_unlock(lock);
1213 }
1214 GVL_UNLOCK_END(th);
1215
1216 thread_debug("native_sleep done\n");
1217 }
1218
1219 #ifdef USE_UBF_LIST
1220 static LIST_HEAD(ubf_list_head);
1221 static rb_nativethread_lock_t ubf_list_lock = RB_NATIVETHREAD_LOCK_INIT;
1222
1223 static void
1224 ubf_list_atfork(void)
1225 {
1226 list_head_init(&ubf_list_head);
1227 rb_native_mutex_initialize(&ubf_list_lock);
1228 }
1229
1230 /* The thread 'th' is registered to be trying unblock. */
1231 static void
1232 register_ubf_list(rb_thread_t *th)
1233 {
1234 struct list_node *node = &th->native_thread_data.node.ubf;
1235
1236 if (list_empty((struct list_head*)node)) {
1237 rb_native_mutex_lock(&ubf_list_lock);
1238 list_add(&ubf_list_head, node);
1239 rb_native_mutex_unlock(&ubf_list_lock);
1240 }
1241 }
1242
1243 /* The thread 'th' is unblocked. It no longer need to be registered. */
1244 static void
1245 unregister_ubf_list(rb_thread_t *th)
1246 {
1247 struct list_node *node = &th->native_thread_data.node.ubf;
1248
1249 /* we can't allow re-entry into ubf_list_head */
1250 VM_ASSERT(th->unblock.func == 0);
1251
1252 if (!list_empty((struct list_head*)node)) {
1253 rb_native_mutex_lock(&ubf_list_lock);
1254 list_del_init(node);
1255 if (list_empty(&ubf_list_head) && !rb_signal_buff_size()) {
1256 ubf_timer_disarm();
1257 }
1258 rb_native_mutex_unlock(&ubf_list_lock);
1259 }
1260 }
1261
1262 /*
1263 * send a signal to intent that a target thread return from blocking syscall.
1264 * Maybe any signal is ok, but we chose SIGVTALRM.
1265 */
1266 static void
1267 ubf_wakeup_thread(rb_thread_t *th)
1268 {
1269 thread_debug("thread_wait_queue_wakeup (%"PRI_THREAD_ID")\n", thread_id_str(th));
1270 pthread_kill(th->thread_id, SIGVTALRM);
1271 }
1272
1273 static void
1274 ubf_select(void *ptr)
1275 {
1276 rb_thread_t *th = (rb_thread_t *)ptr;
1277 rb_vm_t *vm = th->vm;
1278 const rb_thread_t *cur = ruby_thread_from_native(); /* may be 0 */
1279
1280 register_ubf_list(th);
1281
1282 /*
1283 * ubf_wakeup_thread() doesn't guarantee to wake up a target thread.
1284 * Therefore, we repeatedly call ubf_wakeup_thread() until a target thread
1285 * exit from ubf function. We must have a timer to perform this operation.
1286 * We use double-checked locking here because this function may be called
1287 * while vm->gvl.lock is held in do_gvl_timer.
1288 * There is also no need to start a timer if we're the designated
1289 * sigwait_th thread, otherwise we can deadlock with a thread
1290 * in unblock_function_clear.
1291 */
1292 if (cur != vm->gvl.timer && cur != sigwait_th) {
1293 /*
1294 * Double-checked locking above was to prevent nested locking
1295 * by the SAME thread. We use trylock here to prevent deadlocks
1296 * between DIFFERENT threads
1297 */
1298 if (native_mutex_trylock(&vm->gvl.lock) == 0) {
1299 if (!vm->gvl.timer) {
1300 rb_thread_wakeup_timer_thread(-1);
1301 }
1302 rb_native_mutex_unlock(&vm->gvl.lock);
1303 }
1304 }
1305
1306 ubf_wakeup_thread(th);
1307 }
1308
1309 static int
1310 ubf_threads_empty(void)
1311 {
1312 return list_empty(&ubf_list_head);
1313 }
1314
1315 static void
1316 ubf_wakeup_all_threads(void)
1317 {
1318 rb_thread_t *th;
1319 native_thread_data_t *dat;
1320
1321 if (!ubf_threads_empty()) {
1322 rb_native_mutex_lock(&ubf_list_lock);
1323 list_for_each(&ubf_list_head, dat, node.ubf) {
1324 th = container_of(dat, rb_thread_t, native_thread_data);
1325 ubf_wakeup_thread(th);
1326 }
1327 rb_native_mutex_unlock(&ubf_list_lock);
1328 }
1329 }
1330
1331 #else /* USE_UBF_LIST */
1332 #define register_ubf_list(th) (void)(th)
1333 #define unregister_ubf_list(th) (void)(th)
1334 #define ubf_select 0
1335 static void ubf_wakeup_all_threads(void) { return; }
1336 static int ubf_threads_empty(void) { return 1; }
1337 #define ubf_list_atfork() do {} while (0)
1338 #endif /* USE_UBF_LIST */
1339
1340 #define TT_DEBUG 0
1341 #define WRITE_CONST(fd, str) (void)(write((fd),(str),sizeof(str)-1)<0)
1342
1343 static struct {
1344 /* pipes are closed in forked children when owner_process does not match */
1345 int normal[2]; /* [0] == sigwait_fd */
1346 int ub_main[2]; /* unblock main thread from native_ppoll_sleep */
1347
1348 /* volatile for signal handler use: */
1349 volatile rb_pid_t owner_process;
1350 } signal_self_pipe = {
1351 {-1, -1},
1352 {-1, -1},
1353 };
1354
1355 /* only use signal-safe system calls here */
1356 static void
1357 rb_thread_wakeup_timer_thread_fd(int fd)
1358 {
1359 #if USE_EVENTFD
1360 const uint64_t buff = 1;
1361 #else
1362 const char buff = '!';
1363 #endif
1364 ssize_t result;
1365
1366 /* already opened */
1367 if (fd >= 0) {
1368 retry:
1369 if ((result = write(fd, &buff, sizeof(buff))) <= 0) {
1370 int e = errno;
1371 switch (e) {
1372 case EINTR: goto retry;
1373 case EAGAIN:
1374 #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
1375 case EWOULDBLOCK:
1376 #endif
1377 break;
1378 default:
1379 async_bug_fd("rb_thread_wakeup_timer_thread: write", e, fd);
1380 }
1381 }
1382 if (TT_DEBUG) WRITE_CONST(2, "rb_thread_wakeup_timer_thread: write\n");
1383 }
1384 else {
1385 /* ignore wakeup */
1386 }
1387 }
1388
1389 /*
1390 * This ensures we get a SIGVTALRM in TIME_QUANTUM_MSEC if our
1391 * process could not react to the original signal in time.
1392 */
1393 static void
1394 ubf_timer_arm(rb_pid_t current) /* async signal safe */
1395 {
1396 #if UBF_TIMER == UBF_TIMER_POSIX
1397 if ((!current || timer_posix.owner == current) &&
1398 !ATOMIC_CAS(timer_posix.state, RTIMER_DISARM, RTIMER_ARMING)) {
1399 struct itimerspec it;
1400
1401 it.it_interval.tv_sec = it.it_value.tv_sec = 0;
1402 it.it_interval.tv_nsec = it.it_value.tv_nsec = TIME_QUANTUM_NSEC;
1403
1404 if (timer_settime(timer_posix.timerid, 0, &it, 0))
1405 rb_async_bug_errno("timer_settime (arm)", errno);
1406
1407 switch (ATOMIC_CAS(timer_posix.state, RTIMER_ARMING, RTIMER_ARMED)) {
1408 case RTIMER_DISARM:
1409 /* somebody requested a disarm while we were arming */
1410 /* may race harmlessly with ubf_timer_destroy */
1411 (void)timer_settime(timer_posix.timerid, 0, &zero, 0);
1412
1413 case RTIMER_ARMING: return; /* success */
1414 case RTIMER_ARMED:
1415 /*
1416 * it is possible to have another thread disarm, and
1417 * a third thread arm finish re-arming before we get
1418 * here, so we wasted a syscall with timer_settime but
1419 * probably unavoidable in a signal handler.
1420 */
1421 return;
1422 case RTIMER_DEAD:
1423 /* may race harmlessly with ubf_timer_destroy */
1424 (void)timer_settime(timer_posix.timerid, 0, &zero, 0);
1425 return;
1426 default:
1427 rb_async_bug_errno("UBF_TIMER_POSIX unknown state", ERANGE);
1428 }
1429 }
1430 #elif UBF_TIMER == UBF_TIMER_PTHREAD
1431 if (!current || current == timer_pthread.owner) {
1432 if (ATOMIC_EXCHANGE(timer_pthread.armed, 1) == 0)
1433 rb_thread_wakeup_timer_thread_fd(timer_pthread.low[1]);
1434 }
1435 #endif
1436 }
1437
1438 void
1439 rb_thread_wakeup_timer_thread(int sig)
1440 {
1441 rb_pid_t current;
1442
1443 /* non-sighandler path */
1444 if (sig <= 0) {
1445 rb_thread_wakeup_timer_thread_fd(signal_self_pipe.normal[1]);
1446 if (sig < 0) {
1447 ubf_timer_arm(0);
1448 }
1449 return;
1450 }
1451
1452 /* must be safe inside sighandler, so no mutex */
1453 current = getpid();
1454 if (signal_self_pipe.owner_process == current) {
1455 rb_thread_wakeup_timer_thread_fd(signal_self_pipe.normal[1]);
1456
1457 /*
1458 * system_working check is required because vm and main_thread are
1459 * freed during shutdown
1460 */
1461 if (system_working > 0) {
1462 volatile rb_execution_context_t *ec;
1463 rb_vm_t *vm = GET_VM();
1464 rb_thread_t *mth;
1465
1466 /*
1467 * FIXME: root VM and main_thread should be static and not
1468 * on heap for maximum safety (and startup/shutdown speed)
1469 */
1470 if (!vm) return;
1471 mth = vm->main_thread;
1472 if (!mth || system_working <= 0) return;
1473
1474 /* this relies on GC for grace period before cont_free */
1475 ec = ACCESS_ONCE(rb_execution_context_t *, mth->ec);
1476
1477 if (ec) {
1478 RUBY_VM_SET_TRAP_INTERRUPT(ec);
1479 ubf_timer_arm(current);
1480
1481 /* some ubfs can interrupt single-threaded process directly */
1482 if (vm->ubf_async_safe && mth->unblock.func) {
1483 (mth->unblock.func)(mth->unblock.arg);
1484 }
1485 }
1486 }
1487 }
1488 }
1489
1490 #define CLOSE_INVALIDATE_PAIR(expr) \
1491 close_invalidate_pair(expr,"close_invalidate: "#expr)
1492 static void
1493 close_invalidate(int *fdp, const char *msg)
1494 {
1495 int fd = *fdp;
1496
1497 *fdp = -1;
1498 if (close(fd) < 0) {
1499 async_bug_fd(msg, errno, fd);
1500 }
1501 }
1502
1503 static void
1504 close_invalidate_pair(int fds[2], const char *msg)
1505 {
1506 if (USE_EVENTFD && fds[0] == fds[1]) {
1507 close_invalidate(&fds[0], msg);
1508 fds[1] = -1;
1509 }
1510 else {
1511 close_invalidate(&fds[0], msg);
1512 close_invalidate(&fds[1], msg);
1513 }
1514 }
1515
1516 static void
1517 set_nonblock(int fd)
1518 {
1519 int oflags;
1520 int err;
1521
1522 oflags = fcntl(fd, F_GETFL);
1523 if (oflags == -1)
1524 rb_sys_fail(0);
1525 oflags |= O_NONBLOCK;
1526 err = fcntl(fd, F_SETFL, oflags);
1527 if (err == -1)
1528 rb_sys_fail(0);
1529 }
1530
1531 /* communication pipe with timer thread and signal handler */
1532 static int
1533 setup_communication_pipe_internal(int pipes[2])
1534 {
1535 int err;
1536
1537 if (pipes[0] >= 0 || pipes[1] >= 0) {
1538 VM_ASSERT(pipes[0] >= 0);
1539 VM_ASSERT(pipes[1] >= 0);
1540 return 0;
1541 }
1542
1543 /*
1544 * Don't bother with eventfd on ancient Linux 2.6.22..2.6.26 which were
1545 * missing EFD_* flags, they can fall back to pipe
1546 */
1547 #if USE_EVENTFD && defined(EFD_NONBLOCK) && defined(EFD_CLOEXEC)
1548 pipes[0] = pipes[1] = eventfd(0, EFD_NONBLOCK|EFD_CLOEXEC);
1549 if (pipes[0] >= 0) {
1550 rb_update_max_fd(pipes[0]);
1551 return 0;
1552 }
1553 #endif
1554
1555 err = rb_cloexec_pipe(pipes);
1556 if (err != 0) {
1557 rb_warn("pipe creation failed for timer: %s, scheduling broken",
1558 strerror(errno));
1559 return -1;
1560 }
1561 rb_update_max_fd(pipes[0]);
1562 rb_update_max_fd(pipes[1]);
1563 set_nonblock(pipes[0]);
1564 set_nonblock(pipes[1]);
1565 return 0;
1566 }
1567
1568 #if !defined(SET_CURRENT_THREAD_NAME) && defined(__linux__) && defined(PR_SET_NAME)
1569 # define SET_CURRENT_THREAD_NAME(name) prctl(PR_SET_NAME, name)
1570 #endif
1571
1572 static VALUE threadptr_invoke_proc_location(rb_thread_t *th);
1573
1574 static void
1575 native_set_thread_name(rb_thread_t *th)
1576 {
1577 #ifdef SET_CURRENT_THREAD_NAME
1578 VALUE loc;
1579 if (!NIL_P(loc = th->name)) {
1580 SET_CURRENT_THREAD_NAME(RSTRING_PTR(loc));
1581 }
1582 else if ((loc = threadptr_invoke_proc_location(th)) != Qnil) {
1583 char *name, *p;
1584 char buf[16];
1585 size_t len;
1586 int n;
1587
1588 name = RSTRING_PTR(RARRAY_AREF(loc, 0));
1589 p = strrchr(name, '/'); /* show only the basename of the path. */
1590 if (p && p[1])
1591 name = p + 1;
1592
1593 n = snprintf(buf, sizeof(buf), "%s:%d", name, NUM2INT(RARRAY_AREF(loc, 1)));
1594 rb_gc_force_recycle(loc); /* acts as a GC guard, too */
1595
1596 len = (size_t)n;
1597 if (len >= sizeof(buf)) {
1598 buf[sizeof(buf)-2] = '*';
1599 buf[sizeof(buf)-1] = '\0';
1600 }
1601 SET_CURRENT_THREAD_NAME(buf);
1602 }
1603 #endif
1604 }
1605
1606 static VALUE
1607 native_set_another_thread_name(rb_nativethread_id_t thread_id, VALUE name)
1608 {
1609 #ifdef SET_ANOTHER_THREAD_NAME
1610 const char *s = "";
1611 if (!NIL_P(name)) s = RSTRING_PTR(name);
1612 SET_ANOTHER_THREAD_NAME(thread_id, s);
1613 #endif
1614 return name;
1615 }
1616
1617 static void
1618 ubf_timer_invalidate(void)
1619 {
1620 #if UBF_TIMER == UBF_TIMER_PTHREAD
1621 CLOSE_INVALIDATE_PAIR(timer_pthread.low);
1622 #endif
1623 }
1624
1625 static void
1626 ubf_timer_pthread_create(rb_pid_t current)
1627 {
1628 #if UBF_TIMER == UBF_TIMER_PTHREAD
1629 int err;
1630 if (timer_pthread.owner == current)
1631 return;
1632
1633 if (setup_communication_pipe_internal(timer_pthread.low) < 0)
1634 return;
1635
1636 err = pthread_create(&timer_pthread.thid, 0, timer_pthread_fn, GET_VM());
1637 if (!err)
1638 timer_pthread.owner = current;
1639 else
1640 rb_warn("pthread_create failed for timer: %s, signals racy",
1641 strerror(err));
1642 #endif
1643 }
1644
1645 static void
1646 ubf_timer_create(rb_pid_t current)
1647 {
1648 #if UBF_TIMER == UBF_TIMER_POSIX
1649 # if defined(__sun)
1650 # define UBF_TIMER_CLOCK CLOCK_REALTIME
1651 # else /* Tested Linux and FreeBSD: */
1652 # define UBF_TIMER_CLOCK CLOCK_MONOTONIC
1653 # endif
1654
1655 struct sigevent sev;
1656
1657 sev.sigev_notify = SIGEV_SIGNAL;
1658 sev.sigev_signo = SIGVTALRM;
1659 sev.sigev_value.sival_ptr = &timer_posix;
1660
1661 if (!timer_create(UBF_TIMER_CLOCK, &sev, &timer_posix.timerid)) {
1662 rb_atomic_t prev = ATOMIC_EXCHANGE(timer_posix.state, RTIMER_DISARM);
1663
1664 if (prev != RTIMER_DEAD) {
1665 rb_bug("timer_posix was not dead: %u\n", (unsigned)prev);
1666 }
1667 timer_posix.owner = current;
1668 }
1669 else {
1670 rb_warn("timer_create failed: %s, signals racy", strerror(errno));
1671 }
1672 #endif
1673 if (UBF_TIMER == UBF_TIMER_PTHREAD)
1674 ubf_timer_pthread_create(current);
1675 }
1676
1677 static void
1678 rb_thread_create_timer_thread(void)
1679 {
1680 /* we only create the pipe, and lazy-spawn */
1681 rb_pid_t current = getpid();
1682 rb_pid_t owner = signal_self_pipe.owner_process;
1683
1684 if (owner && owner != current) {
1685 CLOSE_INVALIDATE_PAIR(signal_self_pipe.normal);
1686 CLOSE_INVALIDATE_PAIR(signal_self_pipe.ub_main);
1687 ubf_timer_invalidate();
1688 }
1689
1690 if (setup_communication_pipe_internal(signal_self_pipe.normal) < 0) return;
1691 if (setup_communication_pipe_internal(signal_self_pipe.ub_main) < 0) return;
1692
1693 ubf_timer_create(current);
1694 if (owner != current) {
1695 /* validate pipe on this process */
1696 sigwait_th = THREAD_INVALID;
1697 signal_self_pipe.owner_process = current;
1698 }
1699 }
1700
1701 static void
1702 ubf_timer_disarm(void)
1703 {
1704 #if UBF_TIMER == UBF_TIMER_POSIX
1705 rb_atomic_t prev;
1706
1707 prev = ATOMIC_CAS(timer_posix.state, RTIMER_ARMED, RTIMER_DISARM);
1708 switch (prev) {
1709 case RTIMER_DISARM: return; /* likely */
1710 case RTIMER_ARMING: return; /* ubf_timer_arm will disarm itself */
1711 case RTIMER_ARMED:
1712 if (timer_settime(timer_posix.timerid, 0, &zero, 0)) {
1713 int err = errno;
1714
1715 if (err == EINVAL) {
1716 prev = ATOMIC_CAS(timer_posix.state, RTIMER_DISARM, RTIMER_DISARM);
1717
1718 /* main thread may have killed the timer */
1719 if (prev == RTIMER_DEAD) return;
1720
1721 rb_bug_errno("timer_settime (disarm)", err);
1722 }
1723 }
1724 return;
1725 case RTIMER_DEAD: return; /* stay dead */
1726 default:
1727 rb_bug("UBF_TIMER_POSIX bad state: %u\n", (unsigned)prev);
1728 }
1729
1730 #elif UBF_TIMER == UBF_TIMER_PTHREAD
1731 ATOMIC_SET(timer_pthread.armed, 0);
1732 #endif
1733 }
1734
1735 static void
1736 ubf_timer_destroy(void)
1737 {
1738 #if UBF_TIMER == UBF_TIMER_POSIX
1739 if (timer_posix.owner == getpid()) {
1740 rb_atomic_t expect = RTIMER_DISARM;
1741 size_t i, max = 10000000;
1742
1743 /* prevent signal handler from arming: */
1744 for (i = 0; i < max; i++) {
1745 switch (ATOMIC_CAS(timer_posix.state, expect, RTIMER_DEAD)) {
1746 case RTIMER_DISARM:
1747 if (expect == RTIMER_DISARM) goto done;
1748 expect = RTIMER_DISARM;
1749 break;
1750 case RTIMER_ARMING:
1751 native_thread_yield(); /* let another thread finish arming */
1752 expect = RTIMER_ARMED;
1753 break;
1754 case RTIMER_ARMED:
1755 if (expect == RTIMER_ARMED) {
1756 if (timer_settime(timer_posix.timerid, 0, &zero, 0))
1757 rb_bug_errno("timer_settime (destroy)", errno);
1758 goto done;
1759 }
1760 expect = RTIMER_ARMED;
1761 break;
1762 case RTIMER_DEAD:
1763 rb_bug("RTIMER_DEAD unexpected");
1764 }
1765 }
1766 rb_bug("timed out waiting for timer to arm");
1767 done:
1768 if (timer_delete(timer_posix.timerid) < 0)
1769 rb_sys_fail("timer_delete");
1770
1771 VM_ASSERT(ATOMIC_EXCHANGE(timer_posix.state, RTIMER_DEAD) == RTIMER_DEAD);
1772 }
1773 #elif UBF_TIMER == UBF_TIMER_PTHREAD
1774 int err;
1775
1776 timer_pthread.owner = 0;
1777 ubf_timer_disarm();
1778 rb_thread_wakeup_timer_thread_fd(timer_pthread.low[1]);
1779 err = pthread_join(timer_pthread.thid, 0);
1780 if (err) {
1781 rb_raise(rb_eThreadError, "native_thread_join() failed (%d)", err);
1782 }
1783 #endif
1784 }
1785
1786 static int
1787 native_stop_timer_thread(void)
1788 {
1789 int stopped;
1790 stopped = --system_working <= 0;
1791 if (stopped)
1792 ubf_timer_destroy();
1793
1794 if (TT_DEBUG) fprintf(stderr, "stop timer thread\n");
1795 return stopped;
1796 }
1797
1798 static void
1799 native_reset_timer_thread(void)
1800 {
1801 if (TT_DEBUG) fprintf(stderr, "reset timer thread\n");
1802 }
1803
1804 #ifdef HAVE_SIGALTSTACK
1805 int
1806 ruby_stack_overflowed_p(const rb_thread_t *th, const void *addr)
1807 {
1808 void *base;
1809 size_t size;
1810 const size_t water_mark = 1024 * 1024;
1811 STACK_GROW_DIR_DETECTION;
1812
1813 #ifdef STACKADDR_AVAILABLE
1814 if (get_stack(&base, &size) == 0) {
1815 # ifdef __APPLE__
1816 if (pthread_equal(th->thread_id, native_main_thread.id)) {
1817 struct rlimit rlim;
1818 if (getrlimit(RLIMIT_STACK, &rlim) == 0 && rlim.rlim_cur > size) {
1819 size = (size_t)rlim.rlim_cur;
1820 }
1821 }
1822 # endif
1823 base = (char *)base + STACK_DIR_UPPER(+size, -size);
1824 }
1825 else
1826 #endif
1827 if (th) {
1828 size = th->ec->machine.stack_maxsize;
1829 base = (char *)th->ec->machine.stack_start - STACK_DIR_UPPER(0, size);
1830 }
1831 else {
1832 return 0;
1833 }
1834 size /= RUBY_STACK_SPACE_RATIO;
1835 if (size > water_mark) size = water_mark;
1836 if (IS_STACK_DIR_UPPER()) {
1837 if (size > ~(size_t)base+1) size = ~(size_t)base+1;
1838 if (addr > base && addr <= (void *)((char *)base + size)) return 1;
1839 }
1840 else {
1841 if (size > (size_t)base) size = (size_t)base;
1842 if (addr > (void *)((char *)base - size) && addr <= base) return 1;
1843 }
1844 return 0;
1845 }
1846 #endif
1847
1848 int
1849 rb_reserved_fd_p(int fd)
1850 {
1851 /* no false-positive if out-of-FD at startup */
1852 if (fd < 0)
1853 return 0;
1854
1855 #if UBF_TIMER == UBF_TIMER_PTHREAD
1856 if (fd == timer_pthread.low[0] || fd == timer_pthread.low[1])
1857 goto check_pid;
1858 #endif
1859 if (fd == signal_self_pipe.normal[0] || fd == signal_self_pipe.normal[1])
1860 goto check_pid;
1861 if (fd == signal_self_pipe.ub_main[0] || fd == signal_self_pipe.ub_main[1])
1862 goto check_pid;
1863 return 0;
1864 check_pid:
1865 if (signal_self_pipe.owner_process == getpid()) /* async-signal-safe */
1866 return 1;
1867 return 0;
1868 }
1869
1870 rb_nativethread_id_t
1871 rb_nativethread_self(void)
1872 {
1873 return pthread_self();
1874 }
1875
1876 #if USE_MJIT
1877 /* A function that wraps actual worker function, for pthread abstraction. */
1878 static void *
1879 mjit_worker(void *arg)
1880 {
1881 void (*worker_func)(void) = (void(*)(void))arg;
1882
1883 #ifdef SET_CURRENT_THREAD_NAME
1884 SET_CURRENT_THREAD_NAME("ruby-mjitworker"); /* 16 byte including NUL */
1885 #endif
1886 worker_func();
1887 return NULL;
1888 }
1889
1890 /* Launch MJIT thread. Returns FALSE if it fails to create thread. */
1891 int
1892 rb_thread_create_mjit_thread(void (*worker_func)(void))
1893 {
1894 pthread_attr_t attr;
1895 pthread_t worker_pid;
1896 int ret = FALSE;
1897
1898 if (pthread_attr_init(&attr) != 0) return ret;
1899
1900 /* jit_worker thread is not to be joined */
1901 if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) == 0
1902 && pthread_create(&worker_pid, &attr, mjit_worker, (void *)worker_func) == 0) {
1903 ret = TRUE;
1904 }
1905 pthread_attr_destroy(&attr);
1906 return ret;
1907 }
1908 #endif
1909
1910 int
1911 rb_sigwait_fd_get(const rb_thread_t *th)
1912 {
1913 if (signal_self_pipe.normal[0] >= 0) {
1914 VM_ASSERT(signal_self_pipe.owner_process == getpid());
1915 /*
1916 * no need to keep firing the timer if any thread is sleeping
1917 * on the signal self-pipe
1918 */
1919 ubf_timer_disarm();
1920
1921 if (ATOMIC_PTR_CAS(sigwait_th, THREAD_INVALID, th) == THREAD_INVALID) {
1922 return signal_self_pipe.normal[0];
1923 }
1924 }
1925 return -1; /* avoid thundering herd and work stealing/starvation */
1926 }
1927
1928 void
1929 rb_sigwait_fd_put(const rb_thread_t *th, int fd)
1930 {
1931 const rb_thread_t *old;
1932
1933 VM_ASSERT(signal_self_pipe.normal[0] == fd);
1934 old = ATOMIC_PTR_EXCHANGE(sigwait_th, THREAD_INVALID);
1935 if (old != th) assert(old == th);
1936 }
1937
1938 #ifndef HAVE_PPOLL
1939 /* TODO: don't ignore sigmask */
1940 static int
1941 ruby_ppoll(struct pollfd *fds, nfds_t nfds,
1942 const struct timespec *ts, const sigset_t *sigmask)
1943 {
1944 int timeout_ms;
1945
1946 if (ts) {
1947 int tmp, tmp2;
1948
1949 if (ts->tv_sec > INT_MAX/1000)
1950 timeout_ms = INT_MAX;
1951 else {
1952 tmp = (int)(ts->tv_sec * 1000);
1953 /* round up 1ns to 1ms to avoid excessive wakeups for <1ms sleep */
1954 tmp2 = (int)((ts->tv_nsec + 999999L) / (1000L * 1000L));
1955 if (INT_MAX - tmp < tmp2)
1956 timeout_ms = INT_MAX;
1957 else
1958 timeout_ms = (int)(tmp + tmp2);
1959 }
1960 }
1961 else
1962 timeout_ms = -1;
1963
1964 return poll(fds, nfds, timeout_ms);
1965 }
1966 # define ppoll(fds,nfds,ts,sigmask) ruby_ppoll((fds),(nfds),(ts),(sigmask))
1967 #endif
1968
1969 void
1970 rb_sigwait_sleep(rb_thread_t *th, int sigwait_fd, const rb_hrtime_t *rel)
1971 {
1972 struct pollfd pfd;
1973 struct timespec ts;
1974
1975 pfd.fd = sigwait_fd;
1976 pfd.events = POLLIN;
1977
1978 if (!BUSY_WAIT_SIGNALS && ubf_threads_empty()) {
1979 (void)ppoll(&pfd, 1, rb_hrtime2timespec(&ts, rel), 0);
1980 check_signals_nogvl(th, sigwait_fd);
1981 }
1982 else {
1983 rb_hrtime_t to = RB_HRTIME_MAX, end;
1984 int n = 0;
1985
1986 if (rel) {
1987 to = *rel;
1988 end = rb_hrtime_add(rb_hrtime_now(), to);
1989 }
1990 /*
1991 * tricky: this needs to return on spurious wakeup (no auto-retry).
1992 * But we also need to distinguish between periodic quantum
1993 * wakeups, so we care about the result of consume_communication_pipe
1994 *
1995 * We want to avoid spurious wakeup for Mutex#sleep compatibility
1996 * [ruby-core:88102]
1997 */
1998 for (;;) {
1999 const rb_hrtime_t *sto = sigwait_timeout(th, sigwait_fd, &to, &n);
2000
2001 if (n) return;
2002 n = ppoll(&pfd, 1, rb_hrtime2timespec(&ts, sto), 0);
2003 if (check_signals_nogvl(th, sigwait_fd))
2004 return;
2005 if (n || (th && RUBY_VM_INTERRUPTED(th->ec)))
2006 return;
2007 if (rel && hrtime_update_expire(&to, end))
2008 return;
2009 }
2010 }
2011 }
2012
2013 /*
2014 * we need to guarantee wakeups from native_ppoll_sleep because
2015 * ubf_select may not be going through ubf_list if other threads
2016 * are all sleeping.
2017 */
2018 static void
2019 ubf_ppoll_sleep(void *ignore)
2020 {
2021 rb_thread_wakeup_timer_thread_fd(signal_self_pipe.ub_main[1]);
2022 }
2023
2024 /*
2025 * Single CPU setups benefit from explicit sched_yield() before ppoll(),
2026 * since threads may be too starved to enter the GVL waitqueue for
2027 * us to detect contention. Instead, we want to kick other threads
2028 * so they can run and possibly prevent us from entering slow paths
2029 * in ppoll() or similar syscalls.
2030 *
2031 * Confirmed on FreeBSD 11.2 and Linux 4.19.
2032 * [ruby-core:90417] [Bug #15398]
2033 */
2034 #define GVL_UNLOCK_BEGIN_YIELD(th) do { \
2035 const native_thread_data_t *next; \
2036 rb_vm_t *vm = th->vm; \
2037 RB_GC_SAVE_MACHINE_CONTEXT(th); \
2038 rb_native_mutex_lock(&vm->gvl.lock); \
2039 next = gvl_release_common(vm); \
2040 rb_native_mutex_unlock(&vm->gvl.lock); \
2041 if (!next && vm_living_thread_num(vm) > 1) { \
2042 native_thread_yield(); \
2043 }
2044
2045 /*
2046 * This function does not exclusively acquire sigwait_fd, so it
2047 * cannot safely read from it. However, it can be woken up in
2048 * 4 ways:
2049 *
2050 * 1) ubf_ppoll_sleep (from another thread)
2051 * 2) rb_thread_wakeup_timer_thread (from signal handler)
2052 * 3) any unmasked signal hitting the process
2053 * 4) periodic ubf timer wakeups (after 3)
2054 */
2055 static void
2056 native_ppoll_sleep(rb_thread_t *th, rb_hrtime_t *rel)
2057 {
2058 rb_native_mutex_lock(&th->interrupt_lock);
2059 th->unblock.func = ubf_ppoll_sleep;
2060 rb_native_mutex_unlock(&th->interrupt_lock);
2061
2062 GVL_UNLOCK_BEGIN_YIELD(th);
2063
2064 if (!RUBY_VM_INTERRUPTED(th->ec)) {
2065 struct pollfd pfd[2];
2066 struct timespec ts;
2067
2068 pfd[0].fd = signal_self_pipe.normal[0]; /* sigwait_fd */
2069 pfd[1].fd = signal_self_pipe.ub_main[0];
2070 pfd[0].events = pfd[1].events = POLLIN;
2071 if (ppoll(pfd, 2, rb_hrtime2timespec(&ts, rel), 0) > 0) {
2072 if (pfd[1].revents & POLLIN) {
2073 (void)consume_communication_pipe(pfd[1].fd);
2074 }
2075 }
2076 /*
2077 * do not read the sigwait_fd, here, let uplevel callers
2078 * or other threads that, otherwise we may steal and starve
2079 * other threads
2080 */
2081 }
2082 unblock_function_clear(th);
2083 GVL_UNLOCK_END(th);
2084 }
2085
2086 static void
2087 native_sleep(rb_thread_t *th, rb_hrtime_t *rel)
2088 {
2089 int sigwait_fd = rb_sigwait_fd_get(th);
2090
2091 if (sigwait_fd >= 0) {
2092 rb_native_mutex_lock(&th->interrupt_lock);
2093 th->unblock.func = ubf_sigwait;
2094 rb_native_mutex_unlock(&th->interrupt_lock);
2095
2096 GVL_UNLOCK_BEGIN_YIELD(th);
2097
2098 if (!RUBY_VM_INTERRUPTED(th->ec)) {
2099 rb_sigwait_sleep(th, sigwait_fd, rel);
2100 }
2101 else {
2102 check_signals_nogvl(th, sigwait_fd);
2103 }
2104 unblock_function_clear(th);
2105 GVL_UNLOCK_END(th);
2106 rb_sigwait_fd_put(th, sigwait_fd);
2107 rb_sigwait_fd_migrate(th->vm);
2108 }
2109 else if (th == th->vm->main_thread) { /* always able to handle signals */
2110 native_ppoll_sleep(th, rel);
2111 }
2112 else {
2113 native_cond_sleep(th, rel);
2114 }
2115 }
2116
2117 #if UBF_TIMER == UBF_TIMER_PTHREAD
2118 static void *
2119 timer_pthread_fn(void *p)
2120 {
2121 rb_vm_t *vm = p;
2122 pthread_t main_thread_id = vm->main_thread->thread_id;
2123 struct pollfd pfd;
2124 int timeout = -1;
2125 int ccp;
2126
2127 pfd.fd = timer_pthread.low[0];
2128 pfd.events = POLLIN;
2129
2130 while (system_working > 0) {
2131 (void)poll(&pfd, 1, timeout);
2132 ccp = consume_communication_pipe(pfd.fd);
2133
2134 if (system_working > 0) {
2135 if (ATOMIC_CAS(timer_pthread.armed, 1, 1)) {
2136 pthread_kill(main_thread_id, SIGVTALRM);
2137
2138 if (rb_signal_buff_size() || !ubf_threads_empty()) {
2139 timeout = TIME_QUANTUM_MSEC;
2140 }
2141 else {
2142 ATOMIC_SET(timer_pthread.armed, 0);
2143 timeout = -1;
2144 }
2145 }
2146 else if (ccp) {
2147 pthread_kill(main_thread_id, SIGVTALRM);
2148 ATOMIC_SET(timer_pthread.armed, 0);
2149 timeout = -1;
2150 }
2151 }
2152 }
2153
2154 return 0;
2155 }
2156 #endif /* UBF_TIMER_PTHREAD */
2157
2158 static VALUE
2159 ubf_caller(void *ignore)
2160 {
2161 rb_thread_sleep_forever();
2162
2163 return Qfalse;
2164 }
2165
2166 /*
2167 * Called if and only if one thread is running, and
2168 * the unblock function is NOT async-signal-safe
2169 * This assumes USE_THREAD_CACHE is true for performance reasons
2170 */
2171 static VALUE
2172 rb_thread_start_unblock_thread(void)
2173 {
2174 return rb_thread_create(ubf_caller, 0);
2175 }
2176 #endif /* THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION */
pthread_mutex_destroy
int pthread_mutex_destroy(pthread_mutex_t *)
sigaltstack
Definition: rb_mjit_min_header-2.7.0.h:2300
__attribute__
unsigned int UINT8 __attribute__((__mode__(__QI__)))
Definition: ffi_common.h:110
pthread_mutex_init
int pthread_mutex_init(pthread_mutex_t *, const pthread_mutexattr_t *)
UNLIKELY
#define UNLIKELY(x)
Definition: ffi_common.h:126
sig
int sig
Definition: rb_mjit_min_header-2.7.0.h:10422
native_thread_data_struct::cond
struct native_thread_data_struct::@162 cond
list_add
#define list_add(h, n)
Definition: rb_mjit_min_header-2.7.0.h:9004
void
void
Definition: rb_mjit_min_header-2.7.0.h:13273
itimerspec::it_interval
struct timespec it_interval
Definition: rb_mjit_min_header-2.7.0.h:1258
rb_fd_select
int rb_fd_select(int, rb_fdset_t *, rb_fdset_t *, rb_fdset_t *, struct timeval *)
__pthread_condattr_t
Definition: rb_mjit_min_header-2.7.0.h:1350
TRUE
#define TRUE
Definition: nkf.h:175
pthread_attr_getstack
int pthread_attr_getstack(const pthread_attr_t *, void **, size_t *)
EBUSY
#define EBUSY
Definition: rb_mjit_min_header-2.7.0.h:10953
RB_HRTIME_PER_SEC
#define RB_HRTIME_PER_SEC
Definition: hrtime.h:37
STACK_UPPER
#define STACK_UPPER(x, a, b)
Definition: gc.h:83
abs
int abs(int)
pthread_attr_init
int pthread_attr_init(pthread_attr_t *)
RUBY_VM_INTERRUPTED
#define RUBY_VM_INTERRUPTED(ec)
Definition: vm_core.h:1840
rb_cloexec_pipe
int rb_cloexec_pipe(int fildes[2])
Definition: io.c:378
assert
#define assert(x)
Definition: dlmalloc.c:1176
rb_hrtime_now
rb_hrtime_t rb_hrtime_now(void)
Definition: thread.c:1229
id
const int id
Definition: nkf.c:209
pthread_setschedparam
int pthread_setschedparam(pthread_t, int, const struct sched_param *)
rb_nativethread_self
RUBY_SYMBOL_EXPORT_BEGIN rb_nativethread_id_t rb_nativethread_self()
pthread_mutex_unlock
int pthread_mutex_unlock(pthread_mutex_t *)
close
int close(int __fildes)
ATOMIC_PTR_EXCHANGE
#define ATOMIC_PTR_EXCHANGE(var, val)
Definition: ruby_atomic.h:186
rb_timespec_now
void rb_timespec_now(struct timespec *)
Definition: time.c:1873
rb_warn
void rb_warn(const char *fmt,...)
Definition: error.c:313
size_t
long unsigned int size_t
Definition: rb_mjit_min_header-2.7.0.h:666
gc.h
EWOULDBLOCK
#define EWOULDBLOCK
Definition: rubysocket.h:134
rb_vm_struct::thread_machine_stack_size
size_t thread_machine_stack_size
Definition: vm_core.h:664
rb_gc_force_recycle
void rb_gc_force_recycle(VALUE obj)
Definition: gc.c:7011
snprintf
int snprintf(char *__restrict, size_t, const char *__restrict,...) __attribute__((__format__(__printf__
EINTR
#define EINTR
Definition: rb_mjit_min_header-2.7.0.h:10941
pthread_attr_getstacksize
int pthread_attr_getstacksize(const pthread_attr_t *, size_t *)
RSTRING_PTR
#define RSTRING_PTR(str)
Definition: ruby.h:1009
rb_bug_errno
void rb_bug_errno(const char *mesg, int errno_arg)
Definition: error.c:667
O_NONBLOCK
#define O_NONBLOCK
Definition: win32.h:611
native_thread_data_struct::intr
rb_nativethread_cond_t intr
Definition: thread_pthread.h:38
getpagesize
int getpagesize(void)
VALUE
unsigned long VALUE
Definition: ruby.h:102
GET_VM
#define GET_VM()
Definition: vm_core.h:1764
list_del
#define list_del(n)
Definition: rb_mjit_min_header-2.7.0.h:9041
pthread_cond_wait
int pthread_cond_wait(pthread_cond_t *, pthread_mutex_t *)
LIST_HEAD
#define LIST_HEAD(name)
Definition: rb_mjit_min_header-2.7.0.h:8983
ERANGE
#define ERANGE
Definition: rb_mjit_min_header-2.7.0.h:10971
rb_hrtime_t
uint64_t rb_hrtime_t
Definition: hrtime.h:47
int
__inline__ int
Definition: rb_mjit_min_header-2.7.0.h:2839
old
VALUE ID VALUE old
Definition: rb_mjit_min_header-2.7.0.h:16133
rb_thread_struct::thread_id
rb_nativethread_id_t thread_id
Definition: vm_core.h:927
RB_HRTIME_MAX
#define RB_HRTIME_MAX
Definition: hrtime.h:38
__pthread_key_t
Definition: rb_mjit_min_header-2.7.0.h:1347
rb_vm_struct::default_params
struct rb_vm_struct::@185 default_params
pthread_attr_getguardsize
int pthread_attr_getguardsize(const pthread_attr_t *, size_t *)
uint64_t
unsigned long long uint64_t
Definition: sha2.h:102
rb_global_vm_lock_struct::switch_wait_cond
rb_nativethread_cond_t switch_wait_cond
Definition: thread_pthread.h:70
ruby_sigchld_handler
void ruby_sigchld_handler(rb_vm_t *vm)
Definition: signal.c:1073
list_pop
#define list_pop(h, type, member)
Definition: rb_mjit_min_header-2.7.0.h:9077
thread_debug
#define thread_debug
Definition: thread.c:330
rb_signal_buff_size
int rb_signal_buff_size(void)
Definition: signal.c:726
BUSY_WAIT_SIGNALS
#define BUSY_WAIT_SIGNALS
Definition: thread.c:379
EXIT_FAILURE
#define EXIT_FAILURE
Definition: eval_intern.h:32
pthread_self
pthread_t pthread_self(void)
ptr
struct RIMemo * ptr
Definition: debug.c:74
Qfalse
#define Qfalse
Definition: ruby.h:467
uintptr_t
unsigned int uintptr_t
Definition: win32.h:106
ssize_t
_ssize_t ssize_t
Definition: rb_mjit_min_header-2.7.0.h:1329
ATOMIC_PTR_CAS
#define ATOMIC_PTR_CAS(var, oldval, val)
Definition: ruby_atomic.h:201
rb_global_vm_lock_struct::owner
const struct rb_thread_struct * owner
Definition: thread_pthread.h:50
NULL
#define NULL
Definition: _sdbm.c:101
exit
void exit(int __status) __attribute__((__noreturn__))
ruby::backward::cxxanyargs::rb_thread_create
VALUE rb_thread_create(type *q, void *w)
Creates a rb_cThread instance.
Definition: cxxanyargs.hpp:340
native_thread_data_struct::gvl
struct list_node gvl
Definition: thread_pthread.h:26
fcntl
int fcntl(int, int,...)
Definition: win32.c:4282
last
unsigned int last
Definition: nkf.c:4324
pthread_cond_destroy
int pthread_cond_destroy(pthread_cond_t *)
rb_thread_struct::ec
rb_execution_context_t * ec
Definition: vm_core.h:915
rb_global_vm_lock_struct::need_yield
int need_yield
Definition: thread_pthread.h:71
native_thread_data_struct::ubf
struct list_node ubf
Definition: thread_pthread.h:25
VM_ASSERT
#define VM_ASSERT(expr)
Definition: vm_core.h:56
native_thread_data_struct::node
union native_thread_data_struct::@161 node
PTHREAD_CREATE_DETACHED
#define PTHREAD_CREATE_DETACHED
Definition: rb_mjit_min_header-2.7.0.h:9203
ATOMIC_SET
#define ATOMIC_SET(var, val)
Definition: ruby_atomic.h:131
L
#define L(x)
Definition: asm.h:125
posix_signal
#define posix_signal
Definition: rb_mjit_min_header-2.7.0.h:6022
timespec::tv_nsec
long tv_nsec
Definition: missing.h:62
rb_thread_struct::priority
int8_t priority
Definition: vm_core.h:937
rb_fdset_t
Definition: rb_mjit_min_header-2.7.0.h:5705
rb_raise
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2669
pthread_mutex_trylock
int pthread_mutex_trylock(pthread_mutex_t *)
native_thread_data_struct::gvlq
rb_nativethread_cond_t gvlq
Definition: thread_pthread.h:39
pthread_create
int pthread_create(pthread_t *, const pthread_attr_t *, void *(*)(void *), void *)
Init_native_thread
void Init_native_thread(rb_thread_t *th)
rb_global_vm_lock_struct::waitq
struct list_head waitq
Definition: thread_pthread.h:64
EAGAIN
#define EAGAIN
Definition: rb_mjit_min_header-2.7.0.h:10948
rb_global_vm_lock_struct::timer_err
int timer_err
Definition: thread_pthread.h:66
sigevent
Definition: rb_mjit_min_header-2.7.0.h:2086
WRITE_CONST
#define WRITE_CONST(fd, str)
Definition: error.c:685
sched_param
Definition: rb_mjit_min_header-2.7.0.h:9165
PRI_THREAD_ID
#define PRI_THREAD_ID
Definition: thread.c:337
rb_native_cond_initialize
void rb_native_cond_initialize(rb_nativethread_cond_t *cond)
GVL_UNLOCK_END
#define GVL_UNLOCK_END(th)
Definition: thread.c:174
rb_global_vm_lock_struct::wait_yield
int wait_yield
Definition: thread_pthread.h:72
strerror
RUBY_EXTERN char * strerror(int)
Definition: strerror.c:11
fprintf
int fprintf(FILE *__restrict, const char *__restrict,...) __attribute__((__format__(__printf__
__pthread_mutex_t
Definition: rb_mjit_min_header-2.7.0.h:1346
timespec::tv_sec
time_t tv_sec
Definition: missing.h:61
pthread_attr_setstacksize
int pthread_attr_setstacksize(pthread_attr_t *, size_t)
ruby_init_stack
void ruby_init_stack(volatile VALUE *)
itimerspec
Definition: rb_mjit_min_header-2.7.0.h:1257
sched_get_priority_max
int sched_get_priority_max(int __policy)
pthread_attr_destroy
int pthread_attr_destroy(pthread_attr_t *)
rb_vm_struct::main_thread
struct rb_thread_struct * main_thread
Definition: vm_core.h:581
pthread_mutex_lock
int pthread_mutex_lock(pthread_mutex_t *)
list_head
Definition: rb_mjit_min_header-2.7.0.h:8975
thread_id_str
#define thread_id_str(th)
Definition: thread.c:336
SET_CURRENT_THREAD_NAME
#define SET_CURRENT_THREAD_NAME(name)
Definition: rb_mjit_min_header-2.7.0.h:429
i
uint32_t i
Definition: rb_mjit_min_header-2.7.0.h:5464
list_for_each
#define list_for_each(h, i, member)
Definition: rb_mjit_min_header-2.7.0.h:9094
pthread_join
int pthread_join(pthread_t, void **)
sigset_t
__sigset_t sigset_t
Definition: rb_mjit_min_header-2.7.0.h:1262
pthread_condattr_setclock
int pthread_condattr_setclock(pthread_condattr_t *, clockid_t)
rb_vm_struct::gvl
rb_global_vm_lock_t gvl
Definition: vm_core.h:579
container_of
#define container_of(member_ptr, containing_type, member)
Definition: rb_mjit_min_header-2.7.0.h:8962
INT_MAX
#define INT_MAX
Definition: rb_mjit_min_header-2.7.0.h:4052
mjit.h
alloca
#define alloca(size)
Definition: rb_mjit_min_header-2.7.0.h:2487
RB_ALTSTACK
#define RB_ALTSTACK(var)
Definition: vm_core.h:143
list_add_tail
#define list_add_tail(h, n)
Definition: rb_mjit_min_header-2.7.0.h:9023
mjit_worker
void mjit_worker(void)
Definition: mjit_worker.c:1194
rb_update_max_fd
void rb_update_max_fd(int fd)
Definition: io.c:218
list_empty
#define list_empty(h)
Definition: rb_mjit_min_header-2.7.0.h:9030
rb_global_vm_lock_struct::switch_cond
rb_nativethread_cond_t switch_cond
Definition: thread_pthread.h:69
list_del_init
#define list_del_init(n)
Definition: rb_mjit_min_header-2.7.0.h:9048
stderr
#define stderr
Definition: rb_mjit_min_header-2.7.0.h:1485
rb_sys_fail
void rb_sys_fail(const char *mesg)
Definition: error.c:2793
rb_vm_struct::thread_vm_stack_size
size_t thread_vm_stack_size
Definition: vm_core.h:663
rb_thread_sleep_forever
void rb_thread_sleep_forever(void)
Definition: thread.c:1314
rb_execution_context_struct::stack_start
VALUE * stack_start
Definition: vm_core.h:887
rb_native_cond_destroy
void rb_native_cond_destroy(rb_nativethread_cond_t *cond)
RARRAY_AREF
#define RARRAY_AREF(a, i)
Definition: ruby.h:1101
rb_async_bug_errno
void rb_async_bug_errno(const char *mesg, int errno_arg)
Definition: error.c:688
rb_native_cond_broadcast
void rb_native_cond_broadcast(rb_nativethread_cond_t *cond)
rb_eThreadError
VALUE rb_eThreadError
Definition: eval.c:923
size
int size
Definition: encoding.c:58
rb_native_mutex_unlock
void rb_native_mutex_unlock(rb_nativethread_lock_t *lock)
FALSE
#define FALSE
Definition: nkf.h:174
pthread_getattr_np
int pthread_getattr_np(pthread_t, pthread_attr_t *)
pthread_cond_timedwait
int pthread_cond_timedwait(pthread_cond_t *, pthread_mutex_t *, const struct timespec *)
pthread_getspecific
void * pthread_getspecific(pthread_key_t)
getpid
pid_t getpid(void)
rb_thread_wakeup_timer_thread
void rb_thread_wakeup_timer_thread(int)
pthread_equal
int pthread_equal(pthread_t, pthread_t)
__pthread_attr_t
Definition: rb_mjit_min_header-2.7.0.h:1348
EINVAL
#define EINVAL
Definition: rb_mjit_min_header-2.7.0.h:10959
rb_execution_context_struct::stack_maxsize
size_t stack_maxsize
Definition: vm_core.h:889
USE_EVENTFD
#define USE_EVENTFD
Definition: thread.c:383
fill_thread_id_str
#define fill_thread_id_str(th)
Definition: thread.c:335
pthread_attr_setinheritsched
int pthread_attr_setinheritsched(pthread_attr_t *, int)
rb_native_mutex_lock
void rb_native_mutex_lock(rb_nativethread_lock_t *lock)
rb_eNotImpError
VALUE rb_eNotImpError
Definition: error.c:932
ACCESS_ONCE
#define ACCESS_ONCE(type, x)
Definition: internal.h:223
rb_sigwait_sleep
void rb_sigwait_sleep(const rb_thread_t *, int fd, const rb_hrtime_t *)
rb_thread_struct::name
VALUE name
Definition: vm_core.h:989
rb_thread_struct::vm
rb_vm_t * vm
Definition: vm_core.h:913
CLOCK_MONOTONIC
#define CLOCK_MONOTONIC
Definition: win32.h:134
list_tail
#define list_tail(h, type, member)
Definition: rb_mjit_min_header-2.7.0.h:9087
__pthread_cond_t
Definition: rb_mjit_min_header-2.7.0.h:1351
rb_execution_context_struct::machine
struct rb_execution_context_struct::@188 machine
rb_sigwait_fd_migrate
void rb_sigwait_fd_migrate(rb_vm_t *vm)
Definition: process.c:998
n
const char size_t n
Definition: rb_mjit_min_header-2.7.0.h:5456
buf
unsigned char buf[MIME_BUF_SIZE]
Definition: nkf.c:4322
pthread_kill
int pthread_kill(pthread_t, int)
SET_ANOTHER_THREAD_NAME
#define SET_ANOTHER_THREAD_NAME(thid, name)
Definition: rb_mjit_min_header-2.7.0.h:430
ETIMEDOUT
#define ETIMEDOUT
Definition: win32.h:570
rb_bug
void rb_bug(const char *fmt,...)
Definition: error.c:634
RB_ALTSTACK_INIT
#define RB_ALTSTACK_INIT(var)
Definition: vm_core.h:141
arg
VALUE arg
Definition: rb_mjit_min_header-2.7.0.h:5601
rb_native_mutex_destroy
void rb_native_mutex_destroy(rb_nativethread_lock_t *lock)
RUBY_VM_THREAD_VM_STACK_SIZE
#define RUBY_VM_THREAD_VM_STACK_SIZE
Definition: vm_core.h:676
F_SETFL
#define F_SETFL
Definition: win32.h:608
stdout
#define stdout
Definition: rb_mjit_min_header-2.7.0.h:1484
timer_create
int timer_create(clockid_t clock_id, struct sigevent *__restrict evp, timer_t *__restrict timerid)
SIGVTALRM
#define SIGVTALRM
Definition: rb_mjit_min_header-2.7.0.h:2276
strrchr
char * strrchr(const char *, const char)
timeval
Definition: missing.h:53
pthread_attr_setdetachstate
int pthread_attr_setdetachstate(pthread_attr_t *, int)
rb_thread_struct::unblock
struct rb_unblock_callback unblock
Definition: vm_core.h:957
GET_THREAD
#define GET_THREAD()
Definition: vm_core.h:1765
pthread_cond_signal
int pthread_cond_signal(pthread_cond_t *)
timer_settime
int timer_settime(timer_t timerid, int flags, const struct itimerspec *__restrict value, struct itimerspec *__restrict ovalue)
ATOMIC_EXCHANGE
#define ATOMIC_EXCHANGE(var, val)
Definition: ruby_atomic.h:135
NIL_P
#define NIL_P(v)
Definition: ruby.h:482
rb_sigwait_fd_get
int rb_sigwait_fd_get(const rb_thread_t *)
rb_thread_struct::native_thread_data
native_thread_data_t native_thread_data
Definition: vm_core.h:940
pthread_getschedparam
int pthread_getschedparam(pthread_t, int *, struct sched_param *)
STACK_DIR_UPPER
#define STACK_DIR_UPPER(a, b)
Definition: gc.h:99
pthread_cond_init
int pthread_cond_init(pthread_cond_t *, const pthread_condattr_t *)
list_node
Definition: rb_mjit_min_header-2.7.0.h:8971
sched_get_priority_min
int sched_get_priority_min(int __policy)
err
int err
Definition: win32.c:135
SIGEV_SIGNAL
@ SIGEV_SIGNAL
Definition: rb_mjit_min_header-2.7.0.h:2217
rb_vm_struct
Definition: vm_core.h:576
sigaltstack::ss_size
size_t ss_size
Definition: rb_mjit_min_header-2.7.0.h:2303
pthread_key_create
int pthread_key_create(pthread_key_t *, void(*)(void *))
PTHREAD_MUTEX_INITIALIZER
#define PTHREAD_MUTEX_INITIALIZER
Definition: rb_mjit_min_header-2.7.0.h:9214
__pthread_t
Definition: rb_mjit_min_header-2.7.0.h:1345
IS_STACK_DIR_UPPER
#define IS_STACK_DIR_UPPER()
Definition: gc.h:101
errno
int errno
STACK_GROW_DIR_DETECTION
#define STACK_GROW_DIR_DETECTION
Definition: gc.h:98
len
uint8_t len
Definition: escape.c:17
rb_vm_struct::ubf_async_safe
volatile int ubf_async_safe
Definition: vm_core.h:600
rb_native_cond_wait
void rb_native_cond_wait(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *mutex)
timer_t
__timer_t timer_t
Definition: rb_mjit_min_header-2.7.0.h:1338
timespec
Definition: missing.h:60
sigevent::sigev_notify
int sigev_notify
Definition: rb_mjit_min_header-2.7.0.h:2090
native_thread_data_struct
Definition: thread_pthread.h:23
MAYBE_UNUSED
#define MAYBE_UNUSED
Definition: ffi_common.h:32
rb_atomic_t
int rb_atomic_t
Definition: ruby_atomic.h:124
rb_sigwait_fd_put
void rb_sigwait_fd_put(const rb_thread_t *, int fd)
rb_pid_t
#define rb_pid_t
Definition: rb_mjit_min_header-2.7.0.h:99
rb_unblock_callback::arg
void * arg
Definition: vm_core.h:818
ATOMIC_CAS
#define ATOMIC_CAS(var, oldval, newval)
Definition: ruby_atomic.h:136
rb_thread_struct::interrupt_lock
rb_nativethread_lock_t interrupt_lock
Definition: vm_core.h:956
rb_global_vm_lock_struct::timer
const struct rb_thread_struct * timer
Definition: thread_pthread.h:65
GVL_UNLOCK_BEGIN
#define GVL_UNLOCK_BEGIN(th)
Definition: thread.c:170
RB_ALTSTACK_FREE
#define RB_ALTSTACK_FREE(var)
Definition: vm_core.h:142
NUM2INT
#define NUM2INT(x)
Definition: ruby.h:715
Qnil
#define Qnil
Definition: ruby.h:469
pthread_condattr_init
int pthread_condattr_init(pthread_condattr_t *)
pthread_cond_broadcast
int pthread_cond_broadcast(pthread_cond_t *)
thread.h
RUBY_VM_SET_TRAP_INTERRUPT
#define RUBY_VM_SET_TRAP_INTERRUPT(ec)
Definition: vm_core.h:1839
write
_ssize_t write(int __fd, const void *__buf, size_t __nbyte)
rb_reserved_fd_p
int rb_reserved_fd_p(int fd)
rb_thread_struct
Definition: vm_core.h:910
PTHREAD_INHERIT_SCHED
#define PTHREAD_INHERIT_SCHED
Definition: rb_mjit_min_header-2.7.0.h:9206
pthread_setspecific
int pthread_setspecific(pthread_key_t, const void *)
rb_native_mutex_initialize
void rb_native_mutex_initialize(rb_nativethread_lock_t *lock)
timer_delete
int timer_delete(timer_t timerid)
rb_native_cond_signal
void rb_native_cond_signal(rb_nativethread_cond_t *cond)
sigaltstack::ss_sp
void * ss_sp
Definition: rb_mjit_min_header-2.7.0.h:2301
rb_unblock_callback::func
rb_unblock_function_t * func
Definition: vm_core.h:817
list_top
#define list_top(h, type, member)
Definition: rb_mjit_min_header-2.7.0.h:9070
RB_NATIVETHREAD_LOCK_INIT
#define RB_NATIVETHREAD_LOCK_INIT
Definition: thread_pthread.h:18
rb_global_vm_lock_struct::lock
rb_nativethread_lock_t lock
Definition: thread_pthread.h:51
name
const char * name
Definition: nkf.c:208
rb_execution_context_struct
Definition: vm_core.h:843