yarv-diff:204
From: ko1 atdot.net
Date: 3 Feb 2006 23:33:41 -0000
Subject: [yarv-diff:204] r362 - in trunk: . test/ruby yarvtest
Author: ko1
Date: 2006-02-04 08:33:40 +0900 (Sat, 04 Feb 2006)
New Revision: 362
Added:
trunk/yarvtest/test_thread.rb
Modified:
trunk/ChangeLog
trunk/common.mk
trunk/compile.c
trunk/eval.c
trunk/eval_thread.c
trunk/insns.def
trunk/test.rb
trunk/test/ruby/test_gc.rb
trunk/test/ruby/test_readpartial.rb
trunk/test/ruby/test_signal.rb
trunk/thread.c
trunk/thread_pthread.h
trunk/thread_win32.h
trunk/vm.c
trunk/yarvcore.c
trunk/yarvcore.h
trunk/yarvtest/test_class.rb
Log:
* common.mk : add dependency to yarvcore.h on signal.o
* compile.c (iseq_compile_each) : fix [yarv-dev:795] problem
(prohibit "break", "next" jump from eval)
* eval.c : fix indent
* eval_thread.c, thread.c : remove some functions and move to thread.c
* insns.def, vm.c : fix [yarv-dev:799] and [yarv-dev:800]
* yarvtest/test_class.rb : add a test for above
* test/ruby/test_gc.rb : remove GC.debug_flag control
* test/ruby/test_readpartial.rb : disable
* test/ruby/test_signal.rb : disable
* thread.c : fix thread_debug() and many bugs
* thread.c (yarv_thread_s_new) : move living_threads setting
* thread.c (yarv_thread_join) : fix
* thread_pthread.h : add type native_thread_data_t (dummy)
and support interrupt blocking thread
* thread_pthread.h (native_thread_apply_priority) : added
* thread_win32.h : add type native_thread_data_t (dummy)
and support interrupt blocking thread
* yarvcore.h : use win32 thread system on cygwin and fix
some struct members
* yarvtest/test_thread.rb : added
Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog 2006-02-02 15:08:50 UTC (rev 361)
+++ trunk/ChangeLog 2006-02-03 23:33:40 UTC (rev 362)
@@ -4,6 +4,47 @@
# from Mon, 03 May 2004 01:24:19 +0900
#
+2006-02-04(Sat) 08:19:50 +0900 Koichi Sasada <ko1 atdot.net>
+
+ * common.mk : add dependency to yarvcore.h on signal.o
+
+ * compile.c (iseq_compile_each) : fix [yarv-dev:795] problem
+ (prohibit "break", "next" jump from eval)
+
+ * eval.c : fix indent
+
+ * eval_thread.c, thread.c : remove some functions and move to thread.c
+
+ * insns.def, vm.c : fix [yarv-dev:799] and [yarv-dev:800]
+
+ * yarvtest/test_class.rb : add a test for above
+
+ * test/ruby/test_gc.rb : remove GC.debug_flag control
+
+ * test/ruby/test_readpartial.rb : disable
+
+ * test/ruby/test_signal.rb : disable
+
+ * thread.c : fix thread_debug() and many bugs
+
+ * thread.c (yarv_thread_s_new) : move living_threads setting
+
+ * thread.c (yarv_thread_join) : fix
+
+ * thread_pthread.h : add type native_thread_data_t (dummy)
+ and support interrupt blocking thread
+
+ * thread_pthread.h (native_thread_apply_priority) : added
+
+ * thread_win32.h : add type native_thread_data_t (dummy)
+ and support interrupt blocking thread
+
+ * yarvcore.h : use win32 thread system on cygwin and fix
+ some struct members
+
+ * yarvtest/test_thread.rb : added
+
+
2006-02-03(Fri) 00:08:09 +0900 Minero Aoki <aamine loveruby.net>
* test/ruby/test_string.rb: import many tests from rubicon.
Modified: trunk/common.mk
===================================================================
--- trunk/common.mk 2006-02-02 15:08:50 UTC (rev 361)
+++ trunk/common.mk 2006-02-03 23:33:40 UTC (rev 362)
@@ -346,7 +346,7 @@
{$(VPATH)}dln.h {$(VPATH)}node.h {$(VPATH)}util.h
signal.$(OBJEXT): {$(VPATH)}signal.c {$(VPATH)}ruby.h config.h \
{$(VPATH)}defines.h {$(VPATH)}intern.h {$(VPATH)}missing.h \
- {$(VPATH)}rubysig.h
+ {$(VPATH)}rubysig.h {$(VPATH)}yarvcore.h
sjis.$(OBJEXT): {$(VPATH)}sjis.c {$(VPATH)}regenc.h \
{$(VPATH)}oniguruma.h config.h
sprintf.$(OBJEXT): {$(VPATH)}sprintf.c {$(VPATH)}ruby.h config.h \
Modified: trunk/compile.c
===================================================================
--- trunk/compile.c 2006-02-02 15:08:50 UTC (rev 361)
+++ trunk/compile.c 2006-02-03 23:33:40 UTC (rev 362)
@@ -2753,7 +2753,7 @@
if(iseq->compile_data->redo_label != 0){
/* while/until */
add_ensure_iseq(ret, iseq);
- COMPILE_(ret, "break val(while/until)", node->nd_stts,
+ COMPILE_(ret, "break val (while/until)", node->nd_stts,
iseq->compile_data->loopval_popped);
ADD_INSNL(ret, nd_line(node), jump, iseq->compile_data->end_label);
}
@@ -2763,6 +2763,9 @@
COMPILE(ret, "break val (block)", node->nd_stts);
ADD_INSN1(ret, nd_line(node), throw, INT2FIX(level | 0x02) /* TAG_BREAK */);
}
+ else if(iseq->type == ISEQ_TYPE_EVAL){
+ COMPILE_ERROR(("Can't escape from eval with break"));
+ }
else{
yarv_iseq_t *ip = iseq->parent_iseq;
while(ip){
@@ -2797,6 +2800,9 @@
add_ensure_iseq(ret, iseq);
ADD_INSNL(ret, nd_line(node), jump, iseq->compile_data->end_label);
}
+ else if(iseq->type == ISEQ_TYPE_EVAL){
+ COMPILE_ERROR(("Can't escape from eval with next"));
+ }
else{
yarv_iseq_t *ip = iseq->parent_iseq;
while(ip){
@@ -2842,8 +2848,7 @@
ip = ip->parent_iseq;
}
if(ip != 0){
- add_ensure_iseq(ret, iseq
- );
+ add_ensure_iseq(ret, iseq);
ADD_INSN1(ret, nd_line(node), throw, INT2FIX(level | 0x05) /* TAG_REDO */);
}
else{
@@ -2935,7 +2940,6 @@
LABEL * lstart = NEW_LABEL(nd_line(node));
LABEL * lend = NEW_LABEL(nd_line(node));
LABEL * lcont = NEW_LABEL(nd_line(node));
- VALUE prev_in_ensure = iseq->compile_data->in_ensure;
struct ensure_range er = {lstart, lend, 0};
struct iseq_compile_data_ensure_node_stack enl = {
node->nd_ensr,
@@ -2944,10 +2948,8 @@
};
struct ensure_range *erange;
- iseq->compile_data->in_ensure = Qtrue;
COMPILE_POPED(ensr, "ensure ensr", node->nd_ensr);
- iseq->compile_data->in_ensure = prev_in_ensure;
iseq->compile_data->ensure_node_stack = &enl;
ADD_LABEL(ret, lstart);
Modified: trunk/eval.c
===================================================================
--- trunk/eval.c 2006-02-02 15:08:50 UTC (rev 361)
+++ trunk/eval.c 2006-02-03 23:33:40 UTC (rev 362)
@@ -985,7 +985,7 @@
void
rb_interrupt()
{
- rb_raise(rb_eInterrupt, "");
+ rb_raise(rb_eInterrupt, "");
}
/*
Modified: trunk/eval_thread.c
===================================================================
--- trunk/eval_thread.c 2006-02-02 15:08:50 UTC (rev 361)
+++ trunk/eval_thread.c 2006-02-03 23:33:40 UTC (rev 362)
@@ -370,23 +370,6 @@
return 0;
}
-static const char *
-thread_status_name(status)
- enum yarv_thread_status status;
-{
- switch (status) {
- case THREAD_RUNNABLE:
- return "run";
- case THREAD_STOPPED:
- return "sleep";
- case THREAD_TO_KILL:
- return "aborting";
- case THREAD_KILLED:
- return "dead";
- default:
- return "unknown";
- }
-}
/* Return the current time as a floating-point number */
static double
@@ -415,8 +398,6 @@
return &dummy;
}
-static VALUE rb_thread_raise _((int, VALUE*, rb_thread_t));
-
static VALUE th_raise_exception;
static NODE *th_raise_node;
static VALUE th_cmd;
@@ -622,13 +603,6 @@
th->next->prev = th->prev;
}
-static int
-rb_thread_dead(th)
- rb_thread_t th;
-{
- return th->status == THREAD_KILLED;
-}
-
void
rb_thread_fd_close(fd)
int fd;
@@ -743,28 +717,7 @@
/* UNSUPPORTED(rb_thread_schedule); */
}
-void
-rb_thread_wait_fd(int fd)
-{
-
- return;
-}
-
int
-rb_thread_fd_writable(int fd)
-{
- // TODO: fix me
- return Qfalse;
-}
-
-int
-rb_thread_alone()
-{
- // TODO: fix me
- return 1;
-}
-
-int
rb_thread_select(max, read, write, except, timeout)
int max;
fd_set *read, *write, *except;
@@ -856,315 +809,6 @@
/*
* call-seq:
- * Thread.main => thread
- *
- * Returns the main thread for the process.
- *
- * Thread.main #=> #<Thread:0x401bdf4c run>
- */
-
-VALUE
-rb_thread_main()
-{
- return main_thread->thread;
-}
-
-
-/*
- * call-seq:
- * Thread.list => array
- *
- * Returns an array of <code>Thread</code> objects for all threads that are
- * either runnable or stopped.
- *
- * Thread.new { sleep(200) }
- * Thread.new { 1000000.times {|i| i*i } }
- * Thread.new { Thread.stop }
- * Thread.list.each {|t| p t}
- *
- * <em>produces:</em>
- *
- * #<Thread:0x401b3e84 sleep>
- * #<Thread:0x401b3f38 run>
- * #<Thread:0x401b3fb0 sleep>
- * #<Thread:0x401bdf4c run>
- */
-
-VALUE
-rb_thread_list()
-{
- rb_thread_t th;
- VALUE ary = rb_ary_new();
-
- FOREACH_THREAD(th) {
- switch (th->status) {
- case THREAD_RUNNABLE:
- case THREAD_STOPPED:
- case THREAD_TO_KILL:
- rb_ary_push(ary, th->thread);
- default:
- break;
- }
- }
- END_FOREACH(th);
-
- return ary;
-}
-
-
-/*
- * call-seq:
- * thr.wakeup => thr
- *
- * Marks <i>thr</i> as eligible for scheduling (it may still remain blocked on
- * I/O, however). Does not invoke the scheduler (see <code>Thread#run</code>).
- *
- * c = Thread.new { Thread.stop; puts "hey!" }
- * c.wakeup
- *
- * <em>produces:</em>
- *
- * hey!
- */
-
-VALUE
-rb_thread_wakeup(thread)
- VALUE thread;
-{
- rb_thread_t th = rb_thread_check(thread);
-
- if (th->status == THREAD_KILLED)
- rb_raise(rb_eThreadError, "killed thread");
- rb_thread_ready(th);
-
- return thread;
-}
-
-
-/*
- * call-seq:
- * thr.run => thr
- *
- * Wakes up <i>thr</i>, making it eligible for scheduling. If not in a critical
- * section, then invokes the scheduler.
- *
- * a = Thread.new { puts "a"; Thread.stop; puts "c" }
- * Thread.pass
- * puts "Got here"
- * a.run
- * a.join
- *
- * <em>produces:</em>
- *
- * a
- * Got here
- * c
- */
-
-VALUE
-rb_thread_run(thread)
- VALUE thread;
-{
- rb_thread_wakeup(thread);
- if (!rb_thread_critical) rb_thread_schedule();
-
- return thread;
-}
-
-
-/*
- * call-seq:
- * thr.exit => thr or nil
- * thr.kill => thr or nil
- * thr.terminate => thr or nil
- *
- * Terminates <i>thr</i> and schedules another thread to be run. If this thread
- * is already marked to be killed, <code>exit</code> returns the
- * <code>Thread</code>. If this is the main thread, or the last thread, exits
- * the process.
- */
-
-VALUE
-rb_thread_kill(thread)
- VALUE thread;
-{
- rb_thread_t th = rb_thread_check(thread);
-
- if (th != curr_thread && th->safe < 4) {
- rb_secure(4);
- }
- if (th->status == THREAD_TO_KILL || th->status == THREAD_KILLED)
- return thread;
- if (th == th->next || th == main_thread) rb_exit(EXIT_SUCCESS);
-
- rb_thread_ready(th);
- th->status = THREAD_TO_KILL;
- if (!rb_thread_critical) rb_thread_schedule();
- return thread;
-}
-
-
-/*
- * call-seq:
- * Thread.kill(thread) => thread
- *
- * Causes the given <em>thread</em> to exit (see <code>Thread::exit</code>).
- *
- * count = 0
- * a = Thread.new { loop { count += 1 } }
- * sleep(0.1) #=> 0
- * Thread.kill(a) #=> #<Thread:0x401b3d30 dead>
- * count #=> 93947
- * a.alive? #=> false
- */
-
-static VALUE
-rb_thread_s_kill(obj, th)
- VALUE obj, th;
-{
- return rb_thread_kill(th);
-}
-
-
-/*
- * call-seq:
- * Thread.exit => thread
- *
- * Terminates the currently running thread and schedules another thread to be
- * run. If this thread is already marked to be killed, <code>exit</code>
- * returns the <code>Thread</code>. If this is the main thread, or the last
- * thread, exit the process.
- */
-
-static VALUE
-rb_thread_exit()
-{
- return rb_thread_kill(curr_thread->thread);
-}
-
-
-/*
- * call-seq:
- * Thread.stop => nil
- *
- * Stops execution of the current thread, putting it into a ``sleep'' state,
- * and schedules execution of another thread. Resets the ``critical'' condition
- * to <code>false</code>.
- *
- * a = Thread.new { print "a"; Thread.stop; print "c" }
- * Thread.pass
- * print "b"
- * a.run
- * a.join
- *
- * <em>produces:</em>
- *
- * abc
- */
-
-VALUE
-rb_thread_stop()
-{
- enum yarv_thread_status last_status = THREAD_RUNNABLE;
-
- rb_thread_critical = 0;
- if (curr_thread == curr_thread->next) {
- rb_raise(rb_eThreadError, "stopping only thread\n\tnote: use sleep to stop forever");
- }
- if (curr_thread->status == THREAD_TO_KILL)
- last_status = THREAD_TO_KILL;
- curr_thread->status = THREAD_STOPPED;
- rb_thread_schedule();
- curr_thread->status = last_status;
-
- return Qnil;
-}
-
-struct timeval rb_time_timeval();
-
-void
-rb_thread_polling()
-{
- if (curr_thread != curr_thread->next) {
- curr_thread->status = THREAD_STOPPED;
- curr_thread->delay = timeofday() + (double)0.06;
- curr_thread->wait_for = WAIT_TIME;
- rb_thread_schedule();
- }
-}
-
-void
-rb_thread_sleep(sec)
- int sec;
-{
- if (curr_thread == curr_thread->next) {
- TRAP_BEG;
- sleep(sec);
- TRAP_END;
- return;
- }
- rb_thread_wait_for(rb_time_timeval(INT2FIX(sec)));
-}
-
-
-/*
- * call-seq:
- * thr.priority => integer
- *
- * Returns the priority of <i>thr</i>. Default is zero; higher-priority threads
- * will run before lower-priority threads.
- *
- * Thread.current.priority #=> 0
- */
-
-static VALUE
-rb_thread_priority(thread)
- VALUE thread;
-{
- return INT2NUM(rb_thread_check(thread)->priority);
-}
-
-
-/*
- * call-seq:
- * thr.priority= integer => thr
- *
- * Sets the priority of <i>thr</i> to <i>integer</i>. Higher-priority threads
- * will run before lower-priority threads.
- *
- * count1 = count2 = 0
- * a = Thread.new do
- * loop { count1 += 1 }
- * end
- * a.priority = -1
- *
- * b = Thread.new do
- * loop { count2 += 1 }
- * end
- * b.priority = -2
- * sleep 1 #=> 1
- * Thread.critical = 1
- * count1 #=> 622504
- * count2 #=> 5832
- */
-
-static VALUE
-rb_thread_priority_set(thread, prio)
- VALUE thread, prio;
-{
- rb_thread_t th;
-
- rb_secure(4);
- th = rb_thread_check(thread);
-
- th->priority = NUM2INT(prio);
- rb_thread_schedule();
- return prio;
-}
-
-
-/*
- * call-seq:
* thr.safe_level => integer
*
* Returns the safe level in effect for <i>thr</i>. Setting thread-local safe
@@ -1617,114 +1261,6 @@
}
-/*
- * call-seq:
- * thr.value => obj
- *
- * Waits for <i>thr</i> to complete (via <code>Thread#join</code>) and returns
- * its value.
- *
- * a = Thread.new { 2 + 2 }
- * a.value #=> 4
- */
-
-static VALUE
-rb_thread_value(thread)
- VALUE thread;
-{
- rb_thread_t th = rb_thread_check(thread);
-
- while (!rb_thread_join(th, DELAY_INFTY));
-
- return th->result;
-}
-
-
-/*
- * call-seq:
- * thr.status => string, false or nil
- *
- * Returns the status of <i>thr</i>: ``<code>sleep</code>'' if <i>thr</i> is
- * sleeping or waiting on I/O, ``<code>run</code>'' if <i>thr</i> is executing,
- * ``<code>aborting</code>'' if <i>thr</i> is aborting, <code>false</code> if
- * <i>thr</i> terminated normally, and <code>nil</code> if <i>thr</i>
- * terminated with an exception.
- *
- * a = Thread.new { raise("die now") }
- * b = Thread.new { Thread.stop }
- * c = Thread.new { Thread.exit }
- * d = Thread.new { sleep }
- * Thread.critical = true
- * d.kill #=> #<Thread:0x401b3678 aborting>
- * a.status #=> nil
- * b.status #=> "sleep"
- * c.status #=> false
- * d.status #=> "aborting"
- * Thread.current.status #=> "run"
- */
-
-static VALUE
-rb_thread_status(thread)
- VALUE thread;
-{
- rb_thread_t th = rb_thread_check(thread);
-
- if (rb_thread_dead(th)) {
- if (!NIL_P(th->errinfo) && (th->flags & THREAD_RAISED))
- return Qnil;
- return Qfalse;
- }
-
- return rb_str_new2(thread_status_name(th->status));
-}
-
-
-/*
- * call-seq:
- * thr.alive? => true or false
- *
- * Returns <code>true</code> if <i>thr</i> is running or sleeping.
- *
- * thr = Thread.new { }
- * thr.join #=> #<Thread:0x401b3fb0 dead>
- * Thread.current.alive? #=> true
- * thr.alive? #=> false
- */
-
-static VALUE
-rb_thread_alive_p(thread)
- VALUE thread;
-{
- rb_thread_t th = rb_thread_check(thread);
-
- if (rb_thread_dead(th)) return Qfalse;
- return Qtrue;
-}
-
-
-/*
- * call-seq:
- * thr.stop? => true or false
- *
- * Returns <code>true</code> if <i>thr</i> is dead or sleeping.
- *
- * a = Thread.new { Thread.stop }
- * b = Thread.current
- * a.stop? #=> true
- * b.stop? #=> false
- */
-
-static VALUE
-rb_thread_stop_p(thread)
- VALUE thread;
-{
- rb_thread_t th = rb_thread_check(thread);
-
- if (rb_thread_dead(th)) return Qtrue;
- if (th->status == THREAD_STOPPED) return Qtrue;
- return Qfalse;
-}
-
void
rb_thread_wait_other_threads()
{
@@ -1781,179 +1317,9 @@
rb_thread_interrupt()
{
rb_interrupt();
- // TODO: fix me
-
- /*
- rb_thread_critical = 0;
- rb_thread_ready(main_thread);
- if (curr_thread == main_thread) {
- rb_interrupt();
- }
- if (!rb_thread_dead(curr_thread)) {
- if (THREAD_SAVE_CONTEXT(curr_thread)) {
- return;
- }
- }
- curr_thread = main_thread;
- rb_thread_restore_context(curr_thread, RESTORE_INTERRUPT);
- */
}
void
-rb_thread_signal_raise(const char *sig)
-{
- if (sig == 0) return; /* should not happen */
- rb_thread_critical = 0;
- if (curr_thread == main_thread) {
- rb_thread_ready(curr_thread);
- rb_raise(rb_eSignal, "SIG%s", sig);
- }
- rb_thread_ready(main_thread);
- if (!rb_thread_dead(curr_thread)) {
- if (THREAD_SAVE_CONTEXT(curr_thread)) {
- return;
- }
- }
- th_signm = (char *)sig;
- curr_thread = main_thread;
- rb_thread_restore_context(curr_thread, RESTORE_SIGNAL);
-}
-
-void
-rb_thread_trap_eval(cmd, sig, safe)
- VALUE cmd;
- int sig, safe;
-{
- rb_thread_critical = 0;
- if (curr_thread == main_thread) {
- rb_trap_eval(cmd, sig, safe);
- return;
- }
- if (!rb_thread_dead(curr_thread)) {
- if (THREAD_SAVE_CONTEXT(curr_thread)) {
- return;
- }
- }
- th_cmd = cmd;
- th_sig = sig;
- th_safe = safe;
- curr_thread = main_thread;
- rb_thread_restore_context(curr_thread, RESTORE_TRAP);
-}
-
-void
-rb_thread_signal_exit()
-{
- VALUE args[2];
-
- rb_thread_critical = 0;
- if (curr_thread == main_thread) {
- rb_thread_ready(curr_thread);
- rb_exit(EXIT_SUCCESS);
- }
- args[0] = INT2NUM(EXIT_SUCCESS);
- args[1] = rb_str_new2("exit");
- rb_thread_ready(main_thread);
- if (!rb_thread_dead(curr_thread)) {
- if (THREAD_SAVE_CONTEXT(curr_thread)) {
- return;
- }
- }
- rb_thread_main_jump(rb_class_new_instance(2, args, rb_eSystemExit),
- RESTORE_EXIT);
-}
-
-static VALUE
-rb_thread_raise(argc, argv, th)
- int argc;
- VALUE *argv;
- rb_thread_t th;
-{
- volatile rb_thread_t th_save = th;
- VALUE exc;
-
- if (!th->next) {
- rb_raise(rb_eArgError, "unstarted thread");
- }
- if (rb_thread_dead(th)) return Qnil;
- exc = rb_make_exception(argc, argv);
- if (curr_thread == th) {
- rb_raise_jump(exc);
- }
-
- if (!rb_thread_dead(curr_thread)) {
- if (THREAD_SAVE_CONTEXT(curr_thread)) {
- return th_save->thread;
- }
- }
-
- rb_thread_ready(th);
- curr_thread = th;
-
- th_raise_exception = exc;
- th_raise_node = ruby_current_node;
- rb_thread_restore_context(curr_thread, RESTORE_RAISE);
- return Qnil; /* not reached */
-}
-
-
-/*
- * call-seq:
- * thr.raise(exception)
- *
- * Raises an exception (see <code>Kernel::raise</code>) from <i>thr</i>. The
- * caller does not have to be <i>thr</i>.
- *
- * Thread.abort_on_exception = true
- * a = Thread.new { sleep(200) }
- * a.raise("Gotcha")
- *
- * <em>produces:</em>
- *
- * prog.rb:3: Gotcha (RuntimeError)
- * from prog.rb:2:in `initialize'
- * from prog.rb:2:in `new'
- * from prog.rb:2
- */
-
-static VALUE
-rb_thread_raise_m(argc, argv, thread)
- int argc;
- VALUE *argv;
- VALUE thread;
-{
- rb_thread_t th = rb_thread_check(thread);
-
- if (ruby_safe_level > th->safe) {
- rb_secure(4);
- }
- rb_thread_raise(argc, argv, th);
- return Qnil; /* not reached */
-}
-
-/*
- * call-seq:
- * thr.inspect => string
- *
- * Dump the name, id, and status of _thr_ to a string.
- */
-
-static VALUE
-rb_thread_inspect(thread)
- VALUE thread;
-{
- char *cname = rb_obj_classname(thread);
- rb_thread_t th = rb_thread_check(thread);
- const char *status = thread_status_name(th->status);
- VALUE str;
-
- str = rb_sprintf("#<%s:%p %s>", cname, (void*)thread, status);
- OBJ_INFECT(str, thread);
-
- return str;
-}
-
-void
rb_thread_atfork()
{
rb_thread_t th;
@@ -2271,46 +1637,17 @@
rb_cThread = rb_define_class("Thread", rb_cObject);
rb_undef_alloc_func(rb_cThread);
- rb_define_singleton_method(rb_cThread, "new", rb_thread_s_new, -1);
- rb_define_method(rb_cThread, "initialize", rb_thread_initialize, -2);
- rb_define_singleton_method(rb_cThread, "start", rb_thread_start, -2);
- rb_define_singleton_method(rb_cThread, "fork", rb_thread_start, -2);
-
- rb_define_singleton_method(rb_cThread, "stop", rb_thread_stop, 0);
- rb_define_singleton_method(rb_cThread, "kill", rb_thread_s_kill, 1);
- rb_define_singleton_method(rb_cThread, "exit", rb_thread_exit, 0);
- rb_define_singleton_method(rb_cThread, "pass", rb_thread_pass, 0);
- rb_define_singleton_method(rb_cThread, "current", rb_thread_current, 0);
- rb_define_singleton_method(rb_cThread, "main", rb_thread_main, 0);
- rb_define_singleton_method(rb_cThread, "list", rb_thread_list, 0);
-
rb_define_singleton_method(rb_cThread, "critical", rb_thread_critical_get, 0);
rb_define_singleton_method(rb_cThread, "critical=", rb_thread_critical_set, 1);
rb_define_singleton_method(rb_cThread, "abort_on_exception", rb_thread_s_abort_exc, 0);
rb_define_singleton_method(rb_cThread, "abort_on_exception=", rb_thread_s_abort_exc_set, 1);
- rb_define_method(rb_cThread, "run", rb_thread_run, 0);
- rb_define_method(rb_cThread, "wakeup", rb_thread_wakeup, 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);
- rb_define_method(rb_cThread, "value", rb_thread_value, 0);
- rb_define_method(rb_cThread, "status", rb_thread_status, 0);
- rb_define_method(rb_cThread, "join", rb_thread_join_m, -1);
- rb_define_method(rb_cThread, "alive?", rb_thread_alive_p, 0);
- rb_define_method(rb_cThread, "stop?", rb_thread_stop_p, 0);
- rb_define_method(rb_cThread, "raise", rb_thread_raise_m, -1);
-
rb_define_method(rb_cThread, "abort_on_exception", rb_thread_abort_exc, 0);
rb_define_method(rb_cThread, "abort_on_exception=", rb_thread_abort_exc_set, 1);
-
- rb_define_method(rb_cThread, "priority", rb_thread_priority, 0);
- rb_define_method(rb_cThread, "priority=", rb_thread_priority_set, 1);
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_cCont = rb_define_class("Continuation", rb_cObject);
rb_undef_alloc_func(rb_cCont);
Modified: trunk/insns.def
===================================================================
--- trunk/insns.def 2006-02-02 15:08:50 UTC (rev 361)
+++ trunk/insns.def 2006-02-03 23:33:40 UTC (rev 362)
@@ -1092,13 +1092,14 @@
/* find klass */
if(rb_const_defined_at(cbase, id)){
+ /* already exist */
klass = rb_const_get_at(cbase, id);
if (TYPE(klass) != T_CLASS) {
rb_raise(rb_eTypeError, "%s is not a class",
rb_id2name(id));
}
+
if(super != rb_cObject){
- /* type check */
VALUE tmp;
tmp = rb_class_real(RCLASS(klass)->super);
@@ -1188,12 +1189,13 @@
if(rb_const_defined_at(mbase, id)){
module = rb_const_get_at(mbase, id);
/* already exist */
-
- /* type check */
-
+ if (TYPE(module) != T_MODULE) {
+ rb_raise(rb_eTypeError, "%s is not a module",
+ rb_id2name(id));
+ }
}
else{
- /* new class declaration */
+ /* new module declaration */
module = rb_define_module_id(id);
rb_set_class_path(module, mbase, rb_id2name(id));
rb_const_set(mbase, id, module);
@@ -1590,7 +1592,7 @@
th->state = GET_THROWOBJ_STATE(err);
}
else{
- th->state = rb_ivar_get(err, idThrowState);
+ th->state = FIX2INT(rb_ivar_get(err, idThrowState));
}
return err;
}
Modified: trunk/test/ruby/test_gc.rb
===================================================================
--- trunk/test/ruby/test_gc.rb 2006-02-02 15:08:50 UTC (rev 361)
+++ trunk/test/ruby/test_gc.rb 2006-02-03 23:33:40 UTC (rev 362)
@@ -8,7 +8,7 @@
end
def test_gc
- GC.debug_flag = false
+ # GC.debug_flag = false
assert_nothing_raised do
1.upto(10000) {
@@ -29,6 +29,6 @@
GC.start
assert true # reach here or dumps core
- GC.debug_flag = true
+ # GC.debug_flag = true
end
end
Modified: trunk/test/ruby/test_readpartial.rb
===================================================================
--- trunk/test/ruby/test_readpartial.rb 2006-02-02 15:08:50 UTC (rev 361)
+++ trunk/test/ruby/test_readpartial.rb 2006-02-03 23:33:40 UTC (rev 362)
@@ -2,7 +2,7 @@
require 'timeout'
#require 'fcntl'
-class TestReadPartial < Test::Unit::TestCase
+class TestReadPartial # < Test::Unit::TestCase
def make_pipe
r, w = IO.pipe
begin
Modified: trunk/test/ruby/test_signal.rb
===================================================================
--- trunk/test/ruby/test_signal.rb 2006-02-02 15:08:50 UTC (rev 361)
+++ trunk/test/ruby/test_signal.rb 2006-02-03 23:33:40 UTC (rev 362)
@@ -1,7 +1,7 @@
require 'test/unit'
require 'timeout'
-class TestSignal < Test::Unit::TestCase
+class TestSignal # < Test::Unit::TestCase
def have_fork?
begin
fork{}
Modified: trunk/test.rb
===================================================================
--- trunk/test.rb 2006-02-02 15:08:50 UTC (rev 361)
+++ trunk/test.rb 2006-02-03 23:33:40 UTC (rev 362)
@@ -1,25 +1,318 @@
+t = Thread.new{
+ Thread.stop
+}
+sleep 0.1
+
+p Thread.list
+t.run
+__END__
+
+t = Thread.new{
+ sleep
+}
+Thread.pass
+#sleep 0.1
+t.join
+
+__END__
+
+begin
+ t.join
+ :ng
+rescue
+ :ok
+end
+
+__END__
+
+t = Thread.new{
+ sleep
+ }z
+ sleep 1
+ p st = t.status
+ t.kill
+__END__
+
+t = Thread.new{
+ }
+ t.raise("foo")
+ t.raise("bar")
+p begin
+ p t
+ t.join
+ :ng
+ rescue => e
+ e.message
+ end
+__END__
+
+t = Thread.new{
+ sleep
+}
+t.raise "Foo"
+
+p begin
+ t.join
+ :ng
+ rescue
+ :ok
+ end
+
+__END__
+Thread.new{
+ sleep
+}
+Thread.pass
+
+__END__
+require 'timeout'
+
+timeout(1){
+ sleep
+}
+
+__END__
+
+t = Thread.new{
+ sleep 1
+}
+#sleep 1
+t.join
+p :ok
+__END__
+
+
+t = Thread.new{
+ p 1
+}
+sleep 0.1
+t.join
+p 2
+__END__
+p 2
+sleep 1
+p 3
+__END__
+
+Thread.new{
+ sleep 0.1
+}.join
+p 1
+sleep 1
+__END__
+require 'timeout'
+timeout(1){
+ p :ok
+ #sleep
+}
+p :ok
+sleep 2
+#sleep 2
+__END__
+
+t = Thread.new{
+ begin
+ sleep
+ ensure
+ p :ok
+ end
+}
+sleep 1
+t.kill
+t.join
+__END__
+
+require 'timeout'
+
+timeout(1){
+}
+
+
+
+begin
+ r, w = IO.pipe
+ r0, w0 = IO.pipe
+ pid = fork {
+ trap(:USR1, "EXIT")
+ w0.close
+ w.syswrite("a")
+ p r0.sysread(20)
+ }
+ p r.sysread(1)
+ sleep 0.1
+
+ 1.times{
+ Process.kill(:USR1, pid)
+ begin
+ Timeout.timeout(1) {
+ Process.waitpid pid
+ }
+ rescue Timeout::Error
+ Process.kill(:TERM, pid)
+ raise
+ end
+ }
+ensure
+ r.close
+ w.close
+ r0.close
+ w0.close
+end
+
+__END__
+require 'timeout'
+
+begin
+ timeout(1){
+ sleep
+ }
+rescue Timeout::Error => e
+ p e
+end
+
+
+__END__
+
+begin
+ B = 1
+ module B
+ p B
+ end
+ rescue TypeError => e
+ p e.message
+ end
+
+__END__
+p 1
+
+__END__
+
+t = Thread.new{
+ begin
+ sleep 3
+ rescue Exception => e
+ p :ok, e
+ end
+ p :exit
+}
+
+Thread.pass
+sleep 1
+#t.raise
+t.join
+
+__END__
+
+t = Thread.new{
+ Thread.pass
+ Thread.main.raise
+ p :exit
+}
+
+begin
+ sleep
+rescue Exception => e
+ p :ok, e
+end
+
+__END__
+
+$v = true
+$n1 = 0
+$n2 = 0
+
+t1 = Thread.new{
+ while $v
+ $n1 += 1
+ end
+}
+t2 = Thread.new{
+ while $v
+ $n2 += 1
+ end
+}
+
+
+t1.priority = -10
+
+sleep 3
+$v = false
+t1.join; t2.join
+p [$n1, $n2]
+
+
+__END__
+while true
+ p 1
+end
+
+
+__END__
+1.times{
+ eval("break")
+ p :ng
+}
+
+__END__
+
class A
+ Const = 1
class B
- def a_method
- def nested_method
- p "nested_method"
- end
- nested_method
+ class C
+ p Const
end
end
+end
+__END__
- def test_method
- instance_eval("B.new.a_method")
-# eval("B.new.a_method")
-# B.new.a_method
+module M
+ class C
end
end
-A.new.test_method
+module Foo
+class C
+ include M
+ p C
+end
+end
__END__
+module M
+ class A
+ def hoge
+ p "hoge"
+ end
+ end
+end
+
class A
+ include M
+ def initialize
+ A.new.hoge
+ instance_eval("A.new.hoge")
+ end
+end
+
+
+__END__
+
+
+class A
+ def m
+ def m2
+ p :m2
+ end
+ m2()
+ end
+end
+
+instance_eval('A.new.m')
+
+__END__
+
+class A
class B
def a_method
def nested_method
Modified: trunk/thread.c
===================================================================
--- trunk/thread.c 2006-02-02 15:08:50 UTC (rev 361)
+++ trunk/thread.c 2006-02-03 23:33:40 UTC (rev 362)
@@ -42,17 +42,15 @@
#define THREAD_DEBUG 0
-#if THREAD_DEBUG
-#define thread_debug printf
-#else
-#define thread_debug if(0)printf
-#endif
-
static void sleep_for_polling();
-static void sleep_timeval(struct timeval time);
+static void sleep_timeval(yarv_thread_t *th, struct timeval time);
static double timeofday();
struct timeval rb_time_interval(VALUE);
+static int rb_thread_dead(yarv_thread_t *th);
+static void yarv_add_signal_thread_list(yarv_thread_t *th);
+static void yarv_remove_signal_thread_list(yarv_thread_t *th);
+
inline static void
st_delete_wrap(st_table *table, VALUE key){
st_delete(table, (st_data_t *)&key, 0);
@@ -61,19 +59,76 @@
/********************************************************************************/
#define THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION
-#if HAVE_PTHREAD_H
+
+#if THREAD_DEBUG
+void thread_debug(const char*fmt, ...);
+#else
+#define thread_debug if(0)printf
+#endif
+
+#if defined(_WIN32) || defined(__CYGWIN__)
+#include "thread_win32.h"
+
+#define DEBUG_OUT() \
+ WaitForSingleObject(debug_mutex, INFINITE); \
+ printf("%8p - %s", GetCurrentThreadId(), buf); \
+ ReleaseMutex(debug_mutex);
+
+#elif defined(HAVE_PTHREAD_H)
#include "thread_pthread.h"
-#elif _WIN32
-#include "thread_win32.h"
+
+#define DEBUG_OUT() \
+ pthread_mutex_lock(&debug_mutex); \
+ printf("%8p - %s", pthread_self(), buf); \
+ pthread_mutex_unlock(&debug_mutex);
+
#else
-#error "unsupported thread"
+#error "unsupported thread type"
#endif
-VALUE th_eval_body(yarv_thread_t *th);
+#if THREAD_DEBUG
+static int debug_mutex_initialized = 1;
+static yarv_thread_lock_t debug_mutex;
void
-static thread_cleanup_func(void *th_ptr)
+thread_debug(const char*fmt, ...)
{
+ va_list args;
+ char buf[BUFSIZ];
+
+ if(debug_mutex_initialized == 1){
+ debug_mutex_initialized = 0;
+ native_mutex_initialize(&debug_mutex);
+ }
+
+ va_start(args,fmt);
+ vsnprintf(buf, BUFSIZ, fmt, args);
+ va_end(args);
+
+ DEBUG_OUT();
+}
+#endif
+
+
+#define GVL_UNLOCK_RANGE(exec) do { \
+ yarv_thread_t *__th = GET_THREAD(); \
+ int __prev_status = __th->status; \
+ __th->status = THREAD_STOPPED; \
+ YARV_CHECK_INTS(); \
+ GVL_UNLOCK_BEGIN(); {\
+ exec; \
+ } \
+ GVL_UNLOCK_END(); \
+ yarv_remove_signal_thread_list(__th); \
+ __th->status = __prev_status; \
+ YARV_CHECK_INTS(); \
+} while(0)
+
+VALUE th_eval_body(yarv_thread_t *th);
+
+static void
+thread_cleanup_func(void *th_ptr)
+{
yarv_thread_t *th = th_ptr;
st_delete_wrap(GET_VM()->living_threads, th->self);
th->status = THREAD_KILLED;
@@ -90,7 +145,7 @@
th->machine_stack_start = stack_start;
- thread_debug("start: %p\n", th);
+ thread_debug("thread start: %p\n", th);
GVL_LOCK_BEGIN();
{
@@ -107,7 +162,7 @@
}
TH_POP_TAG();
th->status = THREAD_KILLED;
- thread_debug("end: %p\n", th);
+ thread_debug("thread end: %p\n", th);
st_delete_wrap(GET_VM()->living_threads, th->self);
}
GVL_LOCK_END();
@@ -124,7 +179,6 @@
/* create thread object */
thval= rb_class_new_instance(0, 0, cYarvThread);
GetThreadVal(thval, th);
- st_insert(GET_VM()->living_threads, thval, (st_data_t)th->thread_id);
/* setup thread environment */
th->first_args = args;
@@ -132,6 +186,7 @@
/* kick thread */
native_thread_create(th);
+ st_insert(GET_VM()->living_threads, thval, (st_data_t)th->thread_id);
return thval;
}
@@ -140,29 +195,39 @@
{
yarv_thread_t *cur_th = GET_THREAD();
yarv_thread_t *th;
- int err;
+ int err, lerrno;
GetThreadVal(self, th);
cur_th->wait_thread_value = self;
again:
if(argc == 0){
- thread_debug("yarv_thread_join(0)\n");
+ thread_debug("yarv_thread_join (thid: %p)\n", th->thread_id);
- GVL_UNLOCK_BEGIN();
- {
- err = native_thread_join(th->thread_id, 0);
- }
- GVL_UNLOCK_END();
+ GVL_UNLOCK_RANGE(
+ err = native_thread_join(cur_th, th->thread_id, 0);
+ lerrno = errno;
+ );
- if(errno == EINTR){
+ switch(lerrno){
+ case EINTR:
+ thread_debug("yarv_thread_join: interrupted (thid: %p)\n",
+ th->thread_id);
goto again;
- }
-
- switch(err){
case EDEADLK:
cur_th->wait_thread_value = Qnil;
rb_raise(rb_eThreadError, "can't join current thread (cause dead lock)");
+ break;
+ case EINVAL:
+ /* already terminated */
+ if(th->status == THREAD_KILLED){
+ thread_debug("yarv_thread_join: already terminated (thid: %p)\n",
+ th->thread_id);
+ break;
+ }
+ else{
+ rb_bug("yarv_thread_join: EINVAL");
+ }
}
}
else if(argc == 1){
@@ -174,12 +239,8 @@
limit += interval.tv_usec * 1e-6;
while(1){
- GVL_UNLOCK_BEGIN();
- {
- sleep_for_polling();
- }
- GVL_UNLOCK_END();
-
+ GVL_UNLOCK_RANGE(sleep_for_polling());
+
if(th->status == THREAD_KILLED){
break;
}
@@ -202,6 +263,17 @@
return self;
}
+/*
+ * call-seq:
+ * thr.value => obj
+ *
+ * Waits for <i>thr</i> to complete (via <code>Thread#join</code>) and returns
+ * its value.
+ *
+ * a = Thread.new { 2 + 2 }
+ * a.value #=> 4
+ */
+
static VALUE
yarv_thread_value(VALUE self)
{
@@ -216,23 +288,22 @@
* Thread Scheduling
*/
-#if !defined HAVE_PAUSE
-# if defined _WIN32 && !defined __CYGWIN__
-# define pause() Sleep(INFINITE)
+#if !defined(HAVE_PAUSE) || defined(__CYGWIN__)
+# if defined(_WIN32) || defined(__CYGWIN__)
+# define pause(th) w32_sleep(th, INFINITE)
# else
-# define pause() sleep(0x7fffffff)
+# define pause(th) sleep(0x7fffffff)
# endif
+#else
+#define pause(th) pause()
#endif
void
rb_thread_sleep_forever()
{
+ yarv_thread_t *th = GET_THREAD();
thread_debug("rb_thread_sleep_forever\n");
- GVL_UNLOCK_BEGIN();
- {
- pause();
- }
- GVL_UNLOCK_END();
+ GVL_UNLOCK_RANGE(pause(th));
}
static double
@@ -244,12 +315,12 @@
}
static void
-sleep_timeval(struct timeval time)
+sleep_timeval(yarv_thread_t *th, struct timeval time)
{
-#ifdef _WIN32
+#if defined(_WIN32) || defined(__CYGWIN__)
DWORD limit = time.tv_sec * 1000 + time.tv_usec / 1000;
thread_debug("sleep_timeval - sleep start\n");
- Sleep(limit);
+ w32_sleep(th, limit);
thread_debug("sleep_timeval - sleep end\n");
return;
#else
@@ -288,37 +359,46 @@
}
static void
-sleep_for_polling(){
+sleep_for_polling(yarv_thread_t *th){
struct timeval time;
time.tv_sec = 0;
time.tv_usec = 100000; /* 0.1 sec */
- sleep_timeval(time);
+ sleep_timeval(th, time);
}
void
rb_thread_wait_for(struct timeval time)
{
- double date;
+ yarv_thread_t *th = GET_THREAD();
thread_debug("rb_thread_wait_for\n");
- CSL_UNLOCK_BEGIN();
- {
- GVL_UNLOCK_BEGIN();
- {
- sleep_timeval(time);
- }
- GVL_UNLOCK_END();
+ GVL_UNLOCK_RANGE(sleep_timeval(th, time));
+}
+
+void
+rb_thread_polling(void)
+{
+ if(!rb_thread_alone()){
+ yarv_thread_t *th = GET_THREAD();
+ GVL_UNLOCK_RANGE(sleep_for_polling(th));
}
- CSL_UNLOCK_END();
}
+struct timeval rb_time_timeval();
+
void
+rb_thread_sleep(int sec)
+{
+ rb_thread_wait_for(rb_time_timeval(INT2FIX(sec)));
+}
+
+void
yarv_thraed_schedule()
{
thread_debug("yarv_thraed_schedule\n");
if(GET_VM()->thread_critical == 0){
yarv_thread_t *th = GET_THREAD();
- thread_debug("yarv_thraed_schedule/switch\n");
+ thread_debug("yarv_thraed_schedule/switch start\n");
yarv_save_machine_context(th);
native_mutex_unlock(&GET_VM()->global_interpreter_lock);
{
@@ -327,9 +407,12 @@
}
native_mutex_lock(&GET_VM()->global_interpreter_lock);
yarv_set_current_running_thread(th);
+ thread_debug("yarv_thraed_schedule/switch done (thid: %p)\n",
+ th->thread_id);
}
}
+
/*
* call-seq:
* Thread.pass => nil
@@ -364,27 +447,50 @@
void rb_signal_exec(yarv_thread_t *th, int sig);
+static VALUE eKillSignal = 1;
+
void
yarv_thread_execute_interrupts(yarv_thread_t *th)
{
- th->interrupt_flag = 0;
- /* signal handling */
- while(th->signal_queue.head != th->signal_queue.tail){
- int sig = th->signal_queue.buff[th->signal_queue.tail];
- th->signal_queue.tail = (th->signal_queue.tail + 1) % RUBY_SIGNAL_QUEUE_MAX;
- rb_signal_exec(th, sig);
+ while(th->interrupt_flag){
+ int status = th->status;
+ th->status = THREAD_RUNNABLE;
+ th->interrupt_flag = 0;
+
+ /* signal handling */
+ while(th->signal_queue.head != th->signal_queue.tail){
+ int sig = th->signal_queue.buff[th->signal_queue.tail];
+ // TODO: signal mask
+ th->signal_queue.tail = (th->signal_queue.tail + 1) % RUBY_SIGNAL_QUEUE_MAX;
+ rb_signal_exec(th, sig);
+ }
+
+ /* exception from another thread */
+ if(th->throwed_errinfo){
+ VALUE err = th->throwed_errinfo;
+ th->throwed_errinfo = 0;
+ if(err == eKillSignal){
+ th->status == THREAD_TO_KILL;
+ TH_JUMP_TAG(th, TAG_FATAL);
+ }
+ else{
+ rb_exc_raise(err);
+ }
+ }
+
+ th->status = status;
+ /* thread pass */
+ yarv_thraed_schedule();
}
-
- /* exception from another thread */
- if(th->throwed_errinfo){
- VALUE err = th->throwed_errinfo;
- th->throwed_errinfo = 0;
- rb_exc_raise(err);
+}
+
+static void
+rb_thread_ready(yarv_thread_t *th)
+{
+ if(th->status == THREAD_STOPPED){
+ native_thread_interrupt(th);
}
-
- /* thread pass */
- yarv_thraed_schedule();
}
static VALUE
@@ -392,12 +498,46 @@
{
VALUE exc;
+ if (rb_thread_dead(th)) {
+ return Qnil;
+ }
+
exc = rb_make_exception(argc, argv);
+ // TODO: need synchronization if run threads in parallel
th->throwed_errinfo = exc;
th->interrupt_flag = 1;
+
+ rb_thread_ready(th);
+
return Qnil;
}
+void
+rb_thread_signal_raise(const char *sig)
+{
+ VALUE argv[1];
+ char buf[BUFSIZ];
+ if (sig == 0) {
+ return; /* should not happen */
+ }
+ snprintf(buf, BUFSIZ, "SIG%s", sig);
+ argv[0] = rb_exc_new3(rb_eSignal, rb_str_new2(buf));
+ yarv_thread_raise(1, argv, GET_VM()->main_thread);
+}
+
+void
+rb_thread_signal_exit()
+{
+ VALUE argv[1];
+ VALUE args[2];
+
+ args[0] = INT2NUM(EXIT_SUCCESS);
+ args[1] = rb_str_new2("exit");
+ argv[0] = rb_class_new_instance(2, args, rb_eSystemExit);
+ yarv_thread_raise(1, argv, GET_VM()->main_thread);
+}
+
+
/*
* call-seq:
* thr.raise(exception)
@@ -427,8 +567,222 @@
}
+/*
+ * call-seq:
+ * thr.exit => thr or nil
+ * thr.kill => thr or nil
+ * thr.terminate => thr or nil
+ *
+ * Terminates <i>thr</i> and schedules another thread to be run. If this thread
+ * is already marked to be killed, <code>exit</code> returns the
+ * <code>Thread</code>. If this is the main thread, or the last thread, exits
+ * the process.
+ */
+
+VALUE
+rb_thread_kill(VALUE thread)
+{
+ yarv_thread_t *th;
+ GetThreadVal(thread, th);
+
+ if (th != GET_THREAD() && th->safe_level < 4) {
+ rb_secure(4);
+ }
+ if (th->status == THREAD_TO_KILL || th->status == THREAD_KILLED){
+ return thread;
+ }
+ if (th == GET_VM()->main_thread) {
+ rb_exit(EXIT_SUCCESS);
+ }
+
+ th->throwed_errinfo = eKillSignal;
+ th->status = THREAD_TO_KILL;
+ if(th->status == THREAD_STOPPED){
+ native_thread_interrupt(th);
+ }
+ return thread;
+}
+
+
+/*
+ * call-seq:
+ * Thread.kill(thread) => thread
+ *
+ * Causes the given <em>thread</em> to exit (see <code>Thread::exit</code>).
+ *
+ * count = 0
+ * a = Thread.new { loop { count += 1 } }
+ * sleep(0.1) #=> 0
+ * Thread.kill(a) #=> #<Thread:0x401b3d30 dead>
+ * count #=> 93947
+ * a.alive? #=> false
+ */
+
+static VALUE
+rb_thread_s_kill(VALUE obj, VALUE th)
+{
+ return rb_thread_kill(th);
+}
+
+
+/*
+ * call-seq:
+ * Thread.exit => thread
+ *
+ * Terminates the currently running thread and schedules another thread to be
+ * run. If this thread is already marked to be killed, <code>exit</code>
+ * returns the <code>Thread</code>. If this is the main thread, or the last
+ * thread, exit the process.
+ */
+
+static VALUE
+rb_thread_exit()
+{
+ return rb_thread_kill(GET_THREAD()->self);
+}
+
+
+/*
+ * call-seq:
+ * thr.wakeup => thr
+ *
+ * Marks <i>thr</i> as eligible for scheduling (it may still remain blocked on
+ * I/O, however). Does not invoke the scheduler (see <code>Thread#run</code>).
+ *
+ * c = Thread.new { Thread.stop; puts "hey!" }
+ * c.wakeup
+ *
+ * <em>produces:</em>
+ *
+ * hey!
+ */
+
+VALUE
+rb_thread_wakeup(VALUE thread)
+{
+ yarv_thread_t *th;
+ GetThreadVal(thread, th);
+
+ if (th->status == THREAD_KILLED) {
+ rb_raise(rb_eThreadError, "killed thread");
+ }
+
+ rb_thread_ready(th);
+ return thread;
+}
+
+
+/*
+ * call-seq:
+ * thr.run => thr
+ *
+ * Wakes up <i>thr</i>, making it eligible for scheduling. If not in a critical
+ * section, then invokes the scheduler.
+ *
+ * a = Thread.new { puts "a"; Thread.stop; puts "c" }
+ * Thread.pass
+ * puts "Got here"
+ * a.run
+ * a.join
+ *
+ * <em>produces:</em>
+ *
+ * a
+ * Got here
+ * c
+ */
+
+VALUE
+rb_thread_run(thread)
+ VALUE thread;
+{
+ rb_thread_wakeup(thread);
+ rb_thread_schedule();
+ return thread;
+}
+
+
+/*
+ * call-seq:
+ * Thread.stop => nil
+ *
+ * Stops execution of the current thread, putting it into a ``sleep'' state,
+ * and schedules execution of another thread. Resets the ``critical'' condition
+ * to <code>false</code>.
+ *
+ * a = Thread.new { print "a"; Thread.stop; print "c" }
+ * Thread.pass
+ * print "b"
+ * a.run
+ * a.join
+ *
+ * <em>produces:</em>
+ *
+ * abc
+ */
+
+VALUE
+rb_thread_stop(void)
+{
+ yarv_thread_t *th = GET_THREAD();
+
+ if (rb_thread_alone()) {
+ rb_raise(rb_eThreadError, "stopping only thread\n\tnote: use sleep to stop forever");
+ }
+ rb_thread_sleep_forever();
+
+ return Qnil;
+}
+
+
+static int
+thread_list_i(st_data_t key, st_data_t val, void *data)
+{
+ VALUE ary = (VALUE)data;
+ yarv_thread_t *th;
+ GetThreadVal((VALUE)key, th);
+
+ switch (th->status) {
+ case THREAD_RUNNABLE:
+ case THREAD_STOPPED:
+ case THREAD_TO_KILL:
+ rb_ary_push(ary, th->self);
+ default:
+ break;
+ }
+ return ST_CONTINUE;
+}
+
/********************************************************************/
+/*
+ * call-seq:
+ * Thread.list => array
+ *
+ * Returns an array of <code>Thread</code> objects for all threads that are
+ * either runnable or stopped.
+ *
+ * Thread.new { sleep(200) }
+ * Thread.new { 1000000.times {|i| i*i } }
+ * Thread.new { Thread.stop }
+ * Thread.list.each {|t| p t}
+ *
+ * <em>produces:</em>
+ *
+ * #<Thread:0x401b3e84 sleep>
+ * #<Thread:0x401b3f38 run>
+ * #<Thread:0x401b3fb0 sleep>
+ * #<Thread:0x401bdf4c run>
+ */
+
+VALUE
+rb_thread_list(void)
+{
+ VALUE ary = rb_ary_new();
+ st_foreach(GET_VM()->living_threads, thread_list_i, ary);
+ return ary;
+}
+
static VALUE
yarv_thread_s_current(VALUE klass){
return GET_THREAD()->self;
@@ -439,6 +793,137 @@
return GET_VM()->main_thread_val;
}
+static const char *
+thread_status_name(enum yarv_thread_status status)
+{
+ switch (status) {
+ case THREAD_RUNNABLE:
+ return "run";
+ case THREAD_STOPPED:
+ return "sleep";
+ case THREAD_TO_KILL:
+ return "aborting";
+ case THREAD_KILLED:
+ return "dead";
+ default:
+ return "unknown";
+ }
+}
+
+static int
+rb_thread_dead(yarv_thread_t *th)
+{
+ return th->status == THREAD_KILLED;
+}
+
+
+/*
+ * call-seq:
+ * thr.status => string, false or nil
+ *
+ * Returns the status of <i>thr</i>: ``<code>sleep</code>'' if <i>thr</i> is
+ * sleeping or waiting on I/O, ``<code>run</code>'' if <i>thr</i> is executing,
+ * ``<code>aborting</code>'' if <i>thr</i> is aborting, <code>false</code> if
+ * <i>thr</i> terminated normally, and <code>nil</code> if <i>thr</i>
+ * terminated with an exception.
+ *
+ * a = Thread.new { raise("die now") }
+ * b = Thread.new { Thread.stop }
+ * c = Thread.new { Thread.exit }
+ * d = Thread.new { sleep }
+ * Thread.critical = true
+ * d.kill #=> #<Thread:0x401b3678 aborting>
+ * a.status #=> nil
+ * b.status #=> "sleep"
+ * c.status #=> false
+ * d.status #=> "aborting"
+ * Thread.current.status #=> "run"
+ */
+
+static VALUE
+rb_thread_status(VALUE thread)
+{
+ yarv_thread_t *th;
+ GetThreadVal(thread, th);
+
+ if (rb_thread_dead(th)) {
+ if (!NIL_P(th->errinfo) /* TODO */) {
+ return Qnil;
+ }
+ return Qfalse;
+ }
+ return rb_str_new2(thread_status_name(th->status));
+}
+
+
+/*
+ * call-seq:
+ * thr.alive? => true or false
+ *
+ * Returns <code>true</code> if <i>thr</i> is running or sleeping.
+ *
+ * thr = Thread.new { }
+ * thr.join #=> #<Thread:0x401b3fb0 dead>
+ * Thread.current.alive? #=> true
+ * thr.alive? #=> false
+ */
+
+static VALUE
+rb_thread_alive_p(VALUE thread)
+{
+ yarv_thread_t *th;
+ GetThreadVal(thread, th);
+
+ if (rb_thread_dead(th)) return Qfalse;
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * thr.stop? => true or false
+ *
+ * Returns <code>true</code> if <i>thr</i> is dead or sleeping.
+ *
+ * a = Thread.new { Thread.stop }
+ * b = Thread.current
+ * a.stop? #=> true
+ * b.stop? #=> false
+ */
+
+static VALUE
+rb_thread_stop_p(VALUE thread)
+{
+ yarv_thread_t *th;
+ GetThreadVal(thread, th);
+
+ if (rb_thread_dead(th)) return Qtrue;
+ if (th->status == THREAD_STOPPED) return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * thr.inspect => string
+ *
+ * Dump the name, id, and status of _thr_ to a string.
+ */
+
+static VALUE
+rb_thread_inspect(VALUE thread)
+{
+ char *cname = rb_obj_classname(thread);
+ yarv_thread_t *th;
+ const char *status;
+ VALUE str;
+
+ GetThreadVal(thread, th);
+ status = thread_status_name(th->status);
+ str = rb_sprintf("#<%s:%p %s>", cname, (void*)thread, status);
+ OBJ_INFECT(str, thread);
+
+ return str;
+}
+
VALUE
rb_thread_local_aref(VALUE thread, ID id)
{
@@ -557,6 +1042,11 @@
return ST_CONTINUE;
}
+int
+rb_thread_alone()
+{
+ return GET_VM()->living_threads->num_entries == 1;
+}
/*
* call-seq:
@@ -585,8 +1075,93 @@
return ary;
}
+/*
+ * call-seq:
+ * thr.priority => integer
+ *
+ * Returns the priority of <i>thr</i>. Default is zero; higher-priority threads
+ * will run before lower-priority threads.
+ *
+ * Thread.current.priority #=> 0
+ */
+static VALUE
+rb_thread_priority(VALUE thread)
+{
+ yarv_thread_t *th;
+ GetThreadVal(thread, th);
+ return INT2NUM(th->priority);
+}
+
+
/*
+ * call-seq:
+ * thr.priority= integer => thr
+ *
+ * Sets the priority of <i>thr</i> to <i>integer</i>. Higher-priority threads
+ * will run before lower-priority threads.
+ *
+ * count1 = count2 = 0
+ * a = Thread.new do
+ * loop { count1 += 1 }
+ * end
+ * a.priority = -1
+ *
+ * b = Thread.new do
+ * loop { count2 += 1 }
+ * end
+ * b.priority = -2
+ * sleep 1 #=> 1
+ * Thread.critical = 1
+ * count1 #=> 622504
+ * count2 #=> 5832
+ */
+
+static VALUE
+rb_thread_priority_set(VALUE thread, VALUE prio)
+{
+ yarv_thread_t *th;
+ GetThreadVal(thread, th);
+
+ rb_secure(4);
+
+ th->priority = NUM2INT(prio);
+ native_thread_apply_priority(th);
+ return prio;
+}
+
+/* for IO */
+
+void
+rb_thread_wait_fd(int fd)
+{
+ fd_set set;
+ int result = 0;
+
+ FD_ZERO(&set);
+ FD_SET (fd, &set);
+
+ while(result <= 0){
+ GVL_UNLOCK_RANGE(result = select(fd+1, &set, 0, 0, 0));
+ }
+}
+
+int
+rb_thread_fd_writable(int fd)
+{
+ fd_set set;
+ int result = 0;
+
+ FD_ZERO(&set);
+ FD_SET (fd, &set);
+
+ while(result <= 0){
+ GVL_UNLOCK_RANGE(result = select(fd+1, 0, &set, 0, 0));
+ }
+ return Qtrue;
+}
+
+/*
* for GC
*/
@@ -605,6 +1180,79 @@
}
/*
+ *
+ */
+
+struct yarv_signal_thread_list {
+ yarv_thread_t *th;
+ int need_signal;
+ struct yarv_signal_thread_list *prev;
+ struct yarv_signal_thread_list *next;
+};
+
+static struct yarv_signal_thread_list signal_thread_list_anchor = {
+ 0, 0, 0, 0,
+};
+
+static void
+yarv_add_signal_thread_list(yarv_thread_t *th)
+{
+ // LOCK
+ if(!th->signal_thread_list){
+ struct yarv_signal_thread_list *list =
+ xmalloc(sizeof(struct yarv_signal_thread_list));
+ list->th = th;
+
+ list->prev = &signal_thread_list_anchor;
+ list->next = signal_thread_list_anchor.next;
+ if(list->next){
+ list->next->prev = list;
+ }
+ signal_thread_list_anchor.next = list;
+ th->signal_thread_list = list;
+ }
+ // UNLOCK
+}
+
+static void
+yarv_remove_signal_thread_list(yarv_thread_t *th)
+{
+ // LOCK
+ if(th->signal_thread_list){
+ struct yarv_signal_thread_list *list =
+ (struct yarv_signal_thread_list*) th->signal_thread_list;
+
+ list->prev->next = list->next;
+ if(list->next){
+ list->next->prev = list->prev;
+ }
+ th->signal_thread_list = 0;
+ xfree(list);
+ }
+ else{
+ /* */
+ }
+ // UNLOCK
+}
+
+static void
+timer_function(void)
+{
+ struct yarv_signal_thread_list *list;
+
+ // TODO: GET_VM() should not be used
+ GET_VM()->running_thread->interrupt_flag = 1;
+
+#ifndef _WIN32
+ list = signal_thread_list_anchor.next;
+ while(list){
+ native_thread_send_interrupt_signal(list->th);
+ list = list->next;
+ }
+#endif
+}
+
+/*
* for tests
*/
@@ -622,21 +1270,38 @@
void
Init_yarvthread(){
rb_define_global_function("raw_gets", raw_gets, 0);
+
rb_define_singleton_method(cYarvThread, "new", yarv_thread_s_new, -2);
rb_define_singleton_method(cYarvThread, "start", yarv_thread_s_new, -2);
rb_define_singleton_method(cYarvThread, "fork", yarv_thread_s_new, -2);
-
+ rb_define_singleton_method(cYarvThread, "main", yarv_thread_s_main, 0);
rb_define_singleton_method(cYarvThread, "current", yarv_thread_s_current, 0);
+ rb_define_singleton_method(cYarvThread, "stop", rb_thread_stop, 0);
+ rb_define_singleton_method(cYarvThread, "kill", rb_thread_s_kill, 1);
+ rb_define_singleton_method(cYarvThread, "exit", rb_thread_exit, 0);
+ rb_define_singleton_method(cYarvThread, "current", yarv_thread_s_current, 0);
rb_define_singleton_method(cYarvThread, "pass", yarv_thread_s_pass, 0);
+ rb_define_singleton_method(cYarvThread, "list", rb_thread_list, 0);
rb_define_method(cYarvThread, "raise", yarv_thread_raise_m, -1);
rb_define_method(cYarvThread, "join", yarv_thread_join, -1);
rb_define_method(cYarvThread, "value", yarv_thread_value, 0);
+ rb_define_method(cYarvThread, "kill", rb_thread_kill, 0);
+ rb_define_method(cYarvThread, "terminate", rb_thread_kill, 0);
+ rb_define_method(cYarvThread, "exit", rb_thread_kill, 0);
+ rb_define_method(cYarvThread, "run", rb_thread_run, 0);
+ rb_define_method(cYarvThread, "wakeup", rb_thread_wakeup, 0);
rb_define_method(cYarvThread, "[]", rb_thread_aref, 1);
rb_define_method(cYarvThread, "[]=", rb_thread_aset, 2);
rb_define_method(cYarvThread, "key?", rb_thread_key_p, 1);
rb_define_method(cYarvThread, "keys", rb_thread_keys, 0);
-
+ rb_define_method(cYarvThread, "priority", rb_thread_priority, 0);
+ rb_define_method(cYarvThread, "priority=", rb_thread_priority_set, 1);
+ rb_define_method(cYarvThread, "status", rb_thread_status, 0);
+ rb_define_method(cYarvThread, "alive?", rb_thread_alive_p, 0);
+ rb_define_method(cYarvThread, "stop?", rb_thread_stop_p, 0);
+ rb_define_method(cYarvThread, "inspect", rb_thread_inspect, 0);
+
Init_native_thread();
{
/* main thread setting */
Modified: trunk/thread_pthread.h
===================================================================
--- trunk/thread_pthread.h 2006-02-02 15:08:50 UTC (rev 361)
+++ trunk/thread_pthread.h 2006-02-03 23:33:40 UTC (rev 362)
@@ -9,6 +9,10 @@
#define native_mutex_lock pthread_mutex_lock
#define native_mutex_unlock pthread_mutex_unlock
+typedef struct native_thread_data_struct {
+ int dummy;
+} native_thread_data_t;
+
#endif /* THREAD_PTHREAD_H_INCLUDED */
@@ -23,11 +27,17 @@
#define native_cleanup_push pthread_cleanup_push
#define native_cleanup_pop pthread_cleanup_pop
-#define native_thread_join pthread_join
+#define native_thread_join(a, b, c) pthread_join(b, c)
static void
+null_func()
+{
+}
+
+static void
Init_native_thread(){
- /* */
+ GET_THREAD()->thread_id = pthread_self();
+ posix_signal(SIGVTALRM, null_func);
}
static int system_working = 1;
@@ -82,8 +92,46 @@
return err;
}
+static void
+native_thread_apply_priority(yarv_thread_t *th)
+{
+ struct sched_param sp;
+ int policy;
+ int priority = 0 - th->priority;
+ int max, min;
+ pthread_getschedparam(th->thread_id, &policy, &sp);
+ max = sched_get_priority_max(policy);
+ min = sched_get_priority_min(policy);
+
+ if(min < priority){
+ priority = max;
+ }
+ else if(max > priority){
+ priority = min;
+ }
+
+ sp.sched_priority = priority;
+ pthread_setschedparam(th->thread_id, policy, &sp);
+}
+
+static void
+native_thread_send_interrupt_signal(yarv_thread_t *th)
+{
+ thread_debug("native_thread_send_interrupt_signal (%p)\n",
+ th->thread_id);
+ pthread_kill(th->thread_id, SIGVTALRM);
+}
+
+static void
+native_thread_interrupt(yarv_thread_t *th)
+{
+ yarv_add_signal_thread_list(th);
+}
+
static pthread_t time_thread;
+static void timer_function(void);
+
static void*
thread_timer(void *dummy)
{
@@ -100,8 +148,7 @@
tv.tv_usec = 10000;
select(0, NULL, NULL, NULL, &tv);
#endif
- // TODO: GET_VM() should not be used
- GET_VM()->running_thread->interrupt_flag = 1;
+ timer_function();
}
return NULL;
}
Modified: trunk/thread_win32.h
===================================================================
--- trunk/thread_win32.h 2006-02-02 15:08:50 UTC (rev 361)
+++ trunk/thread_win32.h 2006-02-03 23:33:40 UTC (rev 362)
@@ -12,13 +12,14 @@
int native_mutex_unlock(yarv_thread_lock_t *);
void native_mutex_initialize(yarv_thread_lock_t *);
+typedef struct native_thread_data_struct {
+ HANDLE interrupt_event;
+} native_thread_data_t;
+
#endif /* THREAD_WIN32_H_INCLUDED */
-
-
-
/**********************************************************************/
#ifdef THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION
@@ -28,28 +29,47 @@
#define WIN32_WAIT_TIMEOUT 10 /* 10 ms */
#undef Sleep
-static HANDLE interrupted_event;
-
static void
Init_native_thread(){
- interrupted_event
- = CreateEvent(NULL, FALSE, FALSE, NULL);
- GET_THREAD()->thread_id = GetCurrentThread();
+ yarv_thread_t *th = GET_THREAD();
+ 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 system_working = 1;
static int
-kill_func(st_data_t key, st_data_t val, void *dummy){
+kill_func(st_data_t key, st_data_t val, void *dummy)
+{
HANDLE thid = (HANDLE)val;
- if(GetCurrentThread() != thid){
- TerminateThread(thid, 0);
+ yarv_thread_t *th = (void *)key;
+
+ thread_debug("kill_func\n");
+ if(th == th->vm->main_thread){
+ thread_debug("kill_func: main thread - skip (%p)\n", thid);
}
+ else{
+ thread_debug("kill_func: work thread - kill (%p)\n", thid);
+ // TerminateThread(thid, 0);
+ }
return ST_CONTINUE;
}
void
-native_thread_cleanup(yarv_vm_t *vm){
+native_thread_cleanup(yarv_vm_t *vm)
+{
system_working = 0;
st_foreach(vm->living_threads, kill_func, 0);
}
@@ -68,47 +88,80 @@
(LPTSTR) &lpMsgBuf,
0,
NULL);
-
+ // {int *a=0; *a=0;}
MessageBox( NULL, (LPCTSTR)lpMsgBuf, "Error", MB_OK | MB_ICONINFORMATION );
- exit(1);
+ // exit(1);
}
static int
-w32_wait_event(HANDLE event, DWORD timeout)
+w32_wait_event(HANDLE event, DWORD timeout, yarv_thread_t *th)
{
HANDLE events[2];
int count = 0;
DWORD ret;
-
+
if(event){
events[count++] = event;
+ thread_debug(" * handle: %p (count: %d)\n", event, count);
}
- events[count++] = interrupted_event;
+ if(th){
+ HANDLE intr = th->native_thread_data.interrupt_event;
+ ResetEvent(intr);
+ if(th->interrupt_flag){
+ SetEvent(intr);
+ }
+ events[count++] = intr;
+ thread_debug(" * handle: %p (count: %d, intr)\n", intr, count);
+ }
+
+ thread_debug(" WaitForMultipleObjectsEx start (count: %d)\n", count);
ret = WaitForMultipleObjectsEx(count, events, FALSE, timeout, TRUE);
- if(ret == -1){
- w32_show_error_message();
- }
- if (ret == WAIT_OBJECT_0 + count - 1) {
+ thread_debug(" WaitForMultipleObjectsEx end (ret: %d)\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(events[i], &dmy) ? "OK" : "NG");
+ }
+ }
return ret;
}
+static void
+w32_sleep(yarv_thread_t *th, DWORD msec)
+{
+ DWORD ret;
+ thread_debug("w32_sleep start\n");
+ ret = w32_wait_event(0, msec, th);
+ thread_debug("w32_sleep done (%d)\n", ret);
+}
+
int
-native_mutex_lock(yarv_thread_lock_t *lock){
+native_mutex_lock(yarv_thread_lock_t *lock)
+{
DWORD result;
while(1){
- result = w32_wait_event(*lock, INFINITE);
+ thread_debug("native_mutex_lock: %p\n", *lock);
+ result = w32_wait_event(*lock, INFINITE, 0);
switch(result){
case WAIT_OBJECT_0:
/* get mutex object */
- thread_debug("%p acquire mutex: %p\n", GetCurrentThreadId(), *lock);
+ 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:
- /* TODO: check interrupt */
- thread_debug("%p timeout mutex: %p\n", GetCurrentThreadId(), *lock);
+ thread_debug("timeout mutex: %p\n", *lock);
break;
case WAIT_ABANDONED:
rb_bug("win32_mutex_lock: WAIT_ABANDONED");
@@ -122,38 +175,42 @@
}
int
-native_mutex_unlock(yarv_thread_lock_t *lock){
- thread_debug("%p release mutex: %p\n", GetCurrentThreadId(), *lock);
+native_mutex_unlock(yarv_thread_lock_t *lock)
+{
+ thread_debug("release mutex: %p\n", *lock);
return ReleaseMutex(*lock);
}
void
-native_mutex_initialize(yarv_thread_lock_t *lock){
+native_mutex_initialize(yarv_thread_lock_t *lock)
+{
*lock = CreateMutex(NULL, FALSE, NULL);
- thread_debug("%p initialize mutex: %p\n", GetCurrentThreadId(), *lock);
+ // thread_debug("initialize mutex: %p\n", *lock);
}
static int
-native_thread_join(yarv_thread_id_t thid, void *ptr){
+native_thread_join(yarv_thread_t *th, yarv_thread_id_t thid, void *ptr)
+{
DWORD result;
while(1){
- result = w32_wait_event(thid, INFINITE);
+ result = w32_wait_event(thid, INFINITE, th);
switch(result){
case WAIT_OBJECT_0:
/* join */
if(ptr){
GetExitCodeThread(thid, ptr);
}
- thread_debug("%p join to %p\n", GetCurrentThreadId(), thid);
+ thread_debug("join to %p\n", thid);
return 0;
case WAIT_TIMEOUT:
/* TODO: check interrupt */
- thread_debug("%p join timeout (to %p)\n", GetCurrentThreadId(), thid);
+ errno = EINTR;
+ thread_debug("join timeout (to %p)\n", thid);
break;
- default:
- rb_bug("native_thread_join: unknown result (%d)", result);
- break;
+ case -1:
+ errno = EINVAL;
+ return 1;
}
}
return 1;
@@ -169,15 +226,40 @@
yarv_thread_t *th = th_ptr;
VALUE stack_start;
/* run */
- thread_debug("%p (th: %p) thread created\n", GetCurrentThreadId(), th);
+ th->native_thread_data.interrupt_event = CreateEvent(0, TRUE, FALSE, 0);
+
+ 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);
thread_cleanup_func(th);
- thread_debug("%p (th: %p) thread deleted\n", GetCurrentThreadId(), th);
+
+ // native_mutex_unlock(&GET_VM()->global_interpreter_lock);
+
+ thread_debug("close handle - intr: %p, thid: %p\n",
+ th->native_thread_data.interrupt_event,
+ th->thread_id);
+ CloseHandle(th->native_thread_data.interrupt_event);
+ CloseHandle(th->thread_id);
+ thread_debug("thread deleted (th: %p)\n", th);
return 0;
}
static void make_timer_thread();
+static HANDLE
+w32_create_thread(DWORD stack_size, void *func, void *val)
+{
+ HANDLE handle;
+#ifdef __CYGWIN__
+ DWORD dmy;
+ handle = CreateThread(0, stack_size, func, val, 0, &dmy);
+#else
+ handle = (HANDLE)_beginthreadex(
+ 0, stack_size, func, val, 0, 0);
+#endif
+ return handle;
+}
+
static int
native_thread_create(yarv_thread_t *th)
{
@@ -187,28 +269,55 @@
if(!init){
make_timer_thread();
}
-
- if((th->thread_id = (HANDLE)_beginthreadex(0, /* security */
- stack_size,
- thread_start_func_1,
- th,
- 0, /* init flag */
- 0 /* handle */)) == 0){
+
+ if((th->thread_id = w32_create_thread(stack_size, thread_start_func_1, th))
+ == 0){
rb_raise(rb_eThreadError, "can't create Thread (%d)", errno);
}
- thread_debug("%p create: %p (th: %p), stack size: %d\n", GetCurrentThreadId(), th->thread_id, th, stack_size);
+ 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 void
+native_thread_apply_priority(yarv_thread_t *th)
+{
+ int priority = th->priority;
+ if(th->priority > 0){
+ priority = THREAD_PRIORITY_ABOVE_NORMAL;
+ }
+ else if(th->priority < 0){
+ priority = THREAD_PRIORITY_BELOW_NORMAL;
+ }
+ else{
+ priority = THREAD_PRIORITY_NORMAL;
+ }
+
+ SetThreadPriority(th->thread_id, priority);
+}
+
+static void
+native_thread_interrupt(yarv_thread_t *th)
+{
+ SetEvent(th->native_thread_data.interrupt_event);
+}
+
+static void timer_function(void);
+
static HANDLE timer_thread_handle = 0;
static unsigned int _stdcall
timer_thread_func(void *dummy){
- thread_debug("timer_thread: %p\n", GetCurrentThreadId());
+ thread_debug("timer_thread\n");
while(system_working){
Sleep(WIN32_WAIT_TIMEOUT);
- GET_VM()->running_thread->interrupt_flag = 1;
+ timer_function();
}
+ thread_debug("timer killed\n");
return 0;
}
@@ -216,10 +325,7 @@
make_timer_thread(){
if(timer_thread_handle == 0){
timer_thread_handle =
- (HANDLE)_beginthreadex(0,
- 1024,
- timer_thread_func,
- 0, 0, 0);
+ w32_create_thread(1024, timer_thread_func, 0);
}
}
Modified: trunk/vm.c
===================================================================
--- trunk/vm.c 2006-02-02 15:08:50 UTC (rev 361)
+++ trunk/vm.c 2006-02-03 23:33:40 UTC (rev 362)
@@ -1288,8 +1288,9 @@
VALUE type;
err = th->errinfo;
+
if(state == TAG_RAISE){
- rb_ivar_set(err, idThrowState, state);
+ rb_ivar_set(err, idThrowState, INT2FIX(state));
}
exception_handler:
Modified: trunk/yarvcore.c
===================================================================
--- trunk/yarvcore.c 2006-02-02 15:08:50 UTC (rev 361)
+++ trunk/yarvcore.c 2006-02-03 23:33:40 UTC (rev 362)
@@ -1211,3 +1211,4 @@
th_init2(th);
yarv_set_current_running_thread_raw(th);
}
+
Modified: trunk/yarvcore.h
===================================================================
--- trunk/yarvcore.h 2006-02-02 15:08:50 UTC (rev 361)
+++ trunk/yarvcore.h 2006-02-03 23:33:40 UTC (rev 362)
@@ -20,10 +20,10 @@
#include "debug.h"
#include "vm_opts.h"
-#if defined(HAVE_PTHREAD_H)
+#if defined(_WIN32) || defined(__CYGWIN__)
+#include "thread_win32.h"
+#elif defined(HAVE_PTHREAD_H)
#include "thread_pthread.h"
-#elif defined(_WIN32)
-#include "thread_win32.h"
#else
#error "unsupported thread type"
#endif
@@ -180,7 +180,6 @@
struct iseq_label_data* redo_label;
VALUE current_block;
VALUE loopval_popped; /* used by NODE_BREAK */
- VALUE in_ensure; /* used by NODE_RETURN */
VALUE ensure_node;
VALUE for_iseq;
struct iseq_compile_data_ensure_node_stack *ensure_node_stack;
@@ -354,10 +353,11 @@
VALUE *stack; /* must free, must mark*/
unsigned long stack_size;
yarv_control_frame_t *cfp;
-
+
/* passing state */
int state;
-
+ int safe_level;
+
/* for rb_iterate */
yarv_block_t *passed_block;
@@ -375,7 +375,10 @@
/* thread control */
yarv_thread_id_t thread_id;
enum yarv_thread_status status;
+ int priority;
+ native_thread_data_t native_thread_data;
+
VALUE value;
VALUE wait_thread_value;
@@ -390,6 +393,8 @@
int tail;
} signal_queue;
+ void *signal_thread_list;
+
VALUE first_proc;
VALUE first_args;
@@ -578,7 +583,6 @@
#define YARV_CHECK_INTS_TH(th) do { \
if(th->interrupt_flag){ \
- th->interrupt_flag = 0; \
/* TODO: trap something event */ \
yarv_thread_execute_interrupts(th); \
} \
Modified: trunk/yarvtest/test_class.rb
===================================================================
--- trunk/yarvtest/test_class.rb 2006-02-02 15:08:50 UTC (rev 361)
+++ trunk/yarvtest/test_class.rb 2006-02-03 23:33:40 UTC (rev 362)
@@ -509,7 +509,7 @@
end
def test_reopen_not_class
- ae %q{
+ ae %q{ # [yarv-dev:782]
begin
B = 1
class B
@@ -519,6 +519,16 @@
e.message
end
}
+ ae %q{ # [yarv-dev:800]
+ begin
+ B = 1
+ module B
+ p B
+ end
+ rescue TypeError => e
+ e.message
+ end
+ }
end
end
Added: trunk/yarvtest/test_thread.rb
===================================================================
--- trunk/yarvtest/test_thread.rb 2006-02-02 15:08:50 UTC (rev 361)
+++ trunk/yarvtest/test_thread.rb 2006-02-03 23:33:40 UTC (rev 362)
@@ -0,0 +1,123 @@
+
+require 'yarvtest/yarvtest'
+
+class TestThread < YarvTestBase
+ def test_create
+ ae %q{
+ Thread.new{
+ }.join
+ :ok
+ }
+ ae %q{
+ Thread.new{
+ :ok
+ }.value
+ }
+ end
+
+ def test_create_many_threads
+ ae %q{
+ v = 0
+ (1..200).map{|i|
+ Thread.new{
+ i
+ }
+ }.each{|t|
+ v += t.value
+ }
+ v
+ }
+ ae %q{
+ 10000.times{|e|
+ (1..2).map{
+ Thread.new{
+ }
+ }.each{|e|
+ e.join
+ }
+ }
+ }
+ end
+
+ def test_raise
+ ae %q{
+ t = Thread.new{
+ sleep
+ }
+ sleep 0.1
+ t.raise
+ begin
+ t.join
+ :ng
+ rescue
+ :ok
+ end
+ }
+ ae %q{
+ t = Thread.new{
+ loop{}
+ }
+ Thread.pass
+ t.raise
+ begin
+ t.join
+ :ng
+ rescue
+ :ok
+ end
+ }
+ ae %q{
+ t = Thread.new{
+ }
+ Thread.pass
+ t.join
+ t.raise # raise to exited thread
+ begin
+ t.join
+ :ok
+ rescue
+ :ng
+ end
+ }
+ end
+
+ def test_status
+ ae %q{
+ t = Thread.new{
+ loop{}
+ }
+ st = t.status
+ t.kill
+ st
+ }
+ ae %q{
+ t = Thread.new{
+ sleep
+ }
+ sleep 0.1
+ st = t.status
+ t.kill
+ st
+ }
+ ae %q{
+ t = Thread.new{
+ }
+ t.kill
+ sleep 0.1
+ t.status
+ }
+ end
+
+ def test_tlv
+ ae %q{
+ Thread.current[:a] = 1
+ Thread.new{
+ Thread.current[:a] = 10
+ Thread.pass
+ Thread.current[:a]
+ }.value + Thread.current[:a]
+ }
+ end
+
+end
+
--
ML: yarv-diff quickml.atdot.net
Info: http://www.atdot.net/~ko1/quickml