mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* vm_core.h (enum rb_thread_status): remove THREAD_TO_KILL
* vm_core.h (struct rb_thread_struct): add to_kill field * thread.c (terminate_i): convert THREAD_TO_KILL to to_kill. * thread.c (rb_threadptr_to_kill): ditto. * thread.c (rb_thread_kill): ditto. * thread.c (rb_thread_wakeup_alive): ditto. * thread.c (thread_list_i): ditto. * thread.c (static const char): ditto. * thread.c (thread_status_name): ditto. * thread.c (rb_thread_status): ditto. * thread.c (rb_thread_inspect): ditto. * vm_backtrace.c (thread_backtrace_to_ary): ditto. * thread.c (rb_threadptr_execute_interrupts): fix thread status overwritten issue. [Bug #7450] [ruby-core:50249] * test/ruby/test_thread.rb (test_hread_status_raise_after_kill): test for the above. * test/ruby/test_thread.rb (test_thread_status_in_trap): test for thread status in trap. * test/ruby/test_thread.rb (test_status_and_stop_p): remove Thread.control_interrupt unsafe test. Thread#kill no longer changes thread status. Instead of, Thread#kill receiver changes their own status when receiving kill signal. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37931 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
b8a1e36201
commit
9cbf473287
5 changed files with 93 additions and 35 deletions
27
ChangeLog
27
ChangeLog
|
@ -1,3 +1,30 @@
|
||||||
|
Wed Nov 28 16:40:14 2012 KOSAKI Motohiro <kosaki.motohiro@gmail.com>
|
||||||
|
|
||||||
|
* vm_core.h (enum rb_thread_status): remove THREAD_TO_KILL
|
||||||
|
* vm_core.h (struct rb_thread_struct): add to_kill field
|
||||||
|
* thread.c (terminate_i): convert THREAD_TO_KILL to to_kill.
|
||||||
|
* thread.c (rb_threadptr_to_kill): ditto.
|
||||||
|
* thread.c (rb_thread_kill): ditto.
|
||||||
|
* thread.c (rb_thread_wakeup_alive): ditto.
|
||||||
|
* thread.c (thread_list_i): ditto.
|
||||||
|
* thread.c (static const char): ditto.
|
||||||
|
* thread.c (thread_status_name): ditto.
|
||||||
|
* thread.c (rb_thread_status): ditto.
|
||||||
|
* thread.c (rb_thread_inspect): ditto.
|
||||||
|
* vm_backtrace.c (thread_backtrace_to_ary): ditto.
|
||||||
|
|
||||||
|
* thread.c (rb_threadptr_execute_interrupts): fix thread status
|
||||||
|
overwritten issue. [Bug #7450] [ruby-core:50249]
|
||||||
|
|
||||||
|
* test/ruby/test_thread.rb (test_hread_status_raise_after_kill):
|
||||||
|
test for the above.
|
||||||
|
* test/ruby/test_thread.rb (test_thread_status_in_trap): test for
|
||||||
|
thread status in trap.
|
||||||
|
* test/ruby/test_thread.rb (test_status_and_stop_p): remove
|
||||||
|
Thread.control_interrupt unsafe test. Thread#kill no longer
|
||||||
|
changes thread status. Instead of, Thread#kill receiver changes
|
||||||
|
their own status when receiving kill signal.
|
||||||
|
|
||||||
Wed Nov 28 16:21:46 2012 KOSAKI Motohiro <kosaki.motohiro@gmail.com>
|
Wed Nov 28 16:21:46 2012 KOSAKI Motohiro <kosaki.motohiro@gmail.com>
|
||||||
|
|
||||||
* thread.c (struct rb_mutex_struct): add allow_trap field.
|
* thread.c (struct rb_mutex_struct): add allow_trap field.
|
||||||
|
|
|
@ -497,7 +497,6 @@ class TestThread < Test::Unit::TestCase
|
||||||
a = ::Thread.new { raise("die now") }
|
a = ::Thread.new { raise("die now") }
|
||||||
b = Thread.new { Thread.stop }
|
b = Thread.new { Thread.stop }
|
||||||
c = Thread.new { Thread.exit }
|
c = Thread.new { Thread.exit }
|
||||||
d = Thread.new { sleep }
|
|
||||||
e = Thread.current
|
e = Thread.current
|
||||||
sleep 0.5
|
sleep 0.5
|
||||||
|
|
||||||
|
@ -511,21 +510,14 @@ class TestThread < Test::Unit::TestCase
|
||||||
assert_match(/^#<TestThread::Thread:.* dead>$/, c.inspect)
|
assert_match(/^#<TestThread::Thread:.* dead>$/, c.inspect)
|
||||||
assert(c.stop?)
|
assert(c.stop?)
|
||||||
|
|
||||||
d.kill
|
|
||||||
# to avoid thread switching...
|
|
||||||
ds1 = d.status
|
|
||||||
ds2 = d.stop?
|
|
||||||
es1 = e.status
|
es1 = e.status
|
||||||
es2 = e.stop?
|
es2 = e.stop?
|
||||||
assert_equal(["aborting", false], [ds1, ds2])
|
|
||||||
|
|
||||||
assert_equal(["run", false], [es1, es2])
|
assert_equal(["run", false], [es1, es2])
|
||||||
|
|
||||||
ensure
|
ensure
|
||||||
a.kill if a
|
a.kill if a
|
||||||
b.kill if b
|
b.kill if b
|
||||||
c.kill if c
|
c.kill if c
|
||||||
d.kill if d
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_safe_level
|
def test_safe_level
|
||||||
|
@ -912,4 +904,47 @@ Thread.new(Thread.current) {|mth|
|
||||||
}
|
}
|
||||||
INPUT
|
INPUT
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_thread_status_in_trap
|
||||||
|
# when running trap handler, Thread#status must show "run"
|
||||||
|
# Even though interrupted from sleeping function
|
||||||
|
assert_in_out_err([], <<-INPUT, %w(sleep run), [])
|
||||||
|
Signal.trap(:INT) {
|
||||||
|
puts Thread.current.status
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread.new(Thread.current) {|mth|
|
||||||
|
sleep 0.01
|
||||||
|
puts mth.status
|
||||||
|
Process.kill(:INT, $$)
|
||||||
|
}
|
||||||
|
sleep 0.1
|
||||||
|
INPUT
|
||||||
|
end
|
||||||
|
|
||||||
|
# Bug #7450
|
||||||
|
def test_thread_status_raise_after_kill
|
||||||
|
ary = []
|
||||||
|
|
||||||
|
t = Thread.new {
|
||||||
|
begin
|
||||||
|
ary << Thread.current.status
|
||||||
|
sleep
|
||||||
|
ensure
|
||||||
|
begin
|
||||||
|
ary << Thread.current.status
|
||||||
|
sleep
|
||||||
|
ensure
|
||||||
|
ary << Thread.current.status
|
||||||
|
end
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep 0.01
|
||||||
|
t.kill
|
||||||
|
sleep 0.01
|
||||||
|
t.raise
|
||||||
|
sleep 0.01
|
||||||
|
assert_equal(ary, ["run", "aborting", "aborting"])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
39
thread.c
39
thread.c
|
@ -326,7 +326,6 @@ terminate_i(st_data_t key, st_data_t val, rb_thread_t *main_thread)
|
||||||
if (th != main_thread) {
|
if (th != main_thread) {
|
||||||
thread_debug("terminate_i: %p\n", (void *)th);
|
thread_debug("terminate_i: %p\n", (void *)th);
|
||||||
rb_threadptr_async_errinfo_enque(th, eTerminateSignal);
|
rb_threadptr_async_errinfo_enque(th, eTerminateSignal);
|
||||||
th->status = THREAD_TO_KILL;
|
|
||||||
rb_threadptr_interrupt(th);
|
rb_threadptr_interrupt(th);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -1732,7 +1731,8 @@ static void
|
||||||
rb_threadptr_to_kill(rb_thread_t *th)
|
rb_threadptr_to_kill(rb_thread_t *th)
|
||||||
{
|
{
|
||||||
rb_threadptr_async_errinfo_clear(th);
|
rb_threadptr_async_errinfo_clear(th);
|
||||||
th->status = THREAD_TO_KILL;
|
th->status = THREAD_RUNNABLE;
|
||||||
|
th->to_kill = 1;
|
||||||
th->errinfo = INT2FIX(TAG_FATAL);
|
th->errinfo = INT2FIX(TAG_FATAL);
|
||||||
TH_JUMP_TAG(th, TAG_FATAL);
|
TH_JUMP_TAG(th, TAG_FATAL);
|
||||||
}
|
}
|
||||||
|
@ -1743,7 +1743,6 @@ rb_threadptr_execute_interrupts(rb_thread_t *th, int blocking_timing)
|
||||||
if (th->raised_flag) return;
|
if (th->raised_flag) return;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
enum rb_thread_status status = th->status;
|
|
||||||
rb_atomic_t interrupt;
|
rb_atomic_t interrupt;
|
||||||
rb_atomic_t old;
|
rb_atomic_t old;
|
||||||
int sig;
|
int sig;
|
||||||
|
@ -1766,13 +1765,16 @@ rb_threadptr_execute_interrupts(rb_thread_t *th, int blocking_timing)
|
||||||
finalizer_interrupt = interrupt & FINALIZER_INTERRUPT_MASK;
|
finalizer_interrupt = interrupt & FINALIZER_INTERRUPT_MASK;
|
||||||
trap_interrupt = interrupt & TRAP_INTERRUPT_MASK;
|
trap_interrupt = interrupt & TRAP_INTERRUPT_MASK;
|
||||||
|
|
||||||
th->status = THREAD_RUNNABLE;
|
|
||||||
|
|
||||||
/* signal handling */
|
/* signal handling */
|
||||||
if (trap_interrupt && (th == th->vm->main_thread)) {
|
if (trap_interrupt && (th == th->vm->main_thread)) {
|
||||||
|
enum rb_thread_status prev_status = th->status;
|
||||||
|
th->status = THREAD_RUNNABLE;
|
||||||
while ((sig = rb_get_next_signal()) != 0) {
|
while ((sig = rb_get_next_signal()) != 0) {
|
||||||
rb_signal_exec(th, sig);
|
rb_signal_exec(th, sig);
|
||||||
}
|
}
|
||||||
|
th->status = prev_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* exception from another thread */
|
/* exception from another thread */
|
||||||
|
@ -1788,10 +1790,13 @@ rb_threadptr_execute_interrupts(rb_thread_t *th, int blocking_timing)
|
||||||
rb_threadptr_to_kill(th);
|
rb_threadptr_to_kill(th);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
/* set runnable if th was slept. */
|
||||||
|
if (th->status == THREAD_STOPPED ||
|
||||||
|
th->status == THREAD_STOPPED_FOREVER)
|
||||||
|
th->status = THREAD_RUNNABLE;
|
||||||
rb_exc_raise(err);
|
rb_exc_raise(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
th->status = status;
|
|
||||||
|
|
||||||
if (finalizer_interrupt) {
|
if (finalizer_interrupt) {
|
||||||
rb_gc_finalize_deferred();
|
rb_gc_finalize_deferred();
|
||||||
|
@ -1805,7 +1810,7 @@ rb_threadptr_execute_interrupts(rb_thread_t *th, int blocking_timing)
|
||||||
else
|
else
|
||||||
limits_us >>= -th->priority;
|
limits_us >>= -th->priority;
|
||||||
|
|
||||||
if (status == THREAD_RUNNABLE || status == THREAD_TO_KILL)
|
if (th->status == THREAD_RUNNABLE)
|
||||||
th->running_time_us += TIME_QUANTUM_USEC;
|
th->running_time_us += TIME_QUANTUM_USEC;
|
||||||
|
|
||||||
EXEC_EVENT_HOOK(th, RUBY_EVENT_SWITCH, th->cfp->self, 0, 0, Qundef);
|
EXEC_EVENT_HOOK(th, RUBY_EVENT_SWITCH, th->cfp->self, 0, 0, Qundef);
|
||||||
|
@ -1985,7 +1990,7 @@ rb_thread_kill(VALUE thread)
|
||||||
if (th != GET_THREAD() && th->safe_level < 4) {
|
if (th != GET_THREAD() && th->safe_level < 4) {
|
||||||
rb_secure(4);
|
rb_secure(4);
|
||||||
}
|
}
|
||||||
if (th->status == THREAD_TO_KILL || th->status == THREAD_KILLED) {
|
if (th->to_kill || th->status == THREAD_KILLED) {
|
||||||
return thread;
|
return thread;
|
||||||
}
|
}
|
||||||
if (th == th->vm->main_thread) {
|
if (th == th->vm->main_thread) {
|
||||||
|
@ -2000,7 +2005,6 @@ rb_thread_kill(VALUE thread)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rb_threadptr_async_errinfo_enque(th, eKillSignal);
|
rb_threadptr_async_errinfo_enque(th, eKillSignal);
|
||||||
th->status = THREAD_TO_KILL;
|
|
||||||
rb_threadptr_interrupt(th);
|
rb_threadptr_interrupt(th);
|
||||||
}
|
}
|
||||||
return thread;
|
return thread;
|
||||||
|
@ -2082,9 +2086,8 @@ rb_thread_wakeup_alive(VALUE thread)
|
||||||
return Qnil;
|
return Qnil;
|
||||||
}
|
}
|
||||||
rb_threadptr_ready(th);
|
rb_threadptr_ready(th);
|
||||||
if (th->status != THREAD_TO_KILL) {
|
if (th->status == THREAD_STOPPED || th->status == THREAD_STOPPED_FOREVER)
|
||||||
th->status = THREAD_RUNNABLE;
|
th->status = THREAD_RUNNABLE;
|
||||||
}
|
|
||||||
return thread;
|
return thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2157,7 +2160,6 @@ thread_list_i(st_data_t key, st_data_t val, void *data)
|
||||||
case THREAD_RUNNABLE:
|
case THREAD_RUNNABLE:
|
||||||
case THREAD_STOPPED:
|
case THREAD_STOPPED:
|
||||||
case THREAD_STOPPED_FOREVER:
|
case THREAD_STOPPED_FOREVER:
|
||||||
case THREAD_TO_KILL:
|
|
||||||
rb_ary_push(ary, th->self);
|
rb_ary_push(ary, th->self);
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -2352,16 +2354,17 @@ rb_thread_group(VALUE thread)
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *
|
static const char *
|
||||||
thread_status_name(enum rb_thread_status status)
|
thread_status_name(rb_thread_t *th)
|
||||||
{
|
{
|
||||||
switch (status) {
|
switch (th->status) {
|
||||||
case THREAD_RUNNABLE:
|
case THREAD_RUNNABLE:
|
||||||
return "run";
|
if (th->to_kill)
|
||||||
|
return "aborting";
|
||||||
|
else
|
||||||
|
return "run";
|
||||||
case THREAD_STOPPED:
|
case THREAD_STOPPED:
|
||||||
case THREAD_STOPPED_FOREVER:
|
case THREAD_STOPPED_FOREVER:
|
||||||
return "sleep";
|
return "sleep";
|
||||||
case THREAD_TO_KILL:
|
|
||||||
return "aborting";
|
|
||||||
case THREAD_KILLED:
|
case THREAD_KILLED:
|
||||||
return "dead";
|
return "dead";
|
||||||
default:
|
default:
|
||||||
|
@ -2411,7 +2414,7 @@ rb_thread_status(VALUE thread)
|
||||||
}
|
}
|
||||||
return Qfalse;
|
return Qfalse;
|
||||||
}
|
}
|
||||||
return rb_str_new2(thread_status_name(th->status));
|
return rb_str_new2(thread_status_name(th));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2500,7 +2503,7 @@ rb_thread_inspect(VALUE thread)
|
||||||
VALUE str;
|
VALUE str;
|
||||||
|
|
||||||
GetThreadPtr(thread, th);
|
GetThreadPtr(thread, th);
|
||||||
status = thread_status_name(th->status);
|
status = thread_status_name(th);
|
||||||
str = rb_sprintf("#<%s:%p %s>", cname, (void *)thread, status);
|
str = rb_sprintf("#<%s:%p %s>", cname, (void *)thread, status);
|
||||||
OBJ_INFECT(str, thread);
|
OBJ_INFECT(str, thread);
|
||||||
|
|
||||||
|
|
|
@ -738,15 +738,8 @@ thread_backtrace_to_ary(int argc, VALUE *argv, VALUE thval, int to_str)
|
||||||
rb_thread_t *th;
|
rb_thread_t *th;
|
||||||
GetThreadPtr(thval, th);
|
GetThreadPtr(thval, th);
|
||||||
|
|
||||||
switch (th->status) {
|
if (th->to_kill || th->status == THREAD_KILLED)
|
||||||
case THREAD_RUNNABLE:
|
|
||||||
case THREAD_STOPPED:
|
|
||||||
case THREAD_STOPPED_FOREVER:
|
|
||||||
break;
|
|
||||||
case THREAD_TO_KILL:
|
|
||||||
case THREAD_KILLED:
|
|
||||||
return Qnil;
|
return Qnil;
|
||||||
}
|
|
||||||
|
|
||||||
return vm_backtrace_to_ary(th, argc, argv, 0, 0, to_str);
|
return vm_backtrace_to_ary(th, argc, argv, 0, 0, to_str);
|
||||||
}
|
}
|
||||||
|
|
|
@ -425,7 +425,6 @@ extern const rb_data_type_t ruby_threadptr_data_type;
|
||||||
TypedData_Get_Struct((obj), rb_thread_t, &ruby_threadptr_data_type, (ptr))
|
TypedData_Get_Struct((obj), rb_thread_t, &ruby_threadptr_data_type, (ptr))
|
||||||
|
|
||||||
enum rb_thread_status {
|
enum rb_thread_status {
|
||||||
THREAD_TO_KILL,
|
|
||||||
THREAD_RUNNABLE,
|
THREAD_RUNNABLE,
|
||||||
THREAD_STOPPED,
|
THREAD_STOPPED,
|
||||||
THREAD_STOPPED_FOREVER,
|
THREAD_STOPPED_FOREVER,
|
||||||
|
@ -498,6 +497,7 @@ typedef struct rb_thread_struct {
|
||||||
/* thread control */
|
/* thread control */
|
||||||
rb_thread_id_t thread_id;
|
rb_thread_id_t thread_id;
|
||||||
enum rb_thread_status status;
|
enum rb_thread_status status;
|
||||||
|
int to_kill;
|
||||||
int priority;
|
int priority;
|
||||||
|
|
||||||
native_thread_data_t native_thread_data;
|
native_thread_data_t native_thread_data;
|
||||||
|
|
Loading…
Reference in a new issue