[3461] MacRuby/trunk
Revision: 3461 http://trac.macosforge.org/projects/ruby/changeset/3461 Author: lsansonetti@apple.com Date: 2010-02-09 14:39:29 -0800 (Tue, 09 Feb 2010) Log Message: ----------- when doing an assignment from thread X to thread Y's stack, defeat the thread-local collector by making X take ownership of the object Modified Paths: -------------- MacRuby/trunk/compiler.cpp MacRuby/trunk/compiler.h MacRuby/trunk/dispatcher.cpp MacRuby/trunk/vm.cpp MacRuby/trunk/vm.h Modified: MacRuby/trunk/compiler.cpp =================================================================== --- MacRuby/trunk/compiler.cpp 2010-02-09 02:06:07 UTC (rev 3460) +++ MacRuby/trunk/compiler.cpp 2010-02-09 22:39:29 UTC (rev 3461) @@ -148,6 +148,7 @@ debugTrapFunc = NULL; getFFStateFunc = NULL; setFFStateFunc = NULL; + takeOwnershipFunc = NULL; VoidTy = Type::getVoidTy(context); Int1Ty = Type::getInt1Ty(context); @@ -3454,8 +3455,52 @@ assert(node->nd_value != NULL); Value *new_val = compile_node(node->nd_value); + + const int type = nd_type(node); + if ((type == NODE_DASGN || type == NODE_DASGN_CURR) + && running_block != NULL) { + // Dynamic variables assignments inside a block are a + // little bit complicated: if we are creating new objects + // we do need to defeat the thread-local collector by + // taking ownership of the objects, otherwise the TLC might + // prematurely collect them. This is because the assignment + // is done into another thread's stack, which is not + // honored by the TLC. + Value *flag = new BitCastInst(running_block, Int32PtrTy, + "", bb); + flag = new LoadInst(flag, "", bb); + Value *flagv = ConstantInt::get(Int32Ty, VM_BLOCK_THREAD); + flag = BinaryOperator::CreateAnd(flag, flagv, "", bb); + Value *is_thread = new ICmpInst(*bb, ICmpInst::ICMP_EQ, + flag, flagv); + + Function *f = bb->getParent(); + BasicBlock *is_thread_bb = + BasicBlock::Create(context, "", f); + BasicBlock *merge_bb = + BasicBlock::Create(context, "", f); + + BranchInst::Create(is_thread_bb, merge_bb, + is_thread, bb); + + bb = is_thread_bb; + if (takeOwnershipFunc == NULL) { + takeOwnershipFunc = + cast<Function>(module->getOrInsertFunction( + "rb_vm_take_ownership", + VoidTy, RubyObjTy, NULL)); + } + + std::vector<Value *> params; + params.push_back(new_val); + CallInst::Create(takeOwnershipFunc, params.begin(), + params.end(), "", bb); + BranchInst::Create(merge_bb, bb); + + bb = merge_bb; + } + new StoreInst(new_val, compile_lvar_slot(node->nd_vid), bb); - return new_val; } break; Modified: MacRuby/trunk/compiler.h =================================================================== --- MacRuby/trunk/compiler.h 2010-02-09 02:06:07 UTC (rev 3460) +++ MacRuby/trunk/compiler.h 2010-02-09 22:39:29 UTC (rev 3461) @@ -197,9 +197,9 @@ Function *setCurrentClassFunc; Function *getCacheFunc; Function *debugTrapFunc; - // flip-flop Function *getFFStateFunc; Function *setFFStateFunc; + Function *takeOwnershipFunc; Constant *zeroVal; Constant *oneVal; Modified: MacRuby/trunk/dispatcher.cpp =================================================================== --- MacRuby/trunk/dispatcher.cpp 2010-02-09 02:06:07 UTC (rev 3460) +++ MacRuby/trunk/dispatcher.cpp 2010-02-09 22:39:29 UTC (rev 3461) @@ -1573,6 +1573,16 @@ vm->pop_current_block(); + rb_vm_block_t *top_b = vm->current_block(); + if (top_b == NULL) { + if (vm != RoxorVM::main) { + top_b = GetThreadPtr(vm->get_thread())->body; + } + } + if (top_b != NULL && (top_b->flags & VM_BLOCK_THREAD)) { + b->flags |= VM_BLOCK_THREAD; + } + struct Finally { RoxorVM *vm; rb_vm_block_t *b; @@ -1582,6 +1592,7 @@ } ~Finally() { vm->add_current_block(b); + b->flags &= ~VM_BLOCK_THREAD; } } finalizer(vm, b); @@ -1696,6 +1707,10 @@ aot_block = true; } + if (parent_block != NULL && (parent_block->flags & VM_BLOCK_THREAD)) { + flags |= VM_BLOCK_THREAD; + } + if (!cached) { if ((flags & VM_BLOCK_IFUNC) == VM_BLOCK_IFUNC) { b->imp = (IMP)function; Modified: MacRuby/trunk/vm.cpp =================================================================== --- MacRuby/trunk/vm.cpp 2010-02-09 02:06:07 UTC (rev 3460) +++ MacRuby/trunk/vm.cpp 2010-02-09 22:39:29 UTC (rev 3461) @@ -4598,6 +4598,18 @@ extern "C" void +rb_vm_take_ownership(VALUE obj) +{ + // This function allows the given object's ownership to be transfered to + // the current thread. It is used when objects are allocated from a + // thread but assigned into another thread's stack, which is prohibited by + // the thread-local collector. + GC_RETAIN(obj); + GC_RELEASE(obj); +} + +extern "C" +void rb_vm_thread_pre_init(rb_vm_thread_t *t, rb_vm_block_t *body, int argc, const VALUE *argv, void *vm) { @@ -4607,12 +4619,13 @@ GC_WB(&t->body, body); rb_vm_block_make_detachable_proc(body); - // Remove the thread-local bit of all dynamic variables. + // Take ownership of all dynamic variables, mark the block as + // being run from a thread. for (int i = 0; i < body->dvars_size; i++) { VALUE *dvar = body->dvars[i]; - GC_RETAIN(*dvar); - GC_RELEASE(*dvar); + rb_vm_take_ownership(*dvar); } + body->flags |= VM_BLOCK_THREAD; } else { t->body = NULL; Modified: MacRuby/trunk/vm.h =================================================================== --- MacRuby/trunk/vm.h 2010-02-09 02:06:07 UTC (rev 3460) +++ MacRuby/trunk/vm.h 2010-02-09 22:39:29 UTC (rev 3461) @@ -26,24 +26,26 @@ struct rb_vm_local *next; } rb_vm_local_t; -#define VM_BLOCK_PROC 0x0001 // block is a Proc object -#define VM_BLOCK_LAMBDA 0x0002 // block is a lambda -#define VM_BLOCK_ACTIVE 0x0004 // block is active (being executed) -#define VM_BLOCK_METHOD 0x0008 // block is created from Method -#define VM_BLOCK_IFUNC 0x0010 // block is created from rb_vm_create_block() -#define VM_BLOCK_EMPTY 0x0012 // block has an empty body +#define VM_BLOCK_PROC (1<<0) // block is a Proc object +#define VM_BLOCK_LAMBDA (1<<1) // block is a lambda +#define VM_BLOCK_ACTIVE (1<<2) // block is active (being executed) +#define VM_BLOCK_METHOD (1<<3) // block is created from Method +#define VM_BLOCK_IFUNC (1<<4) // block is created from rb_vm_create_block() +#define VM_BLOCK_EMPTY (1<<5) // block has an empty body +#define VM_BLOCK_THREAD (1<<6) // block is being executed as a Thread +#define VM_BLOCK_AOT (1<<10) // block is created by the AOT compiler + // (temporary) -#define VM_BLOCK_AOT 0x1000 // block is created by the AOT compiler (temporary) - typedef struct rb_vm_block { - VALUE proc; // a reference to a Proc object, or nil + int flags; // IMPORTANT: this field should always be at the beginning. + // Look at how rb_vm_take_ownership() is called in compiler.cpp. + VALUE proc; // a reference to a Proc object, or nil. VALUE self; VALUE klass; VALUE userdata; // if VM_BLOCK_IFUNC, contains the user data, otherwise // contains the key used in the blocks cache. rb_vm_arity_t arity; IMP imp; - int flags; rb_vm_local_t *locals; struct rb_vm_var_uses **parent_var_uses; struct rb_vm_block *parent_block;
participants (1)
-
source_changes@macosforge.org