2 /**********************************************************************
8 Copyright (C) 2004-2007 Koichi Sasada
10 **********************************************************************/
12 #ifdef THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION
16 #define TIME_QUANTUM_USEC (10 * 1000)
17 #define RB_CONDATTR_CLOCK_MONOTONIC 1 /* no effect */
21 #define native_thread_yield() Sleep(0)
22 #define unregister_ubf_list(th)
23 #define ubf_wakeup_all_threads() do {} while (0)
24 #define ubf_threads_empty() (1)
25 #define ubf_timer_disarm() do {} while (0)
26 #define ubf_list_atfork() do {} while (0)
28 static volatile DWORD ruby_native_thread_key
= TLS_OUT_OF_INDEXES
;
30 static int w32_wait_events(HANDLE
*events
, int count
, DWORD timeout
, rb_thread_t
*th
);
32 RBIMPL_ATTR_NORETURN()
34 w32_error(const char *func
)
37 DWORD err
= GetLastError();
38 if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
|
39 FORMAT_MESSAGE_FROM_SYSTEM
|
40 FORMAT_MESSAGE_IGNORE_INSERTS
,
43 MAKELANGID(LANG_ENGLISH
, SUBLANG_ENGLISH_US
),
44 (LPTSTR
) & lpMsgBuf
, 0, NULL
) == 0)
45 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
|
46 FORMAT_MESSAGE_FROM_SYSTEM
|
47 FORMAT_MESSAGE_IGNORE_INSERTS
,
50 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
),
51 (LPTSTR
) & lpMsgBuf
, 0, NULL
);
52 rb_bug("%s: %s", func
, (char*)lpMsgBuf
);
57 w32_mutex_lock(HANDLE lock
, bool try)
61 thread_debug("rb_native_mutex_lock: %p\n", lock
);
62 result
= w32_wait_events(&lock
, 1, try ? 0 : INFINITE
, 0);
65 /* get mutex object */
66 thread_debug("acquire mutex: %p\n", lock
);
68 case WAIT_OBJECT_0
+ 1:
71 thread_debug("acquire mutex interrupted: %p\n", lock
);
74 thread_debug("timeout mutex: %p\n", lock
);
77 rb_bug("win32_mutex_lock: WAIT_ABANDONED");
80 rb_bug("win32_mutex_lock: unknown result (%ld)", result
);
88 w32_mutex_create(void)
90 HANDLE lock
= CreateMutex(NULL
, FALSE
, NULL
);
92 w32_error("rb_native_mutex_initialize");
100 gvl_acquire(rb_global_vm_lock_t
*gvl
, rb_thread_t
*th
)
102 w32_mutex_lock(gvl
->lock
, false);
103 if (GVL_DEBUG
) fprintf(stderr
, "gvl acquire (%p): acquire\n", th
);
107 gvl_release(rb_global_vm_lock_t
*gvl
)
109 ReleaseMutex(gvl
->lock
);
113 gvl_yield(rb_global_vm_lock_t
*gvl
, rb_thread_t
*th
)
116 native_thread_yield();
117 gvl_acquire(gvl
, th
);
121 rb_gvl_init(rb_global_vm_lock_t
*gvl
)
123 if (GVL_DEBUG
) fprintf(stderr
, "gvl init\n");
124 gvl
->lock
= w32_mutex_create();
128 gvl_destroy(rb_global_vm_lock_t
*gvl
)
130 if (GVL_DEBUG
) fprintf(stderr
, "gvl destroy\n");
131 CloseHandle(gvl
->lock
);
135 ruby_thread_from_native(void)
137 return TlsGetValue(ruby_native_thread_key
);
141 ruby_thread_set_native(rb_thread_t
*th
)
144 rb_ractor_set_current_ec(th
->ractor
, th
->ec
);
146 return TlsSetValue(ruby_native_thread_key
, th
);
150 Init_native_thread(rb_thread_t
*th
)
152 if ((ruby_current_ec_key
= TlsAlloc()) == TLS_OUT_OF_INDEXES
) {
153 rb_bug("TlsAlloc() for ruby_current_ec_key fails");
155 if ((ruby_native_thread_key
= TlsAlloc()) == TLS_OUT_OF_INDEXES
) {
156 rb_bug("TlsAlloc() for ruby_native_thread_key fails");
158 ruby_thread_set_native(th
);
159 DuplicateHandle(GetCurrentProcess(),
162 &th
->thread_id
, 0, FALSE
, DUPLICATE_SAME_ACCESS
);
164 th
->native_thread_data
.interrupt_event
= CreateEvent(0, TRUE
, FALSE
, 0);
166 thread_debug("initial thread (th: %p, thid: %p, event: %p)\n",
167 th
, GET_THREAD()->thread_id
,
168 th
->native_thread_data
.interrupt_event
);
172 w32_wait_events(HANDLE
*events
, int count
, DWORD timeout
, rb_thread_t
*th
)
174 HANDLE
*targets
= events
;
176 const int initcount
= count
;
179 thread_debug(" w32_wait_events events:%p, count:%d, timeout:%ld, th:%p\n",
180 events
, count
, timeout
, th
);
181 if (th
&& (intr
= th
->native_thread_data
.interrupt_event
)) {
182 if (ResetEvent(intr
) && (!RUBY_VM_INTERRUPTED(th
->ec
) || SetEvent(intr
))) {
183 targets
= ALLOCA_N(HANDLE
, count
+ 1);
184 memcpy(targets
, events
, sizeof(HANDLE
) * count
);
186 targets
[count
++] = intr
;
187 thread_debug(" * handle: %p (count: %d, intr)\n", intr
, count
);
189 else if (intr
== th
->native_thread_data
.interrupt_event
) {
190 w32_error("w32_wait_events");
194 thread_debug(" WaitForMultipleObjects start (count: %d)\n", count
);
195 ret
= WaitForMultipleObjects(count
, targets
, FALSE
, timeout
);
196 thread_debug(" WaitForMultipleObjects end (ret: %lu)\n", ret
);
198 if (ret
== (DWORD
)(WAIT_OBJECT_0
+ initcount
) && th
) {
201 if (ret
== WAIT_FAILED
&& THREAD_DEBUG
) {
204 for (i
= 0; i
< count
; i
++) {
205 thread_debug(" * error handle %d - %s\n", i
,
206 GetHandleInformation(targets
[i
], &dmy
) ? "OK" : "NG");
212 static void ubf_handle(void *ptr
);
213 #define ubf_select ubf_handle
216 rb_w32_wait_events_blocking(HANDLE
*events
, int num
, DWORD timeout
)
218 return w32_wait_events(events
, num
, timeout
, ruby_thread_from_native());
222 rb_w32_wait_events(HANDLE
*events
, int num
, DWORD timeout
)
225 rb_thread_t
*th
= GET_THREAD();
227 BLOCKING_REGION(th
, ret
= rb_w32_wait_events_blocking(events
, num
, timeout
),
228 ubf_handle
, ruby_thread_from_native(), FALSE
);
233 w32_close_handle(HANDLE handle
)
235 if (CloseHandle(handle
) == 0) {
236 w32_error("w32_close_handle");
241 w32_resume_thread(HANDLE handle
)
243 if (ResumeThread(handle
) == (DWORD
)-1) {
244 w32_error("w32_resume_thread");
249 #define HAVE__BEGINTHREADEX 1
251 #undef HAVE__BEGINTHREADEX
254 #ifdef HAVE__BEGINTHREADEX
255 #define start_thread (HANDLE)_beginthreadex
256 #define thread_errno errno
257 typedef unsigned long (__stdcall
*w32_thread_start_func
)(void*);
259 #define start_thread CreateThread
260 #define thread_errno rb_w32_map_errno(GetLastError())
261 typedef LPTHREAD_START_ROUTINE w32_thread_start_func
;
265 w32_create_thread(DWORD stack_size
, w32_thread_start_func func
, void *val
)
267 return start_thread(0, stack_size
, func
, val
, CREATE_SUSPENDED
| STACK_SIZE_PARAM_IS_A_RESERVATION
, 0);
271 rb_w32_sleep(unsigned long msec
)
273 return w32_wait_events(0, 0, msec
, ruby_thread_from_native());
277 rb_w32_Sleep(unsigned long msec
)
280 rb_thread_t
*th
= GET_THREAD();
282 BLOCKING_REGION(th
, ret
= rb_w32_sleep(msec
),
283 ubf_handle
, ruby_thread_from_native(), FALSE
);
288 hrtime2msec(rb_hrtime_t hrt
)
290 return (DWORD
)hrt
/ (DWORD
)RB_HRTIME_PER_MSEC
;
294 native_sleep(rb_thread_t
*th
, rb_hrtime_t
*rel
)
296 const volatile DWORD msec
= rel
? hrtime2msec(*rel
) : INFINITE
;
298 GVL_UNLOCK_BEGIN(th
);
302 rb_native_mutex_lock(&th
->interrupt_lock
);
303 th
->unblock
.func
= ubf_handle
;
304 th
->unblock
.arg
= th
;
305 rb_native_mutex_unlock(&th
->interrupt_lock
);
307 if (RUBY_VM_INTERRUPTED(th
->ec
)) {
308 /* interrupted. return immediate */
311 thread_debug("native_sleep start (%lu)\n", msec
);
312 ret
= w32_wait_events(0, 0, msec
, th
);
313 thread_debug("native_sleep done (%lu)\n", ret
);
316 rb_native_mutex_lock(&th
->interrupt_lock
);
317 th
->unblock
.func
= 0;
319 rb_native_mutex_unlock(&th
->interrupt_lock
);
325 rb_native_mutex_lock(rb_nativethread_lock_t
*lock
)
327 #ifdef USE_WIN32_MUTEX
328 w32_mutex_lock(lock
->mutex
, false);
330 EnterCriticalSection(&lock
->crit
);
335 rb_native_mutex_trylock(rb_nativethread_lock_t
*lock
)
337 #ifdef USE_WIN32_MUTEX
338 return w32_mutex_lock(lock
->mutex
, true);
340 return TryEnterCriticalSection(&lock
->crit
) == 0 ? EBUSY
: 0;
345 rb_native_mutex_unlock(rb_nativethread_lock_t
*lock
)
347 #ifdef USE_WIN32_MUTEX
348 thread_debug("release mutex: %p\n", lock
->mutex
);
349 ReleaseMutex(lock
->mutex
);
351 LeaveCriticalSection(&lock
->crit
);
356 rb_native_mutex_initialize(rb_nativethread_lock_t
*lock
)
358 #ifdef USE_WIN32_MUTEX
359 lock
->mutex
= w32_mutex_create();
360 /* thread_debug("initialize mutex: %p\n", lock->mutex); */
362 InitializeCriticalSection(&lock
->crit
);
367 rb_native_mutex_destroy(rb_nativethread_lock_t
*lock
)
369 #ifdef USE_WIN32_MUTEX
370 w32_close_handle(lock
->mutex
);
372 DeleteCriticalSection(&lock
->crit
);
376 struct cond_event_entry
{
377 struct cond_event_entry
* next
;
378 struct cond_event_entry
* prev
;
383 rb_native_cond_signal(rb_nativethread_cond_t
*cond
)
385 /* cond is guarded by mutex */
386 struct cond_event_entry
*e
= cond
->next
;
387 struct cond_event_entry
*head
= (struct cond_event_entry
*)cond
;
390 struct cond_event_entry
*next
= e
->next
;
391 struct cond_event_entry
*prev
= e
->prev
;
395 e
->next
= e
->prev
= e
;
402 rb_native_cond_broadcast(rb_nativethread_cond_t
*cond
)
404 /* cond is guarded by mutex */
405 struct cond_event_entry
*e
= cond
->next
;
406 struct cond_event_entry
*head
= (struct cond_event_entry
*)cond
;
409 struct cond_event_entry
*next
= e
->next
;
410 struct cond_event_entry
*prev
= e
->prev
;
416 e
->next
= e
->prev
= e
;
423 native_cond_timedwait_ms(rb_nativethread_cond_t
*cond
, rb_nativethread_lock_t
*mutex
, unsigned long msec
)
426 struct cond_event_entry entry
;
427 struct cond_event_entry
*head
= (struct cond_event_entry
*)cond
;
429 entry
.event
= CreateEvent(0, FALSE
, FALSE
, 0);
431 /* cond is guarded by mutex */
433 entry
.prev
= head
->prev
;
434 head
->prev
->next
= &entry
;
437 rb_native_mutex_unlock(mutex
);
439 r
= WaitForSingleObject(entry
.event
, msec
);
440 if ((r
!= WAIT_OBJECT_0
) && (r
!= WAIT_TIMEOUT
)) {
441 rb_bug("rb_native_cond_wait: WaitForSingleObject returns %lu", r
);
444 rb_native_mutex_lock(mutex
);
446 entry
.prev
->next
= entry
.next
;
447 entry
.next
->prev
= entry
.prev
;
449 w32_close_handle(entry
.event
);
450 return (r
== WAIT_OBJECT_0
) ? 0 : ETIMEDOUT
;
454 rb_native_cond_wait(rb_nativethread_cond_t
*cond
, rb_nativethread_lock_t
*mutex
)
456 native_cond_timedwait_ms(cond
, mutex
, INFINITE
);
460 abs_timespec_to_timeout_ms(const struct timespec
*ts
)
465 gettimeofday(&now
, NULL
);
466 tv
.tv_sec
= ts
->tv_sec
;
467 tv
.tv_usec
= ts
->tv_nsec
/ 1000;
469 if (!rb_w32_time_subtract(&tv
, &now
))
472 return (tv
.tv_sec
* 1000) + (tv
.tv_usec
/ 1000);
476 native_cond_timedwait(rb_nativethread_cond_t
*cond
, rb_nativethread_lock_t
*mutex
, const struct timespec
*ts
)
478 unsigned long timeout_ms
;
480 timeout_ms
= abs_timespec_to_timeout_ms(ts
);
484 return native_cond_timedwait_ms(cond
, mutex
, timeout_ms
);
487 static struct timespec
native_cond_timeout(rb_nativethread_cond_t
*cond
, struct timespec timeout_rel
);
490 rb_native_cond_timedwait(rb_nativethread_cond_t
*cond
, rb_nativethread_lock_t
*mutex
, unsigned long msec
)
492 struct timespec rel
= {
493 .tv_sec
= msec
/ 1000,
494 .tv_nsec
= (msec
% 1000) * 1000 * 1000,
496 struct timespec ts
= native_cond_timeout(cond
, rel
);
497 native_cond_timedwait(cond
, mutex
, &ts
);
500 static struct timespec
501 native_cond_timeout(rb_nativethread_cond_t
*cond
, struct timespec timeout_rel
)
505 struct timespec timeout
;
508 ret
= gettimeofday(&tv
, 0);
511 now
.tv_sec
= tv
.tv_sec
;
512 now
.tv_nsec
= tv
.tv_usec
* 1000;
514 timeout
.tv_sec
= now
.tv_sec
;
515 timeout
.tv_nsec
= now
.tv_nsec
;
516 timeout
.tv_sec
+= timeout_rel
.tv_sec
;
517 timeout
.tv_nsec
+= timeout_rel
.tv_nsec
;
519 if (timeout
.tv_nsec
>= 1000*1000*1000) {
521 timeout
.tv_nsec
-= 1000*1000*1000;
524 if (timeout
.tv_sec
< now
.tv_sec
)
525 timeout
.tv_sec
= TIMET_MAX
;
531 rb_native_cond_initialize(rb_nativethread_cond_t
*cond
)
533 cond
->next
= (struct cond_event_entry
*)cond
;
534 cond
->prev
= (struct cond_event_entry
*)cond
;
538 rb_native_cond_destroy(rb_nativethread_cond_t
*cond
)
544 ruby_init_stack(volatile VALUE
*addr
)
548 #define CHECK_ERR(expr) \
549 {if (!(expr)) {rb_bug("err: %lu - %s", GetLastError(), #expr);}}
551 COMPILER_WARNING_PUSH
552 #if defined(__GNUC__)
553 COMPILER_WARNING_IGNORED(-Wmaybe
-uninitialized
)
556 query_memory_basic_info(PMEMORY_BASIC_INFORMATION mi
)
558 return VirtualQuery(mi
, mi
, sizeof(*mi
));
563 native_thread_init_stack(rb_thread_t
*th
)
565 MEMORY_BASIC_INFORMATION mi
;
569 CHECK_ERR(query_memory_basic_info(&mi
));
570 base
= mi
.AllocationBase
;
571 end
= mi
.BaseAddress
;
572 end
+= mi
.RegionSize
;
575 if (space
> 1024*1024) space
= 1024*1024;
576 th
->ec
->machine
.stack_start
= (VALUE
*)end
- 1;
577 th
->ec
->machine
.stack_maxsize
= size
- space
;
580 #ifndef InterlockedExchangePointer
581 #define InterlockedExchangePointer(t, v) \
582 (void *)InterlockedExchange((long *)(t), (long)(v))
585 native_thread_destroy(rb_thread_t
*th
)
587 HANDLE intr
= InterlockedExchangePointer(&th
->native_thread_data
.interrupt_event
, 0);
588 thread_debug("close handle - intr: %p, thid: %p\n", intr
, th
->thread_id
);
589 w32_close_handle(intr
);
592 static unsigned long __stdcall
593 thread_start_func_1(void *th_ptr
)
595 rb_thread_t
*th
= th_ptr
;
596 volatile HANDLE thread_id
= th
->thread_id
;
598 native_thread_init_stack(th
);
599 th
->native_thread_data
.interrupt_event
= CreateEvent(0, TRUE
, FALSE
, 0);
602 thread_debug("thread created (th: %p, thid: %p, event: %p)\n", th
,
603 th
->thread_id
, th
->native_thread_data
.interrupt_event
);
605 thread_start_func_2(th
, th
->ec
->machine
.stack_start
);
607 w32_close_handle(thread_id
);
608 thread_debug("thread deleted (th: %p)\n", th
);
613 native_thread_create(rb_thread_t
*th
)
615 const size_t stack_size
= th
->vm
->default_params
.thread_machine_stack_size
+ th
->vm
->default_params
.thread_vm_stack_size
;
616 th
->thread_id
= w32_create_thread(stack_size
, thread_start_func_1
, th
);
618 if ((th
->thread_id
) == 0) {
622 w32_resume_thread(th
->thread_id
);
626 thread_debug("create: (th: %p, thid: %p, intr: %p), stack size: %"PRIuSIZE
"\n",
628 th
->native_thread_data
.interrupt_event
, stack_size
);
634 native_thread_join(HANDLE th
)
636 w32_wait_events(&th
, 1, INFINITE
, 0);
639 #if USE_NATIVE_THREAD_PRIORITY
642 native_thread_apply_priority(rb_thread_t
*th
)
644 int priority
= th
->priority
;
645 if (th
->priority
> 0) {
646 priority
= THREAD_PRIORITY_ABOVE_NORMAL
;
648 else if (th
->priority
< 0) {
649 priority
= THREAD_PRIORITY_BELOW_NORMAL
;
652 priority
= THREAD_PRIORITY_NORMAL
;
655 SetThreadPriority(th
->thread_id
, priority
);
658 #endif /* USE_NATIVE_THREAD_PRIORITY */
660 int rb_w32_select_with_thread(int, fd_set
*, fd_set
*, fd_set
*, struct timeval
*, void *); /* @internal */
663 native_fd_select(int n
, rb_fdset_t
*readfds
, rb_fdset_t
*writefds
, rb_fdset_t
*exceptfds
, struct timeval
*timeout
, rb_thread_t
*th
)
665 fd_set
*r
= NULL
, *w
= NULL
, *e
= NULL
;
667 rb_fd_resize(n
- 1, readfds
);
668 r
= rb_fd_ptr(readfds
);
671 rb_fd_resize(n
- 1, writefds
);
672 w
= rb_fd_ptr(writefds
);
675 rb_fd_resize(n
- 1, exceptfds
);
676 e
= rb_fd_ptr(exceptfds
);
678 return rb_w32_select_with_thread(n
, r
, w
, e
, timeout
, th
);
683 rb_w32_check_interrupt(rb_thread_t
*th
)
685 return w32_wait_events(0, 0, 0, th
);
689 ubf_handle(void *ptr
)
691 rb_thread_t
*th
= (rb_thread_t
*)ptr
;
692 thread_debug("ubf_handle: %p\n", th
);
694 if (!SetEvent(th
->native_thread_data
.interrupt_event
)) {
695 w32_error("ubf_handle");
699 int rb_w32_set_thread_description(HANDLE th
, const WCHAR
*name
);
700 int rb_w32_set_thread_description_str(HANDLE th
, VALUE name
);
701 #define native_set_another_thread_name rb_w32_set_thread_description_str
707 #define TIMER_THREAD_CREATED_P() (timer_thread.id != 0)
709 static unsigned long __stdcall
710 timer_thread_func(void *dummy
)
712 rb_vm_t
*vm
= GET_VM();
713 thread_debug("timer_thread\n");
714 rb_w32_set_thread_description(GetCurrentThread(), L
"ruby-timer-thread");
715 while (WaitForSingleObject(timer_thread
.lock
,
716 TIME_QUANTUM_USEC
/1000) == WAIT_TIMEOUT
) {
718 ruby_sigchld_handler(vm
); /* probably no-op */
719 rb_threadptr_check_signal(vm
->ractor
.main_thread
);
721 thread_debug("timer killed\n");
726 rb_thread_wakeup_timer_thread(int sig
)
732 rb_thread_start_unblock_thread(void)
734 return Qfalse
; /* no-op */
738 rb_thread_create_timer_thread(void)
740 if (timer_thread
.id
== 0) {
741 if (!timer_thread
.lock
) {
742 timer_thread
.lock
= CreateEvent(0, TRUE
, FALSE
, 0);
744 timer_thread
.id
= w32_create_thread(1024 + (THREAD_DEBUG
? BUFSIZ
: 0),
745 timer_thread_func
, 0);
746 w32_resume_thread(timer_thread
.id
);
751 native_stop_timer_thread(void)
753 int stopped
= --system_working
<= 0;
755 SetEvent(timer_thread
.lock
);
756 native_thread_join(timer_thread
.id
);
757 CloseHandle(timer_thread
.lock
);
758 timer_thread
.lock
= 0;
764 native_reset_timer_thread(void)
766 if (timer_thread
.id
) {
767 CloseHandle(timer_thread
.id
);
773 ruby_stack_overflowed_p(const rb_thread_t
*th
, const void *addr
)
775 return rb_ec_raised_p(th
->ec
, RAISED_STACKOVERFLOW
);
778 #if defined(__MINGW32__)
780 rb_w32_stack_overflow_handler(struct _EXCEPTION_POINTERS
*exception
)
782 if (exception
->ExceptionRecord
->ExceptionCode
== EXCEPTION_STACK_OVERFLOW
) {
783 rb_ec_raised_set(GET_EC(), RAISED_STACKOVERFLOW
);
786 return EXCEPTION_CONTINUE_SEARCH
;
790 #ifdef RUBY_ALLOCA_CHKSTK
792 ruby_alloca_chkstk(size_t len
, void *sp
)
794 if (ruby_stack_length(NULL
) * sizeof(VALUE
) >= len
) {
795 rb_execution_context_t
*ec
= GET_EC();
796 if (!rb_ec_raised_p(ec
, RAISED_STACKOVERFLOW
)) {
797 rb_ec_raised_set(ec
, RAISED_STACKOVERFLOW
);
798 rb_exc_raise(sysstack_error
);
804 rb_reserved_fd_p(int fd
)
810 rb_sigwait_fd_get(rb_thread_t
*th
)
812 return -1; /* TODO */
815 NORETURN(void rb_sigwait_fd_put(rb_thread_t
*, int));
817 rb_sigwait_fd_put(rb_thread_t
*th
, int fd
)
819 rb_bug("not implemented, should not be called");
822 NORETURN(void rb_sigwait_sleep(const rb_thread_t
*, int, const rb_hrtime_t
*));
824 rb_sigwait_sleep(const rb_thread_t
*th
, int fd
, const rb_hrtime_t
*rel
)
826 rb_bug("not implemented, should not be called");
830 rb_nativethread_self(void)
832 return GetCurrentThread();
836 native_set_thread_name(rb_thread_t
*th
)
841 native_thread_native_thread_id(rb_thread_t
*th
)
843 DWORD tid
= GetThreadId(th
->thread_id
);
844 if (tid
== 0) rb_sys_fail("GetThreadId");
845 return ULONG2NUM(tid
);
847 #define USE_NATIVE_THREAD_NATIVE_THREAD_ID 1
850 static unsigned long __stdcall
851 mjit_worker(void *arg
)
853 void (*worker_func
)(void) = arg
;
854 rb_w32_set_thread_description(GetCurrentThread(), L
"ruby-mjitworker");
859 /* Launch MJIT thread. Returns FALSE if it fails to create thread. */
861 rb_thread_create_mjit_thread(void (*worker_func
)(void))
863 size_t stack_size
= 4 * 1024; /* 4KB is the minimum commit size */
864 HANDLE thread_id
= w32_create_thread(stack_size
, mjit_worker
, worker_func
);
865 if (thread_id
== 0) {
869 w32_resume_thread(thread_id
);
874 #endif /* THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION */