Ruby 1.9.2p136(2010-12-25revision30365)

- Main Page
- Modules
- Data Structures
- Files

- File List
- Globals

# thread_win32.c

Go to the documentation of this file.

    00001 /* -*-c-*- */
    00002 /**********************************************************************
    00003
    00004   thread_win32.c -
    00005
    00006   $Author: mame $
    00007
    00008   Copyright (C) 2004-2007 Koichi Sasada
    00009
    00010 **********************************************************************/
    00011
    00012 #ifdef THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION
    00013
    00014 #include <process.h>
    00015
    00016 #define WIN32_WAIT_TIMEOUT 10   /* 10 ms */
    00017 #undef Sleep
    00018
    00019 #define native_thread_yield() Sleep(0)
    00020 #define remove_signal_thread_list(th)
    00021
    00022 static volatile DWORD ruby_native_thread_key = TLS_OUT_OF_INDEXES;
    00023
    00024 static int native_mutex_lock(rb_thread_lock_t *);
    00025 static int native_mutex_unlock(rb_thread_lock_t *);
    00026 static int native_mutex_trylock(rb_thread_lock_t *);
    00027 static void native_mutex_initialize(rb_thread_lock_t *);
    00028
    00029 static void native_cond_signal(rb_thread_cond_t *cond);
    00030 static void native_cond_broadcast(rb_thread_cond_t *cond);
    00031 static void native_cond_wait(rb_thread_cond_t *cond, rb_thread_lock_t *mutex);
    00032 static void native_cond_initialize(rb_thread_cond_t *cond);
    00033 static void native_cond_destroy(rb_thread_cond_t *cond);
    00034
    00035 static rb_thread_t *
    00036 ruby_thread_from_native(void)
    00037 {
    00038     return TlsGetValue(ruby_native_thread_key);
    00039 }
    00040
    00041 static int
    00042 ruby_thread_set_native(rb_thread_t *th)
    00043 {
    00044     return TlsSetValue(ruby_native_thread_key, th);
    00045 }
    00046
    00047 void
    00048 Init_native_thread(void)
    00049 {
    00050     rb_thread_t *th = GET_THREAD();
    00051
    00052     ruby_native_thread_key = TlsAlloc();
    00053     ruby_thread_set_native(th);
    00054     DuplicateHandle(GetCurrentProcess(),
    00055                     GetCurrentThread(),
    00056                     GetCurrentProcess(),
    00057                     &th->thread_id, 0, FALSE, DUPLICATE_SAME_ACCESS);
    00058
    00059     th->native_thread_data.interrupt_event = CreateEvent(0, TRUE, FALSE, 0);
    00060
    00061     thread_debug("initial thread (th: %p, thid: %p, event: %p)\n",
    00062                  th, GET_THREAD()->thread_id,
    00063                  th->native_thread_data.interrupt_event);
    00064 }
    00065
    00066 static void
    00067 w32_error(const char *func)
    00068 {
    00069     LPVOID lpMsgBuf;
    00070     FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
    00071                   FORMAT_MESSAGE_FROM_SYSTEM |
    00072                   FORMAT_MESSAGE_IGNORE_INSERTS,
    00073                   NULL,
    00074                   GetLastError(),
    00075                   MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
    00076                   (LPTSTR) & lpMsgBuf, 0, NULL);
    00077     rb_bug("%s: %s", func, (char*)lpMsgBuf);
    00078 }
    00079
    00080 static void
    00081 w32_set_event(HANDLE handle)
    00082 {
    00083     if (SetEvent(handle) == 0) {
    00084         w32_error("w32_set_event");
    00085     }
    00086 }
    00087
    00088 static void
    00089 w32_reset_event(HANDLE handle)
    00090 {
    00091     if (ResetEvent(handle) == 0) {
    00092         w32_error("w32_reset_event");
    00093     }
    00094 }
    00095
    00096 static int
    00097 w32_wait_events(HANDLE *events, int count, DWORD timeout, rb_thread_t *th)
    00098 {
    00099     HANDLE *targets = events;
    00100     HANDLE intr;
    00101     DWORD ret;
    00102
    00103     thread_debug("  w32_wait_events events:%p, count:%d, timeout:%ld, th:%p\n",
    00104                  events, count, timeout, th);
    00105     if (th && (intr = th->native_thread_data.interrupt_event)) {
    00106         native_mutex_lock(&th->vm->global_vm_lock);
    00107         if (intr == th->native_thread_data.interrupt_event) {
    00108             w32_reset_event(intr);
    00109             if (RUBY_VM_INTERRUPTED(th)) {
    00110                 w32_set_event(intr);
    00111             }
    00112
    00113             targets = ALLOCA_N(HANDLE, count + 1);
    00114             memcpy(targets, events, sizeof(HANDLE) * count);
    00115
    00116             targets[count++] = intr;
    00117             thread_debug("  * handle: %p (count: %d, intr)\n", intr, count);
    00118         }
    00119         native_mutex_unlock(&th->vm->global_vm_lock);
    00120     }
    00121
    00122     thread_debug("  WaitForMultipleObjects start (count: %d)\n", count);
    00123     ret = WaitForMultipleObjects(count, targets, FALSE, timeout);
    00124     thread_debug("  WaitForMultipleObjects end (ret: %lu)\n", ret);
    00125
    00126     if (ret == WAIT_OBJECT_0 + count - 1 && th) {
    00127         errno = EINTR;
    00128     }
    00129     if (ret == -1 && THREAD_DEBUG) {
    00130         int i;
    00131         DWORD dmy;
    00132         for (i = 0; i < count; i++) {
    00133             thread_debug("  * error handle %d - %s\n", i,
    00134                          GetHandleInformation(targets[i], &dmy) ? "OK" : "NG");
    00135         }
    00136     }
    00137     return ret;
    00138 }
    00139
    00140 static void ubf_handle(void *ptr);
    00141 #define ubf_select ubf_handle
    00142
    00143 int
    00144 rb_w32_wait_events_blocking(HANDLE *events, int num, DWORD timeout)
    00145 {
    00146     return w32_wait_events(events, num, timeout, GET_THREAD());
    00147 }
    00148
    00149 int
    00150 rb_w32_wait_events(HANDLE *events, int num, DWORD timeout)
    00151 {
    00152     int ret;
    00153
    00154     BLOCKING_REGION(ret = rb_w32_wait_events_blocking(events, num, timeout),
    00155                     ubf_handle, GET_THREAD());
    00156     return ret;
    00157 }
    00158
    00159 static void
    00160 w32_close_handle(HANDLE handle)
    00161 {
    00162     if (CloseHandle(handle) == 0) {
    00163         w32_error("w32_close_handle");
    00164     }
    00165 }
    00166
    00167 static void
    00168 w32_resume_thread(HANDLE handle)
    00169 {
    00170     if (ResumeThread(handle) == -1) {
    00171         w32_error("w32_resume_thread");
    00172     }
    00173 }
    00174
    00175 #ifdef _MSC_VER
    00176 #define HAVE__BEGINTHREADEX 1
    00177 #else
    00178 #undef HAVE__BEGINTHREADEX
    00179 #endif
    00180
    00181 #ifdef HAVE__BEGINTHREADEX
    00182 #define start_thread (HANDLE)_beginthreadex
    00183 #define thread_errno errno
    00184 typedef unsigned long (_stdcall *w32_thread_start_func)(void*);
    00185 #else
    00186 #define start_thread CreateThread
    00187 #define thread_errno rb_w32_map_errno(GetLastError())
    00188 typedef LPTHREAD_START_ROUTINE w32_thread_start_func;
    00189 #endif
    00190
    00191 static HANDLE
    00192 w32_create_thread(DWORD stack_size, w32_thread_start_func func, void *val)
    00193 {
    00194     return start_thread(0, stack_size, func, val, CREATE_SUSPENDED, 0);
    00195 }
    00196
    00197 int
    00198 rb_w32_sleep(unsigned long msec)
    00199 {
    00200     return w32_wait_events(0, 0, msec, GET_THREAD());
    00201 }
    00202
    00203 int WINAPI
    00204 rb_w32_Sleep(unsigned long msec)
    00205 {
    00206     int ret;
    00207
    00208     BLOCKING_REGION(ret = rb_w32_sleep(msec),
    00209                     ubf_handle, GET_THREAD());
    00210     return ret;
    00211 }
    00212
    00213 static void
    00214 native_sleep(rb_thread_t *th, struct timeval *tv)
    00215 {
    00216     DWORD msec;
    00217
    00218     if (tv) {
    00219         msec = tv->tv_sec * 1000 + tv->tv_usec / 1000;
    00220     }
    00221     else {
    00222         msec = INFINITE;
    00223     }
    00224
    00225     GVL_UNLOCK_BEGIN();
    00226     {
    00227         DWORD ret;
    00228
    00229         native_mutex_lock(&th->interrupt_lock);
    00230         th->unblock.func = ubf_handle;
    00231         th->unblock.arg = th;
    00232         native_mutex_unlock(&th->interrupt_lock);
    00233
    00234         if (RUBY_VM_INTERRUPTED(th)) {
    00235             /* interrupted.  return immediate */
    00236         }
    00237         else {
    00238             thread_debug("native_sleep start (%lu)\n", msec);
    00239             ret = w32_wait_events(0, 0, msec, th);
    00240             thread_debug("native_sleep done (%lu)\n", ret);
    00241         }
    00242
    00243         native_mutex_lock(&th->interrupt_lock);
    00244         th->unblock.func = 0;
    00245         th->unblock.arg = 0;
    00246         native_mutex_unlock(&th->interrupt_lock);
    00247     }
    00248     GVL_UNLOCK_END();
    00249 }
    00250
    00251 static int
    00252 native_mutex_lock(rb_thread_lock_t *lock)
    00253 {
    00254 #if USE_WIN32_MUTEX
    00255     DWORD result;
    00256     while (1) {
    00257         thread_debug("native_mutex_lock: %p\n", *lock);
    00258         result = w32_wait_events(&*lock, 1, INFINITE, 0);
    00259         switch (result) {
    00260           case WAIT_OBJECT_0:
    00261             /* get mutex object */
    00262             thread_debug("acquire mutex: %p\n", *lock);
    00263             return 0;
    00264           case WAIT_OBJECT_0 + 1:
    00265             /* interrupt */
    00266             errno = EINTR;
    00267             thread_debug("acquire mutex interrupted: %p\n", *lock);
    00268             return 0;
    00269           case WAIT_TIMEOUT:
    00270             thread_debug("timeout mutex: %p\n", *lock);
    00271             break;
    00272           case WAIT_ABANDONED:
    00273             rb_bug("win32_mutex_lock: WAIT_ABANDONED");
    00274             break;
    00275           default:
    00276             rb_bug("win32_mutex_lock: unknown result (%d)", result);
    00277             break;
    00278         }
    00279     }
    00280     return 0;
    00281 #else
    00282     EnterCriticalSection(lock);
    00283     return 0;
    00284 #endif
    00285 }
    00286
    00287 static int
    00288 native_mutex_unlock(rb_thread_lock_t *lock)
    00289 {
    00290 #if USE_WIN32_MUTEX
    00291     thread_debug("release mutex: %p\n", *lock);
    00292     return ReleaseMutex(*lock);
    00293 #else
    00294     LeaveCriticalSection(lock);
    00295     return 0;
    00296 #endif
    00297 }
    00298
    00299 static int
    00300 native_mutex_trylock(rb_thread_lock_t *lock)
    00301 {
    00302 #if USE_WIN32_MUTEX
    00303     int result;
    00304     thread_debug("native_mutex_trylock: %p\n", *lock);
    00305     result = w32_wait_events(&*lock, 1, 1, 0);
    00306     thread_debug("native_mutex_trylock result: %d\n", result);
    00307     switch (result) {
    00308       case WAIT_OBJECT_0:
    00309         return 0;
    00310       case WAIT_TIMEOUT:
    00311         return EBUSY;
    00312     }
    00313     return EINVAL;
    00314 #else
    00315     return TryEnterCriticalSection(lock) == 0;
    00316 #endif
    00317 }
    00318
    00319 static void
    00320 native_mutex_initialize(rb_thread_lock_t *lock)
    00321 {
    00322 #if USE_WIN32_MUTEX
    00323     *lock = CreateMutex(NULL, FALSE, NULL);
    00324     if (*lock == NULL) {
    00325         w32_error("native_mutex_initialize");
    00326     }
    00327     /* thread_debug("initialize mutex: %p\n", *lock); */
    00328 #else
    00329     InitializeCriticalSection(lock);
    00330 #endif
    00331 }
    00332
    00333 #define native_mutex_reinitialize_atfork(lock) (void)(lock)
    00334
    00335 static void
    00336 native_mutex_destroy(rb_thread_lock_t *lock)
    00337 {
    00338 #if USE_WIN32_MUTEX
    00339     w32_close_handle(lock);
    00340 #else
    00341     DeleteCriticalSection(lock);
    00342 #endif
    00343 }
    00344
    00345 struct cond_event_entry {
    00346     struct cond_event_entry* next;
    00347     HANDLE event;
    00348 };
    00349
    00350 struct rb_thread_cond_struct {
    00351     struct cond_event_entry *next;
    00352     struct cond_event_entry *last;
    00353 };
    00354
    00355 static void
    00356 native_cond_signal(rb_thread_cond_t *cond)
    00357 {
    00358     /* cond is guarded by mutex */
    00359     struct cond_event_entry *e = cond->next;
    00360
    00361     if (e) {
    00362         cond->next = e->next;
    00363         SetEvent(e->event);
    00364     }
    00365     else {
    00366         rb_bug("native_cond_signal: no pending threads");
    00367     }
    00368 }
    00369
    00370 static void
    00371 native_cond_broadcast(rb_thread_cond_t *cond)
    00372 {
    00373     /* cond is guarded by mutex */
    00374     struct cond_event_entry *e = cond->next;
    00375     cond->next = 0;
    00376
    00377     while (e) {
    00378         SetEvent(e->event);
    00379         e = e->next;
    00380     }
    00381 }
    00382
    00383 static void
    00384 native_cond_wait(rb_thread_cond_t *cond, rb_thread_lock_t *mutex)
    00385 {
    00386     DWORD r;
    00387     struct cond_event_entry entry;
    00388
    00389     entry.next = 0;
    00390     entry.event = CreateEvent(0, FALSE, FALSE, 0);
    00391
    00392     /* cond is guarded by mutex */
    00393     if (cond->next) {
    00394         cond->last->next = &entry;
    00395         cond->last = &entry;
    00396     }
    00397     else {
    00398         cond->next = &entry;
    00399         cond->last = &entry;
    00400     }
    00401
    00402     native_mutex_unlock(mutex);
    00403     {
    00404         r = WaitForSingleObject(entry.event, INFINITE);
    00405         if (r != WAIT_OBJECT_0) {
    00406             rb_bug("native_cond_wait: WaitForSingleObject returns %lu", r);
    00407         }
    00408     }
    00409     native_mutex_lock(mutex);
    00410
    00411     w32_close_handle(entry.event);
    00412 }
    00413
    00414 static void
    00415 native_cond_initialize(rb_thread_cond_t *cond)
    00416 {
    00417     cond->next = 0;
    00418     cond->last = 0;
    00419 }
    00420
    00421 static void
    00422 native_cond_destroy(rb_thread_cond_t *cond)
    00423 {
    00424     /* */
    00425 }
    00426
    00427 void
    00428 ruby_init_stack(volatile VALUE *addr)
    00429 {
    00430 }
    00431
    00432 #define CHECK_ERR(expr) \
    00433     {if (!(expr)) {rb_bug("err: %lu - %s", GetLastError(), #expr);}}
    00434
    00435 static void
    00436 native_thread_init_stack(rb_thread_t *th)
    00437 {
    00438     MEMORY_BASIC_INFORMATION mi;
    00439     char *base, *end;
    00440     DWORD size, space;
    00441
    00442     CHECK_ERR(VirtualQuery(&mi, &mi, sizeof(mi)));
    00443     base = mi.AllocationBase;
    00444     end = mi.BaseAddress;
    00445     end += mi.RegionSize;
    00446     size = end - base;
    00447     space = size / 5;
    00448     if (space > 1024*1024) space = 1024*1024;
    00449     th->machine_stack_start = (VALUE *)end - 1;
    00450     th->machine_stack_maxsize = size - space;
    00451 }
    00452
    00453 #ifndef InterlockedExchangePointer
    00454 #define InterlockedExchangePointer(t, v) \
    00455     (void *)InterlockedExchange((long *)(t), (long)(v))
    00456 #endif
    00457 static void
    00458 native_thread_destroy(rb_thread_t *th)
    00459 {
    00460     HANDLE intr = InterlockedExchangePointer(&th->native_thread_data.interrupt_event, 0);
    00461     native_mutex_destroy(&th->interrupt_lock);
    00462     thread_debug("close handle - intr: %p, thid: %p\n", intr, th->thread_id);
    00463     w32_close_handle(intr);
    00464 }
    00465
    00466 static unsigned long _stdcall
    00467 thread_start_func_1(void *th_ptr)
    00468 {
    00469     rb_thread_t *th = th_ptr;
    00470     volatile HANDLE thread_id = th->thread_id;
    00471
    00472     native_thread_init_stack(th);
    00473     th->native_thread_data.interrupt_event = CreateEvent(0, TRUE, FALSE, 0);
    00474
    00475     /* run */
    00476     thread_debug("thread created (th: %p, thid: %p, event: %p)\n", th,
    00477                  th->thread_id, th->native_thread_data.interrupt_event);
    00478
    00479     thread_start_func_2(th, th->machine_stack_start, rb_ia64_bsp());
    00480
    00481     w32_close_handle(thread_id);
    00482     thread_debug("thread deleted (th: %p)\n", th);
    00483     return 0;
    00484 }
    00485
    00486 static int
    00487 native_thread_create(rb_thread_t *th)
    00488 {
    00489     size_t stack_size = 4 * 1024; /* 4KB */
    00490     th->thread_id = w32_create_thread(stack_size, thread_start_func_1, th);
    00491
    00492     if ((th->thread_id) == 0) {
    00493         return thread_errno;
    00494     }
    00495
    00496     w32_resume_thread(th->thread_id);
    00497
    00498     if (THREAD_DEBUG) {
    00499         Sleep(0);
    00500         thread_debug("create: (th: %p, thid: %p, intr: %p), stack size: %d\n",
    00501                      th, th->thread_id,
    00502                      th->native_thread_data.interrupt_event, stack_size);
    00503     }
    00504     return 0;
    00505 }
    00506
    00507 static void
    00508 native_thread_join(HANDLE th)
    00509 {
    00510     w32_wait_events(&th, 1, INFINITE, 0);
    00511 }
    00512
    00513 #if USE_NATIVE_THREAD_PRIORITY
    00514
    00515 static void
    00516 native_thread_apply_priority(rb_thread_t *th)
    00517 {
    00518     int priority = th->priority;
    00519     if (th->priority > 0) {
    00520         priority = THREAD_PRIORITY_ABOVE_NORMAL;
    00521     }
    00522     else if (th->priority < 0) {
    00523         priority = THREAD_PRIORITY_BELOW_NORMAL;
    00524     }
    00525     else {
    00526         priority = THREAD_PRIORITY_NORMAL;
    00527     }
    00528
    00529     SetThreadPriority(th->thread_id, priority);
    00530 }
    00531
    00532 #endif /* USE_NATIVE_THREAD_PRIORITY */
    00533
    00534 static void
    00535 ubf_handle(void *ptr)
    00536 {
    00537     rb_thread_t *th = (rb_thread_t *)ptr;
    00538     thread_debug("ubf_handle: %p\n", th);
    00539
    00540     w32_set_event(th->native_thread_data.interrupt_event);
    00541 }
    00542
    00543 static HANDLE timer_thread_id = 0;
    00544 static HANDLE timer_thread_lock;
    00545
    00546 static unsigned long _stdcall
    00547 timer_thread_func(void *dummy)
    00548 {
    00549     thread_debug("timer_thread\n");
    00550     while (WaitForSingleObject(timer_thread_lock, WIN32_WAIT_TIMEOUT) ==
    00551            WAIT_TIMEOUT) {
    00552         timer_thread_function(dummy);
    00553     }
    00554     thread_debug("timer killed\n");
    00555     return 0;
    00556 }
    00557
    00558 static void
    00559 rb_thread_create_timer_thread(void)
    00560 {
    00561     if (timer_thread_id == 0) {
    00562         if (!timer_thread_lock) {
    00563             timer_thread_lock = CreateEvent(0, TRUE, FALSE, 0);
    00564         }
    00565         timer_thread_id = w32_create_thread(1024 + (THREAD_DEBUG ? BUFSIZ : 0),
    00566                                             timer_thread_func, 0);
    00567         w32_resume_thread(timer_thread_id);
    00568     }
    00569 }
    00570
    00571 static int
    00572 native_stop_timer_thread(void)
    00573 {
    00574     int stopped = --system_working <= 0;
    00575     if (stopped) {
    00576         SetEvent(timer_thread_lock);
    00577         native_thread_join(timer_thread_id);
    00578         CloseHandle(timer_thread_lock);
    00579         timer_thread_lock = 0;
    00580     }
    00581     return stopped;
    00582 }
    00583
    00584 static void
    00585 native_reset_timer_thread(void)
    00586 {
    00587     if (timer_thread_id) {
    00588         CloseHandle(timer_thread_id);
    00589         timer_thread_id = 0;
    00590     }
    00591 }
    00592
    00593 #endif /* THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION */
    00594