Browse files

Merge pull request #1882 from ryoqun/thread-kill

Correctly implement Thread#kill
  • Loading branch information...
2 parents ff2d84c + 523f136 commit d8f1f0e946a3f5ed50969c4c8ed77c5068da2b49 @dbussink dbussink committed Sep 27, 2012
View
17 kernel/bootstrap/thread.rb
@@ -31,6 +31,11 @@ def raise_prim(exc)
Kernel.raise PrimitiveFailure, "Thread#raise primitive failed"
end
+ def kill_prim
+ Rubinius.primitive :thread_kill
+ Kernel.raise PrimitiveFailure, "Thread#kill primitive failed"
+ end
+
def wakeup
Rubinius.primitive :thread_wakeup
Kernel.raise ThreadError, "Thread#wakeup primitive failed, thread may be dead"
@@ -66,8 +71,6 @@ def unlock_locks
Kernel.raise PrimitiveFailure, "Thread#unlock_locks primitive failed"
end
- class Die < Exception; end # HACK
-
@abort_on_exception = false
def self.abort_on_exception
@@ -157,16 +160,6 @@ def stop?
!alive? || @sleep
end
- def kill
- @dying = true
- @sleep = false
- self.raise Die
- self
- end
-
- alias_method :exit, :kill
- alias_method :terminate, :kill
-
def sleeping?
Rubinius.synchronize(self) do
@sleep
View
20 kernel/bootstrap/thread18.rb
@@ -80,8 +80,6 @@ def __run__()
@joins.each { |join| join.send self }
end
end
- rescue Die
- @exception = nil
rescue Exception => e
# I don't really get this, but this is MRI's behavior. If we're dying
# by request, ignore any raised exception.
@@ -109,8 +107,26 @@ def setup(prime_lock)
@critical = false
@dying = false
@joins = []
+ @killed = false
end
+ def kill
+ @dying = true
+ Rubinius.synchronize(self) do
+ if @sleep and @killed
+ @sleep = false
+ wakeup
+ else
+ @sleep = false
+ @killed = true
+ kill_prim
+ end
+ end
+ end
+
+ alias_method :exit, :kill
+ alias_method :terminate, :kill
+
def value
join_inner { @result }
end
View
15 kernel/bootstrap/thread19.rb
@@ -65,14 +65,14 @@ def __run__()
@joins.each { |join| join.send self }
end
end
- rescue Die
- @killed = true
- @exception = nil
rescue Exception => e
# I don't really get this, but this is MRI's behavior. If we're dying
# by request, ignore any raised exception.
@exception = e # unless @dying
ensure
+ if Rubinius.thread_state[0] == :thread_kill
+ @killed = true
+ end
@alive = false
Rubinius.unlock(self)
unlock_locks
@@ -98,6 +98,15 @@ def setup(prime_lock)
@killed = false
end
+ def kill
+ @dying = true
+ @sleep = false
+ kill_prim
+ end
+
+ alias_method :exit, :kill
+ alias_method :terminate, :kill
+
def value
join_inner do
@killed ? nil : @result
View
3 spec/tags/18/ruby/core/thread/exit_tags.txt
@@ -1,3 +0,0 @@
-fails:Thread#exit does not set $!
-fails:Thread#exit cannot be rescued
-fails:Thread#exit killing dying sleeping thread wakes up thread
View
3 spec/tags/18/ruby/core/thread/kill_tags.txt
@@ -1,3 +0,0 @@
-fails:Thread#kill does not set $!
-fails:Thread#kill cannot be rescued
-fails:Thread#kill killing dying sleeping thread wakes up thread
View
3 spec/tags/18/ruby/core/thread/terminate_tags.txt
@@ -1,3 +0,0 @@
-fails:Thread#terminate does not set $!
-fails:Thread#terminate cannot be rescued
-fails:Thread#terminate killing dying sleeping thread wakes up thread
View
2 spec/tags/19/ruby/core/thread/kill_tags.txt
@@ -1,2 +0,0 @@
-fails:Thread#kill does not set $!
-fails:Thread#kill cannot be rescued
View
2 spec/tags/19/ruby/core/thread/terminate_tags.txt
@@ -1,2 +0,0 @@
-fails:Thread#terminate does not set $!
-fails:Thread#terminate cannot be rescued
View
3 vm/builtin/system.cpp
@@ -1592,6 +1592,9 @@ namespace rubinius {
case cCatchThrow:
reason = state->symbol("catch_throw");
break;
+ case cThreadKill:
+ reason = state->symbol("thread_kill");
+ break;
default:
reason = state->symbol("unknown");
}
View
28 vm/builtin/thread.cpp
@@ -322,41 +322,57 @@ namespace rubinius {
}
Object* Thread::raise(STATE, GCToken gct, Exception* exc) {
- init_lock_.lock();
+ utilities::thread::SpinLock::LockGuard lg(init_lock_);
Thread* self = this;
OnStack<2> os(state, self, exc);
VM* vm = self->vm_;
if(!vm) {
- self->init_lock_.unlock();
return cNil;
}
vm->register_raise(state, exc);
vm->wakeup(state, gct);
- self->init_lock_.unlock();
return exc;
}
+ Object* Thread::kill(STATE, GCToken gct) {
+ utilities::thread::SpinLock::LockGuard lg(init_lock_);
+ Thread* self = this;
+ OnStack<1> os(state, self);
+
+ VM* vm = self->vm_;
+ if(!vm) {
+ return cNil;
+ }
+
+ if(state->vm()->thread.get() == self) {
+ vm_->thread_state_.raise_thread_kill();
+ return NULL;
+ } else {
+ vm->register_kill(state);
+ vm->wakeup(state, gct);
+ return self;
+ }
+ }
+
Object* Thread::set_priority(STATE, Fixnum* new_priority) {
return new_priority;
}
Thread* Thread::wakeup(STATE, GCToken gct) {
- init_lock_.lock();
+ utilities::thread::SpinLock::LockGuard lg(init_lock_);
Thread* self = this;
OnStack<1> os(state, self);
VM* vm = self->vm_;
if(alive() == cFalse || !vm) {
- self->init_lock_.unlock();
return force_as<Thread>(Primitives::failure());
}
vm->wakeup(state, gct);
- self->init_lock_.unlock();
return self;
}
View
6 vm/builtin/thread.hpp
@@ -167,6 +167,12 @@ namespace rubinius {
Object* raise(STATE, GCToken gct, Exception* exc);
/**
+ * Kill this Thread.
+ */
+ // Rubinius.primitive :thread_kill
+ Object* kill(STATE, GCToken gct);
+
+ /**
* Set the priority for this Thread.
*
* The value is numeric, higher being more important
View
4 vm/instructions.cpp
@@ -161,6 +161,7 @@ Object* MachineCode::interpreter(STATE,
// Otherwise, fall through and run the unwinds
case cReturn:
case cCatchThrow:
+ case cThreadKill:
// Otherwise, we're doing a long return/break unwind through
// here. We need to run ensure blocks.
while(current_unwind > 0) {
@@ -314,6 +315,7 @@ Object* MachineCode::uncommon_interpreter(STATE,
// Otherwise, fall through and run the unwinds
case cReturn:
case cCatchThrow:
+ case cThreadKill:
// Otherwise, we're doing a long return/break unwind through
// here. We need to run ensure blocks.
while(current_unwind > 0) {
@@ -460,6 +462,7 @@ Object* MachineCode::debugger_interpreter(STATE,
// Otherwise, fall through and run the unwinds
case cReturn:
case cCatchThrow:
+ case cThreadKill:
// Otherwise, we're doing a long return/break unwind through
// here. We need to run ensure blocks.
while(current_unwind > 0) {
@@ -591,6 +594,7 @@ Object* MachineCode::debugger_interpreter_continue(STATE,
// Otherwise, fall through and run the unwinds
case cReturn:
case cCatchThrow:
+ case cThreadKill:
// Otherwise, we're doing a long return/break unwind through
// here. We need to run ensure blocks.
while(current_unwind > 0) {
View
3 vm/raise_reason.hpp
@@ -8,7 +8,8 @@ namespace rubinius {
cReturn,
cBreak,
cExit,
- cCatchThrow
+ cCatchThrow,
+ cThreadKill,
};
}
View
5 vm/state.cpp
@@ -38,6 +38,11 @@ namespace rubinius {
vm_->thread_state_.raise_exception(exc);
return false;
}
+ if(vm_->interrupt_by_kill_) {
+ vm_->interrupt_by_kill_ = false;
+ vm_->thread_state_.raise_thread_kill();
+ return false;
+ }
return true;
}
View
4 vm/thread_state.cpp
@@ -98,4 +98,8 @@ namespace rubinius {
raise_value_.set(value);
throw_dest_.set(dest);
}
+
+ void ThreadState::raise_thread_kill() {
+ raise_reason_ = cThreadKill;
+ }
}
View
1 vm/thread_state.hpp
@@ -58,6 +58,7 @@ namespace rubinius {
void raise_break(Object* value, VariableScope* dest);
void raise_exit(Object* code);
void raise_throw(Object* dest, Object* value);
+ void raise_thread_kill();
};
};
View
8 vm/vm.cpp
@@ -76,6 +76,7 @@ namespace rubinius {
, waiting_channel_(this, nil<Channel>())
, interrupted_exception_(this, nil<Exception>())
, interrupt_with_signal_(false)
+ , interrupt_by_kill_(false)
, waiting_header_(0)
, custom_wakeup_(0)
, custom_wakeup_data_(0)
@@ -428,6 +429,13 @@ namespace rubinius {
get_attention();
}
+ void VM::register_kill(STATE) {
+ SYNC(state);
+ interrupt_by_kill_ = true;
+ check_local_interrupts = true;
+ get_attention();
+ }
+
void VM::set_current_fiber(Fiber* fib) {
set_stack_bounds((uintptr_t)fib->stack_start(), fib->stack_size());
current_fiber.set(fib);
View
2 vm/vm.hpp
@@ -112,6 +112,7 @@ namespace rubinius {
TypedRoot<Exception*> interrupted_exception_;
bool interrupt_with_signal_;
+ bool interrupt_by_kill_;
InflatedHeader* waiting_header_;
void (*custom_wakeup_)(void*);
@@ -407,6 +408,7 @@ namespace rubinius {
}
void register_raise(STATE, Exception* exc);
+ void register_kill(STATE);
void gc_scan(GarbageCollector* gc);

0 comments on commit d8f1f0e

Please sign in to comment.