2 /**********************************************************************
8 Copyright (C) 2004-2007 Koichi Sasada
10 **********************************************************************/
12 #ifdef THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION
16 #ifdef HAVE_SYS_RESOURCE_H
17 #include <sys/resource.h>
20 static void native_mutex_lock(pthread_mutex_t
*lock
);
21 static void native_mutex_unlock(pthread_mutex_t
*lock
);
22 static int native_mutex_trylock(pthread_mutex_t
*lock
);
23 static void native_mutex_initialize(pthread_mutex_t
*lock
);
24 static void native_mutex_destroy(pthread_mutex_t
*lock
);
26 static void native_cond_signal(pthread_cond_t
*cond
);
27 static void native_cond_broadcast(pthread_cond_t
*cond
);
28 static void native_cond_wait(pthread_cond_t
*cond
, pthread_mutex_t
*mutex
);
29 static void native_cond_initialize(pthread_cond_t
*cond
);
30 static void native_cond_destroy(pthread_cond_t
*cond
);
33 native_mutex_lock(pthread_mutex_t
*lock
)
36 if ((r
= pthread_mutex_lock(lock
)) != 0) {
37 rb_bug("pthread_mutex_lock: %d", r
);
42 native_mutex_unlock(pthread_mutex_t
*lock
)
45 if ((r
= pthread_mutex_unlock(lock
)) != 0) {
46 rb_bug("native_mutex_unlock return non-zero: %d", r
);
51 native_mutex_trylock(pthread_mutex_t
*lock
)
54 if ((r
= pthread_mutex_trylock(lock
)) != 0) {
59 rb_bug("native_mutex_trylock return non-zero: %d", r
);
66 native_mutex_initialize(pthread_mutex_t
*lock
)
68 int r
= pthread_mutex_init(lock
, 0);
70 rb_bug("native_mutex_initialize return non-zero: %d", r
);
75 native_mutex_destroy(pthread_mutex_t
*lock
)
77 int r
= pthread_mutex_destroy(lock
);
79 rb_bug("native_mutex_destroy return non-zero: %d", r
);
84 native_cond_initialize(pthread_cond_t
*cond
)
86 int r
= pthread_cond_init(cond
, 0);
88 rb_bug("native_cond_initialize return non-zero: %d", r
);
93 native_cond_destroy(pthread_cond_t
*cond
)
95 int r
= pthread_cond_destroy(cond
);
97 rb_bug("native_cond_destroy return non-zero: %d", r
);
102 native_cond_signal(pthread_cond_t
*cond
)
104 pthread_cond_signal(cond
);
108 native_cond_broadcast(pthread_cond_t
*cond
)
110 pthread_cond_broadcast(cond
);
114 native_cond_wait(pthread_cond_t
*cond
, pthread_mutex_t
*mutex
)
116 pthread_cond_wait(cond
, mutex
);
120 #define native_cleanup_push pthread_cleanup_push
121 #define native_cleanup_pop pthread_cleanup_pop
122 #ifdef HAVE_SCHED_YIELD
123 #define native_thread_yield() (void)sched_yield()
125 #define native_thread_yield() ((void)0)
129 static void add_signal_thread_list(rb_thread_t
*th
);
131 static void remove_signal_thread_list(rb_thread_t
*th
);
133 static rb_thread_lock_t signal_thread_list_lock
;
135 static pthread_key_t ruby_native_thread_key
;
144 ruby_thread_from_native(void)
146 return pthread_getspecific(ruby_native_thread_key
);
150 ruby_thread_set_native(rb_thread_t
*th
)
152 return pthread_setspecific(ruby_native_thread_key
, th
) == 0;
156 Init_native_thread(void)
158 rb_thread_t
*th
= GET_THREAD();
160 pthread_key_create(&ruby_native_thread_key
, NULL
);
161 th
->thread_id
= pthread_self();
162 native_cond_initialize(&th
->native_thread_data
.sleep_cond
);
163 ruby_thread_set_native(th
);
164 native_mutex_initialize(&signal_thread_list_lock
);
165 posix_signal(SIGVTALRM
, null_func
);
169 native_thread_destroy(rb_thread_t
*th
)
171 pthread_mutex_destroy(&th
->interrupt_lock
);
172 pthread_cond_destroy(&th
->native_thread_data
.sleep_cond
);
175 #define USE_THREAD_CACHE 0
179 size_t stack_maxsize
;
182 VALUE
*register_stack_start
;
184 } native_main_thread
;
186 #ifdef STACK_END_ADDRESS
187 extern void *STACK_END_ADDRESS
;
190 #undef ruby_init_stack
192 ruby_init_stack(VALUE
*addr
198 native_main_thread
.id
= pthread_self();
199 #ifdef STACK_END_ADDRESS
200 native_main_thread
.stack_start
= STACK_END_ADDRESS
;
202 if (!native_main_thread
.stack_start
||
204 native_main_thread
.stack_start
> addr
,
205 native_main_thread
.stack_start
< addr
)) {
206 native_main_thread
.stack_start
= addr
;
210 if (!native_main_thread
.register_stack_start
||
211 (VALUE
*)bsp
< native_main_thread
.register_stack_start
) {
212 native_main_thread
.register_stack_start
= (VALUE
*)bsp
;
215 #ifdef HAVE_GETRLIMIT
219 if (getrlimit(RLIMIT_STACK
, &rlim
) == 0) {
220 unsigned int space
= rlim
.rlim_cur
/5;
222 if (space
> 1024*1024) space
= 1024*1024;
223 native_main_thread
.stack_maxsize
= rlim
.rlim_cur
- space
;
229 #define CHECK_ERR(expr) \
230 {int err = (expr); if (err) {rb_bug("err: %d - %s", err, #expr);}}
233 native_thread_init_stack(rb_thread_t
*th
)
235 rb_thread_id_t curr
= pthread_self();
237 if (pthread_equal(curr
, native_main_thread
.id
)) {
238 th
->machine_stack_start
= native_main_thread
.stack_start
;
239 th
->machine_stack_maxsize
= native_main_thread
.stack_maxsize
;
242 #ifdef HAVE_PTHREAD_GETATTR_NP
245 CHECK_ERR(pthread_getattr_np(curr
, &attr
));
246 # if defined HAVE_PTHREAD_ATTR_GETSTACK
247 CHECK_ERR(pthread_attr_getstack(&attr
, &start
, &th
->machine_stack_maxsize
));
248 # elif defined HAVE_PTHREAD_ATTR_GETSTACKSIZE && defined HAVE_PTHREAD_ATTR_GETSTACKADDR
249 CHECK_ERR(pthread_attr_getstackaddr(&attr
, &start
));
250 CHECK_ERR(pthread_attr_getstacksize(&attr
, &th
->machine_stack_maxsize
));
252 th
->machine_stack_start
= start
;
254 rb_raise(rb_eNotImpError
, "ruby engine can initialize only in the main thread");
258 th
->machine_register_stack_start
= native_main_thread
.register_stack_start
;
259 th
->machine_stack_maxsize
/= 2;
260 th
->machine_register_stack_maxsize
= th
->machine_stack_maxsize
;
266 thread_start_func_1(void *th_ptr
)
272 rb_thread_t
*th
= th_ptr
;
276 thread_start_func_2(th
, &stack_start
, rb_ia64_bsp());
282 static rb_thread_t
*register_cached_thread_and_wait(void);
283 if ((th
= register_cached_thread_and_wait()) != 0) {
285 th
->thread_id
= pthread_self();
293 void rb_thread_create_control_thread(void);
295 struct cached_thread_entry
{
296 volatile rb_thread_t
**th_area
;
297 pthread_cond_t
*cond
;
298 struct cached_thread_entry
*next
;
303 static pthread_mutex_t thread_cache_lock
= PTHREAD_MUTEX_INITIALIZER
;
304 struct cached_thread_entry
*cached_thread_root
;
307 register_cached_thread_and_wait(void)
309 pthread_cond_t cond
= PTHREAD_COND_INITIALIZER
;
310 volatile rb_thread_t
*th_area
= 0;
311 struct cached_thread_entry
*entry
=
312 (struct cached_thread_entry
*)malloc(sizeof(struct cached_thread_entry
));
316 gettimeofday(&tv
, 0);
317 ts
.tv_sec
= tv
.tv_sec
+ 60;
318 ts
.tv_nsec
= tv
.tv_usec
* 1000;
320 pthread_mutex_lock(&thread_cache_lock
);
322 entry
->th_area
= &th_area
;
324 entry
->next
= cached_thread_root
;
325 cached_thread_root
= entry
;
327 pthread_cond_timedwait(&cond
, &thread_cache_lock
, &ts
);
330 struct cached_thread_entry
*e
= cached_thread_root
;
331 struct cached_thread_entry
*prev
= cached_thread_root
;
335 if (prev
== cached_thread_root
) {
336 cached_thread_root
= e
->next
;
339 prev
->next
= e
->next
;
348 free(entry
); /* ok */
349 pthread_cond_destroy(&cond
);
351 pthread_mutex_unlock(&thread_cache_lock
);
353 return (rb_thread_t
*)th_area
;
358 use_cached_thread(rb_thread_t
*th
)
362 struct cached_thread_entry
*entry
;
364 if (cached_thread_root
) {
365 pthread_mutex_lock(&thread_cache_lock
);
366 entry
= cached_thread_root
;
368 if (cached_thread_root
) {
369 cached_thread_root
= entry
->next
;
370 *entry
->th_area
= th
;
375 pthread_cond_signal(entry
->cond
);
377 pthread_mutex_unlock(&thread_cache_lock
);
384 native_thread_create(rb_thread_t
*th
)
388 if (use_cached_thread(th
)) {
389 thread_debug("create (use cached thread): %p\n", th
);
393 size_t stack_size
= 512 * 1024; /* 512KB */
396 #ifdef PTHREAD_STACK_MIN
397 if (stack_size
< PTHREAD_STACK_MIN
) {
398 stack_size
= PTHREAD_STACK_MIN
* 2;
401 space
= stack_size
/5;
402 if (space
> 1024*1024) space
= 1024*1024;
403 th
->machine_stack_maxsize
= stack_size
- space
;
405 th
->machine_stack_maxsize
/= 2;
406 th
->machine_register_stack_maxsize
= th
->machine_stack_maxsize
;
409 CHECK_ERR(pthread_attr_init(&attr
));
411 #ifdef PTHREAD_STACK_MIN
412 thread_debug("create - stack size: %lu\n", (unsigned long)stack_size
);
413 CHECK_ERR(pthread_attr_setstacksize(&attr
, stack_size
));
416 #ifdef HAVE_PTHREAD_ATTR_SETINHERITSCHED
417 CHECK_ERR(pthread_attr_setinheritsched(&attr
, PTHREAD_INHERIT_SCHED
));
419 CHECK_ERR(pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
));
421 err
= pthread_create(&th
->thread_id
, &attr
, thread_start_func_1
, th
);
422 thread_debug("create: %p (%d)", th
, err
);
423 CHECK_ERR(pthread_attr_destroy(&attr
));
426 pthread_cond_init(&th
->native_thread_data
.sleep_cond
, 0);
429 st_delete_wrap(th
->vm
->living_threads
, th
->self
);
430 th
->status
= THREAD_KILLED
;
431 rb_raise(rb_eThreadError
, "can't create Thread (%d)", err
);
438 native_thread_join(pthread_t th
)
440 int err
= pthread_join(th
, 0);
442 rb_raise(rb_eThreadError
, "native_thread_join() failed (%d)", err
);
447 native_thread_apply_priority(rb_thread_t
*th
)
449 #if defined(_POSIX_PRIORITY_SCHEDULING) && (_POSIX_PRIORITY_SCHEDULING > 0)
450 struct sched_param sp
;
452 int priority
= 0 - th
->priority
;
454 pthread_getschedparam(th
->thread_id
, &policy
, &sp
);
455 max
= sched_get_priority_max(policy
);
456 min
= sched_get_priority_min(policy
);
458 if (min
> priority
) {
461 else if (max
< priority
) {
465 sp
.sched_priority
= priority
;
466 pthread_setschedparam(th
->thread_id
, policy
, &sp
);
473 ubf_pthread_cond_signal(void *ptr
)
475 rb_thread_t
*th
= (rb_thread_t
*)ptr
;
476 thread_debug("ubf_pthread_cond_signal (%p)\n", th
);
477 pthread_cond_signal(&th
->native_thread_data
.sleep_cond
);
482 ubf_select_each(rb_thread_t
*th
)
484 thread_debug("ubf_select_each (%p)\n", (void *)th
->thread_id
);
486 pthread_kill(th
->thread_id
, SIGVTALRM
);
491 ubf_select(void *ptr
)
493 rb_thread_t
*th
= (rb_thread_t
*)ptr
;
494 add_signal_thread_list(th
);
502 native_sleep(rb_thread_t
*th
, struct timeval
*tv
)
508 gettimeofday(&tvn
, NULL
);
509 ts
.tv_sec
= tvn
.tv_sec
+ tv
->tv_sec
;
510 ts
.tv_nsec
= (tvn
.tv_usec
+ tv
->tv_usec
) * 1000;
511 if (ts
.tv_nsec
>= 1000000000){
513 ts
.tv_nsec
-= 1000000000;
517 thread_debug("native_sleep %ld\n", tv
? tv
->tv_sec
: -1);
520 pthread_mutex_lock(&th
->interrupt_lock
);
521 th
->unblock
.func
= ubf_pthread_cond_signal
;
522 th
->unblock
.arg
= th
;
524 if (RUBY_VM_INTERRUPTED(th
)) {
525 /* interrupted. return immediate */
526 thread_debug("native_sleep: interrupted before sleep\n");
529 if (tv
== 0 || ts
.tv_sec
< tvn
.tv_sec
/* overflow */ ) {
531 thread_debug("native_sleep: pthread_cond_wait start\n");
532 r
= pthread_cond_wait(&th
->native_thread_data
.sleep_cond
,
533 &th
->interrupt_lock
);
534 if (r
) rb_bug("pthread_cond_wait: %d", r
);
535 thread_debug("native_sleep: pthread_cond_wait end\n");
539 thread_debug("native_sleep: pthread_cond_timedwait start (%ld, %ld)\n",
540 (unsigned long)ts
.tv_sec
, ts
.tv_nsec
);
541 r
= pthread_cond_timedwait(&th
->native_thread_data
.sleep_cond
,
542 &th
->interrupt_lock
, &ts
);
543 if (r
&& r
!= ETIMEDOUT
) rb_bug("pthread_cond_timedwait: %d", r
);
545 thread_debug("native_sleep: pthread_cond_timedwait end (%d)\n", r
);
548 th
->unblock
.func
= 0;
551 pthread_mutex_unlock(&th
->interrupt_lock
);
555 thread_debug("native_sleep done\n");
558 struct signal_thread_list
{
560 struct signal_thread_list
*prev
;
561 struct signal_thread_list
*next
;
565 static struct signal_thread_list signal_thread_list_anchor
= {
570 #define FGLOCK(lock, body) do { \
571 native_mutex_lock(lock); \
575 native_mutex_unlock(lock); \
578 #if 0 /* for debug */
580 print_signal_list(char *str
)
582 struct signal_thread_list
*list
=
583 signal_thread_list_anchor
.next
;
584 thread_debug("list (%s)> ", str
);
586 thread_debug("%p (%p), ", list
->th
, list
->th
->thread_id
);
595 add_signal_thread_list(rb_thread_t
*th
)
597 if (!th
->native_thread_data
.signal_thread_list
) {
598 FGLOCK(&signal_thread_list_lock
, {
599 struct signal_thread_list
*list
=
600 malloc(sizeof(struct signal_thread_list
));
603 fprintf(stderr
, "[FATAL] failed to allocate memory\n");
609 list
->prev
= &signal_thread_list_anchor
;
610 list
->next
= signal_thread_list_anchor
.next
;
612 list
->next
->prev
= list
;
614 signal_thread_list_anchor
.next
= list
;
615 th
->native_thread_data
.signal_thread_list
= list
;
622 remove_signal_thread_list(rb_thread_t
*th
)
624 if (th
->native_thread_data
.signal_thread_list
) {
625 FGLOCK(&signal_thread_list_lock
, {
626 struct signal_thread_list
*list
=
627 (struct signal_thread_list
*)
628 th
->native_thread_data
.signal_thread_list
;
630 list
->prev
->next
= list
->next
;
632 list
->next
->prev
= list
->prev
;
634 th
->native_thread_data
.signal_thread_list
= 0;
644 static pthread_t timer_thread_id
;
647 thread_timer(void *dummy
)
649 while (system_working
) {
650 #ifdef HAVE_NANOSLEEP
651 struct timespec req
, rem
;
653 req
.tv_nsec
= 10 * 1000 * 1000; /* 10 ms */
654 nanosleep(&req
, &rem
);
658 tv
.tv_usec
= 10000; /* 10 ms */
659 select(0, NULL
, NULL
, NULL
, &tv
);
662 if (signal_thread_list_anchor
.next
) {
663 FGLOCK(&signal_thread_list_lock
, {
664 struct signal_thread_list
*list
;
665 list
= signal_thread_list_anchor
.next
;
667 ubf_select_each(list
->th
);
673 timer_thread_function(dummy
);
679 rb_thread_create_timer_thread(void)
681 rb_enable_interrupt();
683 if (!timer_thread_id
) {
687 pthread_attr_init(&attr
);
688 #ifdef PTHREAD_STACK_MIN
689 pthread_attr_setstacksize(&attr
,
690 PTHREAD_STACK_MIN
+ (THREAD_DEBUG
? BUFSIZ
: 0));
692 err
= pthread_create(&timer_thread_id
, &attr
, thread_timer
, GET_VM());
694 rb_bug("rb_thread_create_timer_thread: return non-zero (%d)", err
);
697 rb_disable_interrupt(); /* only timer thread recieve signal */
700 #endif /* THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION */