static void native_thread_destroy(rb_thread_t *th) { HANDLE intr = th->native_thread_data.interrupt_event; thread_debug("close handle - intr: %p, thid: %p\n", intr, th->thread_id); th->native_thread_data.interrupt_event = 0; w32_close_handle(intr); }
static void ubf_handle(void *ptr) { rb_thread_t *th = (rb_thread_t *)ptr; thread_debug("ubf_handle: %p\n", th); w32_set_event(th->native_thread_data.interrupt_event); }
static void native_thread_destroy(rb_thread_t *th) { HANDLE intr = InterlockedExchangePointer(&th->native_thread_data.interrupt_event, 0); native_mutex_destroy(&th->interrupt_lock); thread_debug("close handle - intr: %p, thid: %p\n", intr, th->thread_id); w32_close_handle(intr); }
static void ubf_select_each(rb_thread_t *th) { thread_debug("ubf_select_each (%p)\n", (void *)th->thread_id); if (th) { pthread_kill(th->thread_id, SIGVTALRM); } }
static unsigned long _stdcall thread_start_func_1(void *th_ptr) { rb_thread_t *th = th_ptr; VALUE stack_start; volatile HANDLE thread_id = th->thread_id; th->native_thread_data.interrupt_event = CreateEvent(0, TRUE, FALSE, 0); /* run */ thread_debug("thread created (th: %p, thid: %p, event: %p)\n", th, th->thread_id, th->native_thread_data.interrupt_event); thread_start_func_2(th, &stack_start, 0); w32_close_handle(thread_id); thread_debug("thread deleted (th: %p)\n", th); return 0; }
static void ubf_handle(void *ptr) { typedef BOOL (WINAPI *cancel_io_func_t)(HANDLE); rb_thread_t *th = (rb_thread_t *)ptr; thread_debug("ubf_handle: %p\n", th); w32_set_event(th->native_thread_data.interrupt_event); }
static int native_thread_create(rb_thread_t *th) { int err = 0; if (use_cached_thread(th)) { thread_debug("create (use cached thread): %p\n", (void *)th); } else { pthread_attr_t attr; const size_t stack_size = th->vm->default_params.thread_machine_stack_size; const size_t space = space_size(stack_size); th->machine_stack_maxsize = stack_size - space; #ifdef __ia64 th->machine_stack_maxsize /= 2; th->machine_register_stack_maxsize = th->machine_stack_maxsize; #endif #ifdef HAVE_PTHREAD_ATTR_INIT CHECK_ERR(pthread_attr_init(&attr)); # ifdef PTHREAD_STACK_MIN thread_debug("create - stack size: %lu\n", (unsigned long)stack_size); CHECK_ERR(pthread_attr_setstacksize(&attr, stack_size)); # endif # ifdef HAVE_PTHREAD_ATTR_SETINHERITSCHED CHECK_ERR(pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED)); # endif CHECK_ERR(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)); err = pthread_create(&th->thread_id, &attr, thread_start_func_1, th); #else err = pthread_create(&th->thread_id, NULL, thread_start_func_1, th); #endif thread_debug("create: %p (%d)\n", (void *)th, err); #ifdef HAVE_PTHREAD_ATTR_INIT CHECK_ERR(pthread_attr_destroy(&attr)); #endif } return err; }
static int w32_wait_events(HANDLE *events, int count, DWORD timeout, rb_thread_t *th) { HANDLE *targets = events; HANDLE intr; DWORD ret; thread_debug(" w32_wait_events events:%p, count:%d, timeout:%ld, th:%p\n", events, count, timeout, th); if (th && (intr = th->native_thread_data.interrupt_event)) { native_mutex_lock(&th->vm->global_vm_lock); if (intr == th->native_thread_data.interrupt_event) { w32_reset_event(intr); if (RUBY_VM_INTERRUPTED(th)) { w32_set_event(intr); } targets = ALLOCA_N(HANDLE, count + 1); memcpy(targets, events, sizeof(HANDLE) * count); targets[count++] = intr; thread_debug(" * handle: %p (count: %d, intr)\n", intr, count); } native_mutex_unlock(&th->vm->global_vm_lock); } thread_debug(" WaitForMultipleObjects start (count: %d)\n", count); ret = WaitForMultipleObjects(count, targets, FALSE, timeout); thread_debug(" WaitForMultipleObjects end (ret: %lu)\n", ret); if (ret == WAIT_OBJECT_0 + count - 1 && th) { errno = EINTR; } if (ret == -1 && THREAD_DEBUG) { int i; DWORD dmy; for (i = 0; i < count; i++) { thread_debug(" * error handle %d - %s\n", i, GetHandleInformation(targets[i], &dmy) ? "OK" : "NG"); } } return ret; }
static int native_mutex_trylock(rb_thread_lock_t *lock) { #if USE_WIN32_MUTEX int result; thread_debug("native_mutex_trylock: %p\n", *lock); result = w32_wait_events(&*lock, 1, 1, 0); thread_debug("native_mutex_trylock result: %d\n", result); switch (result) { case WAIT_OBJECT_0: return 0; case WAIT_TIMEOUT: return EBUSY; } return EINVAL; #else return TryEnterCriticalSection(lock) == 0; #endif }
static void native_sleep(rb_thread_t *th, struct timeval *timeout_tv) { struct timespec timeout; struct timeval tvn; pthread_mutex_t *lock = &th->interrupt_lock; rb_thread_cond_t *cond = &th->native_thread_data.sleep_cond; if (timeout_tv) { struct timespec timeout_rel; timeout_rel.tv_sec = timeout_tv->tv_sec; timeout_rel.tv_nsec = timeout_tv->tv_usec; timeout = native_cond_timeout(cond, timeout_rel); } GVL_UNLOCK_BEGIN(); { pthread_mutex_lock(lock); th->unblock.func = ubf_pthread_cond_signal; th->unblock.arg = th; if (RUBY_VM_INTERRUPTED(th)) { /* interrupted. return immediate */ thread_debug("native_sleep: interrupted before sleep\n"); } else { if (!timeout_tv) native_cond_wait(cond, lock); else native_cond_timedwait(cond, lock, &timeout); } th->unblock.func = 0; th->unblock.arg = 0; pthread_mutex_unlock(lock); } GVL_UNLOCK_END(); thread_debug("native_sleep done\n"); }
static int native_mutex_unlock(rb_thread_lock_t *lock) { #if USE_WIN32_MUTEX thread_debug("release mutex: %p\n", *lock); return ReleaseMutex(*lock); #else LeaveCriticalSection(lock); return 0; #endif }
static void native_sleep(rb_thread_t *th, struct timeval *tv) { struct timespec ts; struct timeval tvn; if (tv) { gettimeofday(&tvn, NULL); ts.tv_sec = tvn.tv_sec + tv->tv_sec; ts.tv_nsec = (tvn.tv_usec + tv->tv_usec) * 1000; if (ts.tv_nsec >= PER_NANO){ ts.tv_sec += 1; ts.tv_nsec -= PER_NANO; } } thread_debug("native_sleep %ld\n", (long)(tv ? tv->tv_sec : -1)); GVL_UNLOCK_BEGIN(); { pthread_mutex_lock(&th->interrupt_lock); th->unblock.func = ubf_pthread_cond_signal; th->unblock.arg = th; if (RUBY_VM_INTERRUPTED(th)) { /* interrupted. return immediate */ thread_debug("native_sleep: interrupted before sleep\n"); } else { if (tv == 0 || ts.tv_sec < tvn.tv_sec /* overflow */ ) { int r; thread_debug("native_sleep: pthread_cond_wait start\n"); r = pthread_cond_wait(&th->native_thread_data.sleep_cond, &th->interrupt_lock); if (r) rb_bug_errno("pthread_cond_wait", r); thread_debug("native_sleep: pthread_cond_wait end\n"); } else { int r; thread_debug("native_sleep: pthread_cond_timedwait start (%ld, %ld)\n", (unsigned long)ts.tv_sec, ts.tv_nsec); r = pthread_cond_timedwait(&th->native_thread_data.sleep_cond, &th->interrupt_lock, &ts); if (r && r != ETIMEDOUT) rb_bug_errno("pthread_cond_timedwait", r); thread_debug("native_sleep: pthread_cond_timedwait end (%d)\n", r); } } th->unblock.func = 0; th->unblock.arg = 0; pthread_mutex_unlock(&th->interrupt_lock); } GVL_UNLOCK_END(); thread_debug("native_sleep done\n"); }
static void native_sleep(rb_thread_t *th, struct timeval *tv) { DWORD msec; if (tv) { msec = tv->tv_sec * 1000 + tv->tv_usec / 1000; } else { msec = INFINITE; } GVL_UNLOCK_BEGIN(); { DWORD ret; native_mutex_lock(&th->interrupt_lock); th->unblock.func = ubf_handle; th->unblock.arg = th; native_mutex_unlock(&th->interrupt_lock); if (RUBY_VM_INTERRUPTED(th)) { /* interrupted. return immediate */ } else { thread_debug("native_sleep start (%lu)\n", msec); ret = w32_wait_events(0, 0, msec, th); thread_debug("native_sleep done (%lu)\n", ret); } native_mutex_lock(&th->interrupt_lock); th->unblock.func = 0; th->unblock.arg = 0; native_mutex_unlock(&th->interrupt_lock); } GVL_UNLOCK_END(); }
//RHO /*static*/ int //RHO native_mutex_lock(rb_thread_lock_t *lock) { #if USE_WIN32_MUTEX DWORD result; while (1) { thread_debug("native_mutex_lock: %p\n", *lock); result = w32_wait_events(&*lock, 1, INFINITE, 0); switch (result) { case WAIT_OBJECT_0: /* get mutex object */ thread_debug("acquire mutex: %p\n", *lock); return 0; case WAIT_OBJECT_0 + 1: /* interrupt */ errno = EINTR; thread_debug("acquire mutex interrupted: %p\n", *lock); return 0; case WAIT_TIMEOUT: thread_debug("timeout mutex: %p\n", *lock); break; case WAIT_ABANDONED: rb_bug("win32_mutex_lock: WAIT_ABANDONED"); break; default: rb_bug("win32_mutex_lock: unknown result (%d)", result); break; } } return 0; #else EnterCriticalSection(lock); return 0; #endif }
static void native_sleep(rb_thread_t *th, struct timeval *tv) { DWORD msec; if (tv) { msec = tv->tv_sec * 1000 + tv->tv_usec / 1000; } else { msec = INFINITE; } GVL_UNLOCK_BEGIN(); { DWORD ret; int status = th->status; th->status = THREAD_STOPPED; th->unblock_function = ubf_handle; th->unblock_function_arg = th; if (RUBY_VM_INTERRUPTED(th)) { /* interrupted. return immediate */ } else { thread_debug("native_sleep start (%d)\n", (int)msec); ret = w32_wait_events(0, 0, msec, th); thread_debug("native_sleep done (%d)\n", ret); } th->unblock_function = 0; th->unblock_function_arg = 0; th->status = status; } GVL_UNLOCK_END(); RUBY_VM_CHECK_INTS(); }
static void Init_native_thread(void) { rb_thread_t *th = GET_THREAD(); ruby_native_thread_key = TlsAlloc(); DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &th->thread_id, 0, FALSE, DUPLICATE_SAME_ACCESS); th->native_thread_data.interrupt_event = CreateEvent(0, TRUE, FALSE, 0); thread_debug("initial thread (th: %p, thid: %p, event: %p)\n", th, GET_THREAD()->thread_id, th->native_thread_data.interrupt_event); }
static int native_thread_create(rb_thread_t *th) { size_t stack_size = 4 * 1024; /* 4KB */ th->thread_id = w32_create_thread(stack_size, thread_start_func_1, th); if ((th->thread_id) == 0) { return thread_errno; } w32_resume_thread(th->thread_id); if (THREAD_DEBUG) { Sleep(0); thread_debug("create: (th: %p, thid: %p, intr: %p), stack size: %d\n", th, th->thread_id, th->native_thread_data.interrupt_event, stack_size); } return 0; }
static int native_thread_create(rb_thread_t *th) { size_t stack_size = 4 * 1024; /* 4KB */ th->thread_id = w32_create_thread(stack_size, thread_start_func_1, th); if ((th->thread_id) == 0) { st_delete_wrap(th->vm->living_threads, th->self); rb_raise(rb_eThreadError, "can't create Thread (%d)", errno); } w32_resume_thread(th->thread_id); if (THREAD_DEBUG) { Sleep(0); thread_debug("create: (th: %p, thid: %p, intr: %p), stack size: %d\n", th, th->thread_id, th->native_thread_data.interrupt_event, stack_size); } return 0; }
int search_tree_for_val(tree *t, int num_threads, int val) { int i; treenode *n; dsp_stack_t *s; int *thread_id; int threads_ready = 1; int node_val; n = t->head; if(n == NULL) return -1; //Set globals for new search... DFS_NUM_THREADS = num_threads; search_val = val; val_found = 0; //Allocate threads and thread id's threads = (pthread_t *)malloc(sizeof(pthread_t) * num_threads); thread_id = (int *)malloc(sizeof(int) * num_threads); if(threads == NULL || thread_id == NULL) { perror(PROGNAME "error: error allocating threads"); exit(1); } //Allocate thread work stacks thread_work_stack = (dsp_stack_t **)malloc(sizeof(dsp_stack_t *) * num_threads); if(thread_work_stack == NULL) { perror(PROGNAME "error: error allocating thread_stacks"); exit(1); } for(i = 0; i < num_threads; i++) { thread_work_stack[i] = dsp_stack_create(t->node_count); } //Allocate thread work stack mutexes thread_work_stack_mutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t) * num_threads); if(thread_work_stack_mutex == NULL) { perror(PROGNAME "error: error allocating thread_stacks"); exit(1); } //Initialize threads for(i = 0; i < num_threads; i++) { thread_id[i] = i; dsp_stack_init(thread_work_stack[i], t->node_count); pthread_mutex_init(&thread_work_stack_mutex[i], NULL); } //Spread initial work across other thread work stacks while(n != NULL && threads_ready < num_threads) { if(n->right != NULL) { dsp_stack_push(thread_work_stack[threads_ready], n->right); threads_ready++; } //Heck, we might get lucky and find it in this predistribution step if(n->data != NULL) { node_val = *((int *)n->data); if(node_val == search_val) { thread_debug(1, "main thread: found value during predistribution step!\n"); printf("%d\t\t%d\t\t%f\t\t%d\n", DFS_TREE_SIZE, num_threads, 0.0, 1); return 0; } } n = n->left; } thread_debug(1, "main thread: predistribution step checked %d nodes\n", threads_ready - 1); //Put current node (post distribution) on first thread's work stack. s = thread_work_stack[0]; dsp_stack_push(s, n); //Timing vars float search_time; struct timeval t0, t1; gettimeofday(&t0, NULL); prog_debug(1, PROGNAME ": starting %d threads\n", num_threads); //Create threads for(i = 0; i < num_threads; i++) { pthread_create(&threads[i], NULL, thread_traverse_tree, &thread_id[i]); } //Join threads for(i = 0; i < num_threads; i++) { pthread_join(threads[i], NULL); } gettimeofday(&t1, NULL); search_time = (float)(t1.tv_sec - t0.tv_sec) + ((float)(t1.tv_usec - t0.tv_usec)/1000000.0); if(val_found == 0) { prog_debug(1, PROGNAME ": value not found in tree!\n"); } else { prog_debug(1, PROGNAME ": value found!\n"); } prog_debug(1, PROGNAME ": all threads complete\n"); //printf("size\t\tthreads\t\ttime\t\tfound\n"); printf("%d\t\t%d\t\t%f\t\t%d\n", DFS_TREE_SIZE, num_threads, search_time, val_found); //Clean up free(threads); free(thread_id); for(i = 0; i < num_threads; i++) { dsp_stack_destroy(thread_work_stack[i]); } free(thread_work_stack); free(thread_work_stack_mutex); return 0; }
void *thread_traverse_tree(void *tid) { int id = *((int *)tid); dsp_stack_t *s = thread_work_stack[id]; pthread_mutex_t *dsp_stack_mutex = &thread_work_stack_mutex[id]; treenode *n; int node_val; int nodes_processed = 0; thread_debug(1, "thread %d: started...\n", id); while(1) { //Check to see if we are done if(val_found) break; //Get next node from my stack pthread_mutex_lock(dsp_stack_mutex); n = dsp_stack_pop(s); pthread_mutex_unlock(dsp_stack_mutex); //If my stack is empty, get next available node from another stack. if(n == NULL) { n = get_next_available_treenode(id); } //If no work found in any other thread's stack, exit... if(n == NULL) { break; } //Go depth first in searching, going to the left child //and pushing the right child onto my stack. while(n != NULL) { nodes_processed++; //Make sure we are not done. if(val_found) break; //Because this is hit so often, we don't even compile //unless we absolutely need it. //thread_debug(3, "thread %d: traversing node %d\n", id, n->id); //Check value against search value if(n->data != NULL) { node_val = *((int *)n->data); if(node_val == search_val) { val_found = 1; thread_debug(1, "thread %d: value %d found at node %d!\n", id, node_val, n->id); break; } } if(n->right != NULL) { pthread_mutex_lock(dsp_stack_mutex); dsp_stack_push(s, n->right); pthread_mutex_unlock(dsp_stack_mutex); } n = n->left; } } thread_debug(1, "thread %d: exiting after processing %d nodes...\n", id, nodes_processed); pthread_exit(0); }