Revision: 1925 http://trac.macosforge.org/projects/ruby/changeset/1925 Author: lsansonetti@apple.com Date: 2009-06-24 21:04:55 -0700 (Wed, 24 Jun 2009) Log Message: ----------- more MT work Modified Paths: -------------- MacRuby/branches/experimental/thread.c MacRuby/branches/experimental/vm.cpp MacRuby/branches/experimental/vm.h Modified: MacRuby/branches/experimental/thread.c =================================================================== --- MacRuby/branches/experimental/thread.c 2009-06-24 19:13:48 UTC (rev 1924) +++ MacRuby/branches/experimental/thread.c 2009-06-25 04:04:55 UTC (rev 1925) @@ -6,8 +6,19 @@ typedef struct rb_vm_mutex { pthread_mutex_t mutex; + pthread_t thread; } rb_vm_mutex_t; +#define assert_ok(call) \ + do { \ + const int __r = call; \ + if (__r != 0) { \ + rb_raise(rb_eRuntimeError, "pthread operation failed: error %d", \ + __r); \ + } \ + } \ + while (0) + VALUE rb_cThread; VALUE rb_cMutex; @@ -67,6 +78,7 @@ } t->vm = rb_vm_create_vm(); + t->value = Qundef; // Retain the Thread object to avoid a potential GC, the corresponding // release is done in rb_vm_thread_run(). @@ -136,11 +148,8 @@ } rb_vm_thread_t *t = GetThreadPtr(self); + pthread_join(t->thread, NULL); - if (pthread_join(t->thread, NULL) != 0) { - rb_sys_fail("pthread_join() failed"); - } - return self; } @@ -156,10 +165,10 @@ */ static VALUE -thread_value(VALUE self) +thread_value(VALUE self, SEL sel) { - // TODO - return Qnil; + thread_join_m(self, 0, 0, NULL); + return GetThreadPtr(self)->value; } void @@ -616,10 +625,15 @@ */ static VALUE -rb_thread_inspect(VALUE thread) +rb_thread_inspect(VALUE thread, SEL sel) { - // TODO - return Qnil; + const char *status = "unknown"; // TODO + + char buf[100]; + snprintf(buf, sizeof buf, "#<%s:%p %s>", rb_obj_classname(thread), + (void *)thread, status); + + return rb_str_new2(buf); } /* @@ -1134,22 +1148,12 @@ return Data_Wrap_Struct(rb_cMutex, NULL, NULL, t); } -#define GetMutex(obj) (((rb_vm_mutex_t *)DATA_PTR(obj))->mutex) +#define GetMutexPtr(obj) ((rb_vm_mutex_t *)DATA_PTR(obj)) -#define assert_ok(call) \ - do { \ - const int __r = call; \ - if (__r != 0) { \ - rb_raise(rb_eRuntimeError, "mutex operation failed: error %d", \ - __r); \ - } \ - } \ - while (0) - static VALUE mutex_initialize(VALUE self, SEL sel) { - assert_ok(pthread_mutex_init(&GetMutex(self), NULL)); + assert_ok(pthread_mutex_init(&GetMutexPtr(self)->mutex, NULL)); return self; } @@ -1160,11 +1164,10 @@ * Returns +true+ if this lock is currently held by some thread. */ -VALUE -rb_mutex_locked_p(VALUE self) +static VALUE +rb_mutex_locked_p(VALUE self, SEL sel) { - // TODO - return Qnil; + return GetMutexPtr(self)->thread == 0 ? Qfalse : Qtrue; } /* @@ -1178,23 +1181,34 @@ static VALUE rb_mutex_trylock(VALUE self, SEL sel) { - // TODO - return Qnil; + if (pthread_mutex_trylock(&GetMutexPtr(self)->mutex) == 0) { + GetMutexPtr(self)->thread = pthread_self(); + return Qtrue; + } + return Qfalse; } /* * call-seq: - * mutex.lock => true or false + * mutex.lock => self * * Attempts to grab the lock and waits if it isn't available. * Raises +ThreadError+ if +mutex+ was locked by the current thread. */ -VALUE -rb_mutex_lock(VALUE self) +static VALUE +rb_mutex_lock(VALUE self, SEL sel) { - // TODO - return Qnil; + pthread_t current = pthread_self(); + rb_vm_mutex_t *m = GetMutexPtr(self); + if (m->thread == current) { + rb_raise(rb_eThreadError, "deadlock; recursive locking"); + } + + assert_ok(pthread_mutex_lock(&m->mutex)); + m->thread = current; + + return self; } /* @@ -1205,11 +1219,19 @@ * Raises +ThreadError+ if +mutex+ wasn't locked by the current thread. */ -VALUE -rb_mutex_unlock(VALUE self) +static VALUE +rb_mutex_unlock(VALUE self, SEL sel) { - // TODO - return Qnil; + rb_vm_mutex_t *m = GetMutexPtr(self); + if (m->thread == 0) { + rb_raise(rb_eThreadError, + "Attempt to unlock a mutex which is not locked"); + } + + assert_ok(pthread_mutex_unlock(&m->mutex)); + m->thread = 0; + + return self; } /* @@ -1239,10 +1261,10 @@ static VALUE mutex_synchronize(VALUE self, SEL sel) { - assert_ok(pthread_mutex_lock(&GetMutex(self))); + rb_mutex_lock(self, 0); // TODO catch exception VALUE ret = rb_yield(Qundef); - assert_ok(pthread_mutex_unlock(&GetMutex(self))); + rb_mutex_unlock(self, 0); return ret; } @@ -1286,7 +1308,7 @@ rb_objc_define_method(rb_cThread, "initialize", thread_initialize, -1); rb_define_method(rb_cThread, "raise", thread_raise_m, -1); rb_objc_define_method(rb_cThread, "join", thread_join_m, -1); - rb_define_method(rb_cThread, "value", thread_value, 0); + rb_objc_define_method(rb_cThread, "value", thread_value, 0); rb_define_method(rb_cThread, "kill", rb_thread_kill, 0); rb_define_method(rb_cThread, "terminate", rb_thread_kill, 0); rb_define_method(rb_cThread, "exit", rb_thread_kill, 0); @@ -1306,7 +1328,7 @@ rb_define_method(rb_cThread, "safe_level", rb_thread_safe_level, 0); rb_define_method(rb_cThread, "group", rb_thread_group, 0); - rb_define_method(rb_cThread, "inspect", rb_thread_inspect, 0); + rb_objc_define_method(rb_cThread, "inspect", rb_thread_inspect, 0); cThGroup = rb_define_class("ThreadGroup", rb_cObject); rb_define_method(cThGroup, "list", thgroup_list, 0); @@ -1317,10 +1339,10 @@ rb_cMutex = rb_define_class("Mutex", rb_cObject); rb_objc_define_method(*(VALUE *)rb_cMutex, "alloc", mutex_s_alloc, 0); rb_objc_define_method(rb_cMutex, "initialize", mutex_initialize, 0); - rb_define_method(rb_cMutex, "locked?", rb_mutex_locked_p, 0); - rb_define_method(rb_cMutex, "try_lock", rb_mutex_trylock, 0); - rb_define_method(rb_cMutex, "lock", rb_mutex_lock, 0); - rb_define_method(rb_cMutex, "unlock", rb_mutex_unlock, 0); + rb_objc_define_method(rb_cMutex, "locked?", rb_mutex_locked_p, 0); + rb_objc_define_method(rb_cMutex, "try_lock", rb_mutex_trylock, 0); + rb_objc_define_method(rb_cMutex, "lock", rb_mutex_lock, 0); + rb_objc_define_method(rb_cMutex, "unlock", rb_mutex_unlock, 0); rb_define_method(rb_cMutex, "sleep", mutex_sleep, -1); rb_objc_define_method(rb_cMutex, "synchronize", mutex_synchronize, 0); Modified: MacRuby/branches/experimental/vm.cpp =================================================================== --- MacRuby/branches/experimental/vm.cpp 2009-06-24 19:13:48 UTC (rev 1924) +++ MacRuby/branches/experimental/vm.cpp 2009-06-25 04:04:55 UTC (rev 1925) @@ -443,10 +443,8 @@ RoxorCore::method_node_get(IMP imp) { std::map<IMP, rb_vm_method_node_t *>::iterator iter = ruby_imps.find(imp); - if (iter == ruby_imps.end()) { - return NULL; - } - return iter->second; + rb_vm_method_node_t *m = iter == ruby_imps.end() ? NULL : iter->second; + return m; } extern "C" @@ -1834,7 +1832,8 @@ } static force_inline void -__rb_vm_fix_args(const VALUE *argv, VALUE *new_argv, const rb_vm_arity_t &arity, int argc) +__rb_vm_fix_args(const VALUE *argv, VALUE *new_argv, + const rb_vm_arity_t &arity, int argc) { assert(argc >= arity.min); assert((arity.max == -1) || (argc <= arity.max)); @@ -2121,16 +2120,22 @@ inline void * RoxorCore::gen_stub(std::string types, int argc, bool is_objc) { + lock(); + std::map<std::string, void *> &stubs = is_objc ? objc_stubs : c_stubs; std::map<std::string, void *>::iterator iter = stubs.find(types); - if (iter != stubs.end()) { - return iter->second; + void *stub; + if (iter == stubs.end()) { + Function *f = RoxorCompiler::shared->compile_stub(types.c_str(), argc, + is_objc); + stub = (void *)compile(f); + stubs.insert(std::make_pair(types, stub)); } + else { + stub = iter->second; + } - Function *f = RoxorCompiler::shared->compile_stub(types.c_str(), argc, - is_objc); - void *stub = (void *)compile(f); - stubs.insert(std::make_pair(types, stub)); + unlock(); return stub; } @@ -2295,10 +2300,8 @@ argc = real_argc; } } - GET_CORE()->lock(); ocache.stub = (rb_vm_objc_stub_t *)GET_CORE()->gen_stub(types, argc, true); - GET_CORE()->unlock(); } static force_inline VALUE @@ -2833,7 +2836,7 @@ } rb_vm_block_t * -RoxorCore::uncache_or_create_block(NODE *key, bool *cached, int dvars_size) +RoxorVM::uncache_or_create_block(NODE *key, bool *cached, int dvars_size) { std::map<NODE *, rb_vm_block_t *>::iterator iter = blocks.find(key); @@ -2845,12 +2848,10 @@ if (iter != blocks.end()) { rb_objc_release(iter->second); } - b = (rb_vm_block_t *)xmalloc(sizeof(rb_vm_block_t) + (sizeof(VALUE *) * dvars_size)); rb_objc_retain(b); blocks[key] = b; - *cached = false; } else { @@ -2878,10 +2879,8 @@ cache_key = node; } - GET_CORE()->lock(); - bool cached = false; - rb_vm_block_t *b = GET_CORE()->uncache_or_create_block(cache_key, &cached, + rb_vm_block_t *b = GET_VM()->uncache_or_create_block(cache_key, &cached, dvars_size); if (!cached) { @@ -2892,7 +2891,9 @@ } else { assert(llvm_function != NULL); + GET_CORE()->lock(); b->imp = GET_CORE()->compile((Function *)llvm_function); + GET_CORE()->unlock(); b->arity = rb_vm_node_arity(node); } b->flags = 0; @@ -2904,8 +2905,6 @@ assert(b->dvars_size == dvars_size); } - GET_CORE()->unlock(); - b->self = self; b->node = node; b->parent_var_uses = parent_var_uses; @@ -4219,7 +4218,9 @@ void RoxorCore::register_thread(VALUE thread) { + lock(); rb_ary_push(threads, thread); + unlock(); rb_vm_thread_t *t = GetThreadPtr(thread); assert(pthread_setspecific(RoxorVM::vm_thread_key, t->vm) == 0); @@ -4231,11 +4232,13 @@ void RoxorCore::unregister_thread(VALUE thread) { + lock(); if (rb_ary_delete(threads, thread) != thread) { printf("trying to unregister a thread (%p) that was never registered!", (void *)thread); abort(); } + unlock(); rb_vm_thread_t *t = GetThreadPtr(thread); RoxorVM *vm = (RoxorVM *)t->vm; @@ -4257,10 +4260,12 @@ try { rb_vm_thread_t *t = GetThreadPtr(thread); - rb_vm_block_eval(t->body, t->argc, t->argv); + VALUE val = rb_vm_block_eval(t->body, t->argc, t->argv); + GC_WB(&t->value, val); } catch (...) { // TODO handle thread-level exceptions. + printf("exception raised inside thread %p\n", pthread_self()); } GET_CORE()->unregister_thread(thread); Modified: MacRuby/branches/experimental/vm.h =================================================================== --- MacRuby/branches/experimental/vm.h 2009-06-24 19:13:48 UTC (rev 1924) +++ MacRuby/branches/experimental/vm.h 2009-06-25 04:04:55 UTC (rev 1925) @@ -77,6 +77,7 @@ int argc; const VALUE *argv; void *vm; // an instance of RoxorVM + VALUE value; } rb_vm_thread_t; typedef struct rb_vm_outer { @@ -478,9 +479,6 @@ // Cache to avoid compiling the same Function twice. std::map<Function *, IMP> JITcache; - // Cache to avoid compiling the same block twice. - std::map<NODE *, rb_vm_block_t *> blocks; - // Cache to identify pure Ruby methods. std::map<IMP, rb_vm_method_node_t *> ruby_imps; @@ -652,9 +650,6 @@ } } - rb_vm_block_t *uncache_or_create_block(NODE *key, bool *cached, - int dvars_size); - size_t get_sizeof(const Type *type); size_t get_sizeof(const char *type); bool is_large_struct_type(const Type *type); @@ -696,6 +691,10 @@ } private: + // Cache to avoid allocating the same block twice. + std::map<NODE *, rb_vm_block_t *> blocks; + + // Keeps track of the current VM state (blocks, exceptions, bindings). std::vector<rb_vm_block_t *> current_blocks; std::vector<VALUE> current_exceptions; std::vector<rb_vm_binding_t *> bindings; @@ -765,6 +764,9 @@ return b; } + rb_vm_block_t *uncache_or_create_block(NODE *key, bool *cached, + int dvars_size); + rb_vm_binding_t *current_binding(void) { return bindings.empty() ? NULL : bindings.back();