[ruby-dev:14805] Re: [BUG] $_ on thread switching
From:
nobu.nakada@...
Date:
2001-09-19 10:19:53 UTC
List:
ruby-dev #14805
なかだです。
At Tue, 18 Sep 2001 12:43:10 +0900,
matz@ruby-lang.org (Yukihiro Matsumoto) wrote:
> | dyna_varsもたしか一度ちょっと考えたような気がしますが、なんで
> |やめたんだったか覚えてません。やっぱりそっちの方がいいでしょう
> |か。そうした場合、FLIPの方は適当にIDを割り振るようにするとか?
>
> たぶん。FLIPについては現在cntを使っている部分を「適当なID」
> にすることで対応するのではないかと思います。
だいたいうまく行くような感じ。相変わらずでかいパッチですいま
せんが。
IDの種類にID_INTERNALというのを追加してます。それとrb_svar()
というのはローカル変数のインデックスから変数への参照を返す関数。
たぶん、SCOPE_SHAREDは不要になると思うんですがどうでしょう。
想定してるテストケースはこんなの。
class TestScope < Rubicon::TestCase
# $_ is a local variable but not be shared by threads.
def wakeup(t)
t.run
end
private :wakeup
def test_scope_uscore_string
sub = proc {|x| "#{x, $_ = $_, x; x}"}
assert_equal("", sub.call("first"))
assert_equal("first", sub.call("second"))
end
def test_threadscope_uscore
# shares local scope
t = Thread.start do
$_ = "sub"
loop do
Thread.stop
assert_equal("sub", $_)
end
end
begin
$_ = "main"
# another thread may change?
t.run
assert_equal("main", $_)
ensure
t.kill
t.join
end
end
def test_threadscope_uscore_sub
t = Thread.start do
$_ = "sub"
loop do
Thread.stop
assert_equal("sub", $_)
end
end
begin
$_ = "main"
# another thread waked in function may change?
wakeup(t)
assert_equal("main", $_)
ensure
t.kill
t.join
end
end
def test_threadscope_uscore_main
t = Thread.new(Thread.current) do |main|
$_ = "sub"
loop do
assert_equal("sub", $_)
Thread.stop
wakeup(main)
end
end
begin
$_ = "main"
# another thread waked in function may change while this thread
# is absent this scope?
wakeup(t)
assert_equal("main", $_)
ensure
t.kill
t.join
end
end
def test_threadscope_uscore_proc
sub = nil
Thread.start do
$_ = "sub"
sub = proc {$_}
end.join
$_ = "main"
assert_equal("main", $_)
assert_equal("sub", sub.call)
assert_equal("main", $_)
end
def test_threadscope_local
sub = nil
Thread.start do
var = "sub"
sub = proc {var}
end.join
var = "main"
assert_equal("main", var)
assert_equal("sub", sub.call)
assert_equal("main", var)
end
def test_threadscope_shared
sub = nil
var = "main"
Thread.start do
sub = proc {
tmp, var = var, "sub"
tmp
}
end.join
assert_equal("main", sub.call)
assert_equal("sub", var)
end
end
class TestFlip < Rubicon::TestCase
def test_flip2_while
result = []
i = 0
while i < 5
result << (i if (i==1)..(i==3))
i += 1
end
assert_equal([nil,1,2,3,nil], result)
end
def test_flip2_for
result = []
for i in (0...5)
result << (i if (i==1)..(i==3))
end
assert_equal([nil,1,2,3,nil], result)
end
def test_flip2_proc
sub = proc {|x| x if (x==1)..(x==3)}
assert_nil(sub.call(0))
assert_equal(1, sub.call(1))
assert_equal(2, sub.call(2))
assert_equal(3, sub.call(3))
assert_nil(sub.call(3))
assert_nil(sub.call(4))
end
def test_flip2_eval
sub = eval("proc {|x| x if (x==1)..(x==3)}")
assert_nil(sub.call(0))
assert_equal(1, sub.call(1))
assert_equal(2, sub.call(2))
assert_equal(3, sub.call(3))
assert_nil(sub.call(3))
assert_nil(sub.call(4))
end
def test_flip2_string
sub = proc {|x| "#{x if (x==1)..(x==3)}"}
assert_equal("", sub.call(0))
assert_equal("1", sub.call(1))
assert_equal("2", sub.call(2))
assert_equal("3", sub.call(3))
assert_equal("", sub.call(3))
assert_equal("", sub.call(4))
end
def test_flip2_thread
sub = []
2.times do |i|
Thread.start do
sub << proc do |x|
x if (x==1)...(x==3)
end
end.join
end
assert_nil(sub[0].call(0))
assert_equal(1, sub[0].call(1))
assert_nil(sub[1].call(2))
assert_nil(sub[1].call(3))
assert_equal(1, sub[1].call(1))
assert_equal(3, sub[0].call(3))
assert_nil(sub[0].call(3))
assert_equal(3, sub[1].call(3))
end
end
Index: eval.c
===================================================================
RCS file: /cvs/ruby/src/ruby/eval.c,v
retrieving revision 1.204
diff -u -2 -p -r1.204 eval.c
--- eval.c 2001/09/19 06:54:10 1.204
+++ eval.c 2001/09/19 10:10:29
@@ -722,4 +722,22 @@ dvar_asgn_curr(id, value)
}
+VALUE *
+rb_svar(cnt)
+ int cnt;
+{
+ struct RVarmap *vars = ruby_dyna_vars;
+ ID id;
+
+ if (!ruby_scope->local_tbl) return NULL;
+ if (cnt >= ruby_scope->local_tbl[0]) return NULL;
+ id = ruby_scope->local_tbl[cnt+1];
+ while (vars) {
+ if (vars->id == id) return &vars->val;
+ vars = vars->next;
+ }
+ if (ruby_scope->local_vars == 0) return NULL;
+ return &ruby_scope->local_vars[cnt];
+}
+
struct iter {
int iter;
@@ -2537,38 +2555,39 @@ rb_eval(self, n)
case NODE_FLIP2: /* like AWK */
- if (ruby_scope->local_vars == 0) {
- rb_bug("unexpected local variable");
- }
- if (!RTEST(ruby_scope->local_vars[node->nd_cnt])) {
- if (RTEST(rb_eval(self, node->nd_beg))) {
- ruby_scope->local_vars[node->nd_cnt] =
- RTEST(rb_eval(self, node->nd_end))?Qfalse:Qtrue;
- result = Qtrue;
+ {
+ VALUE *flip = rb_svar(node->nd_cnt);
+ if (!flip) rb_bug("unexpected local variable");
+ if (!RTEST(*flip)) {
+ if (RTEST(rb_eval(self, node->nd_beg))) {
+ *flip = RTEST(rb_eval(self, node->nd_end))?Qfalse:Qtrue;
+ result = Qtrue;
+ }
+ else {
+ result = Qfalse;
+ }
}
else {
- result = Qfalse;
- }
- }
- else {
- if (RTEST(rb_eval(self, node->nd_end))) {
- ruby_scope->local_vars[node->nd_cnt] = Qfalse;
+ if (RTEST(rb_eval(self, node->nd_end))) {
+ *flip = Qfalse;
+ }
+ result = Qtrue;
}
- result = Qtrue;
}
break;
case NODE_FLIP3: /* like SED */
- if (ruby_scope->local_vars == 0) {
- rb_bug("unexpected local variable");
- }
- if (!RTEST(ruby_scope->local_vars[node->nd_cnt])) {
- result = RTEST(rb_eval(self, node->nd_beg)) ? Qtrue : Qfalse;
- ruby_scope->local_vars[node->nd_cnt] = result;
- }
- else {
- if (RTEST(rb_eval(self, node->nd_end))) {
- ruby_scope->local_vars[node->nd_cnt] = Qfalse;
+ {
+ VALUE *flip = rb_svar(node->nd_cnt);
+ if (!flip) rb_bug("unexpected local variable");
+ if (!RTEST(*flip)) {
+ result = RTEST(rb_eval(self, node->nd_beg)) ? Qtrue : Qfalse;
+ *flip = result;
+ }
+ else {
+ if (RTEST(rb_eval(self, node->nd_end))) {
+ *flip = Qfalse;
+ }
+ result = Qtrue;
}
- result = Qtrue;
}
break;
@@ -5853,6 +5872,8 @@ rb_f_local_variables()
n = *tbl++;
for (i=2; i<n; i++) { /* skip first 2 ($_ and $~) */
- if (tbl[i] == 0) continue; /* skip flip states */
- rb_ary_push(ary, rb_str_new2(rb_id2name(tbl[i])));
+ ID id = tbl[i];
+ if (id == 0 || rb_is_internal_id(id)) /* skip flip states */
+ continue;
+ rb_ary_push(ary, rb_str_new2(rb_id2name(id)));
}
}
@@ -6415,4 +6436,5 @@ proc_invoke(proc, args, pcall)
volatile int safe = ruby_safe_level;
volatile VALUE old_wrapper = ruby_wrapper;
+ struct RVarmap * volatile old_dvars = ruby_dyna_vars;
if (rb_block_given_p() && ruby_frame->last_func) {
@@ -6426,4 +6448,5 @@ proc_invoke(proc, args, pcall)
ruby_wrapper = data->wrapper;
+ ruby_dyna_vars = data->dyna_vars;
/* PUSH BLOCK from data */
old_block = ruby_block;
@@ -6451,4 +6474,5 @@ proc_invoke(proc, args, pcall)
ruby_block = old_block;
ruby_wrapper = old_wrapper;
+ ruby_dyna_vars = old_dvars;
ruby_safe_level = safe;
@@ -8255,6 +8279,4 @@ int rb_thread_tick = THREAD_TICK;
#endif
-#define SCOPE_SHARED FL_USER1
-
#if defined(HAVE_SETITIMER)
static int thread_init = 0;
@@ -8322,5 +8344,4 @@ rb_thread_start_0(fn, arg, th_arg)
}
scope_dup(ruby_scope);
- FL_SET(ruby_scope, SCOPE_SHARED);
if (!th->next) {
@@ -8398,10 +8419,4 @@ rb_thread_create(fn, arg)
}
-int
-rb_thread_scope_shared_p()
-{
- return FL_TEST(ruby_scope, SCOPE_SHARED);
-}
-
static VALUE
rb_thread_yield(arg, th)
@@ -8409,5 +8424,21 @@ rb_thread_yield(arg, th)
rb_thread_t th;
{
+ ID *tbl;
+
scope_dup(ruby_block->scope);
+
+ tbl = ruby_scope->local_tbl;
+ if (tbl) {
+ int n = *tbl++;
+ for (tbl += 2, n -= 2; n > 0; --n) { /* skip first 2 ($_ and $~) */
+ ID id = *tbl++;
+ if (id != 0 && rb_is_internal_id(id)) /* push flip states */
+ rb_dvar_push(id, Qfalse);
+ }
+ }
+ rb_dvar_push('_', Qnil);
+ rb_dvar_push('~', Qnil);
+ ruby_block->dyna_vars = ruby_dyna_vars;
+
return rb_yield_0(mvalue_to_svalue(arg), 0, 0, Qtrue);
}
Index: intern.h
===================================================================
RCS file: /cvs/ruby/src/ruby/intern.h,v
retrieving revision 1.64
diff -u -2 -p -r1.64 intern.h
--- intern.h 2001/09/05 22:31:07 1.64
+++ intern.h 2001/09/19 10:11:03
@@ -134,4 +134,5 @@ VALUE rb_dvar_ref _((ID));
void rb_dvar_asgn _((ID, VALUE));
void rb_dvar_push _((ID, VALUE));
+VALUE *rb_svar _((int));
VALUE rb_eval_cmd _((VALUE, VALUE));
int rb_respond_to _((VALUE, ID));
@@ -171,5 +172,4 @@ VALUE rb_thread_wakeup _((VALUE));
VALUE rb_thread_run _((VALUE));
VALUE rb_thread_create _((VALUE (*)(ANYARGS), void*));
-int rb_thread_scope_shared_p _((void));
void rb_thread_interrupt _((void));
void rb_thread_trap_eval _((VALUE, int));
Index: parse.y
===================================================================
RCS file: /cvs/ruby/src/ruby/parse.y,v
retrieving revision 1.120
diff -u -2 -p -r1.120 parse.y
--- parse.y 2001/09/19 06:54:10 1.120
+++ parse.y 2001/09/19 09:55:44
@@ -24,4 +24,5 @@
#define ID_SCOPE_SHIFT 3
#define ID_SCOPE_MASK 0x07
+#define ID_INTERNAL 0x00
#define ID_LOCAL 0x01
#define ID_INSTANCE 0x02
@@ -33,4 +34,5 @@
#define is_notop_id(id) ((id)>LAST_TOKEN)
+#define is_internal_id(id) (is_notop_id(id)&&((id)&ID_SCOPE_MASK)==ID_INTERNAL)
#define is_local_id(id) (is_notop_id(id)&&((id)&ID_SCOPE_MASK)==ID_LOCAL)
#define is_global_id(id) (is_notop_id(id)&&((id)&ID_SCOPE_MASK)==ID_GLOBAL)
@@ -139,4 +141,5 @@ static int local_cnt();
static int local_id();
static ID *local_tbl();
+static ID internal_id();
static struct RVarmap *dyna_push();
@@ -4627,5 +4630,5 @@ cond0(node)
if (type == NODE_DOT2) nd_set_type(node,NODE_FLIP2);
else if (type == NODE_DOT3) nd_set_type(node, NODE_FLIP3);
- node->nd_cnt = local_append(0);
+ node->nd_cnt = local_append(internal_id());
warning_unless_e_option("range literal in condition");
break;
@@ -5004,9 +5007,16 @@ Init_sym()
}
+static ID last_id = LAST_TOKEN;
+
+static ID
+internal_id()
+{
+ return ID_INTERNAL | (++last_id << ID_SCOPE_SHIFT);
+}
+
ID
rb_intern(name)
const char *name;
{
- static ID last_id = LAST_TOKEN;
const char *m = name;
ID id;
@@ -5163,4 +5173,12 @@ rb_is_instance_id(id)
}
+int
+rb_is_internal_id(id)
+ ID id;
+{
+ if (is_internal_id(id)) return Qtrue;
+ return Qfalse;
+}
+
static void
special_local_set(c, val)
@@ -5176,9 +5194,12 @@ special_local_set(c, val)
}
+VALUE *rb_svar _((int cnt));
+
VALUE
rb_backref_get()
{
- if (ruby_scope->local_vars) {
- return ruby_scope->local_vars[1];
+ VALUE *var = rb_svar(1);
+ if (var) {
+ return *var;
}
return Qnil;
@@ -5189,6 +5210,7 @@ rb_backref_set(val)
VALUE val;
{
- if (ruby_scope->local_vars) {
- ruby_scope->local_vars[1] = val;
+ VALUE *var = rb_svar(1);
+ if (var) {
+ *var = val;
}
else {
@@ -5200,6 +5222,7 @@ VALUE
rb_lastline_get()
{
- if (ruby_scope->local_vars) {
- return ruby_scope->local_vars[0];
+ VALUE *var = rb_svar(0);
+ if (var) {
+ return *var;
}
return Qnil;
@@ -5210,6 +5233,7 @@ rb_lastline_set(val)
VALUE val;
{
- if (ruby_scope->local_vars) {
- ruby_scope->local_vars[0] = val;
+ VALUE *var = rb_svar(0);
+ if (var) {
+ *var = val;
}
else {
Index: re.c
===================================================================
RCS file: /cvs/ruby/src/ruby/re.c,v
retrieving revision 1.45
diff -u -2 -p -r1.45 re.c
--- re.c 2001/09/05 06:54:53 1.45
+++ re.c 2001/09/19 10:10:10
@@ -646,10 +646,5 @@ rb_reg_search(re, str, pos, reverse)
}
- if (rb_thread_scope_shared_p()) {
- match = Qnil;
- }
- else {
- match = rb_backref_get();
- }
+ match = rb_backref_get();
if (NIL_P(match) || FL_TEST(match, MATCH_BUSY)) {
match = match_alloc();
--
--- 僕の前にBugはない。
--- 僕の後ろにBugはできる。
中田 伸悦