• 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 

Generated on Wed Sep 8 2010 09:56:18 for Ruby by  doxygen 1.7.1