Tietew です。

表題の通りなんですが,現状プロセスグローバルな $std{in,out,err} 
の,スレッドローカルな奴が欲しいのです。背景としては,同じプロセ
ス上で互いに無関係な ruby スクリプトがスレッドを分けた上で同時に
実行され得るアプリ(Becky! のプラグインなんですが)を開発してい
て,$stdout を切り替えたときにそれが(一時的でも)全体に波及する
のを防ぎたいという意図です。

本当は $KCODE もスレッドローカルが欲しいんですけど,難しそうなの
と,/re/[esn] でいちおう代用できるので,保留。
他のグローバル変数は「使うな」で逃げられるし,もう非推奨なのでパ
ス。

後方互換性確保のために Thread#separated_stdio フラグが真になった
スレッド(および派生スレッド)のみ分離 stdio になるようにするパッ
チ。struct thread のメンバに _save が付いているのは,stdout 等が
マクロの環境 (VC++) があるため。

--- eval.c.orig	2003-09-11 14:30:42.000000000 +0900
+++ eval.c	2003-09-17 16:23:15.000000000 +0900
@@ -7880,4 +7880,9 @@ struct thread {
 
     VALUE thread;
+
+    int separated_stdio;
+    VALUE stdout_save;
+    VALUE stderr_save;
+    VALUE stdin_save;
 };
 
@@ -8089,4 +8094,8 @@ thread_mark(th)
     rb_mark_tbl(th->locals);
 
+    rb_gc_mark(th->stdout_save);
+    rb_gc_mark(th->stderr_save);
+    rb_gc_mark(th->stdin_save);
+
     /* mark data in copied stack */
     if (th == curr_thread) return;
@@ -8249,4 +8258,13 @@ rb_thread_save_context(th)
     th->last_match = tval;
     th->safe = ruby_safe_level;
+    if(th->separated_stdio) {
+	th->stdout_save = rb_stdout;
+	th->stderr_save = rb_stderr;
+	th->stdin_save = rb_stdin;
+    } else {
+	main_thread->stdout_save = rb_stdout;
+	main_thread->stderr_save = rb_stderr;
+	main_thread->stdin_save = rb_stdin;
+    }
 
     th->node = ruby_current_node;
@@ -8357,4 +8375,13 @@ rb_thread_restore_context(th, exit)
     rb_last_status = th->last_status;
     ruby_safe_level = th->safe;
+    if(th->separated_stdio) {
+	rb_stdout = th->stdout_save;
+	rb_stderr = th->stderr_save;
+	rb_stdin = th->stdin_save;
+    } else {
+	rb_stdout = main_thread->stdout_save;
+	rb_stderr = main_thread->stderr_save;
+	rb_stdin = main_thread->stdin_save;
+    }
 
     ruby_current_node = th->node;
@@ -9259,4 +9286,28 @@ rb_thread_group(thread)
 }
 
+static VALUE
+rb_thread_separated_stdio(thread)
+    VALUE thread;
+{
+    rb_thread_t th;
+    
+    th = rb_thread_check(thread);
+    return th->separated_stdio ? Qtrue : Qfalse;
+}
+
+static VALUE
+rb_thread_separated_stdio_set(thread, sep)
+    VALUE thread, sep;
+{
+    rb_thread_t th;
+    
+    th = rb_thread_check(thread);
+    if(th->separated_stdio && !RTEST(sep) && curr_thread != main_thread) {
+	th->stdout_save = th->stderr_save = th->stdin_save = Qnil;
+	rb_stdout = main_thread->stdout_save;
+	rb_stderr = main_thread->stderr_save;
+	rb_stdin = main_thread->stdin_save;
+    }
+    th->separated_stdio = RTEST(sep);
+    return sep;
+}
+
 #ifdef __ia64__
 # define IA64_INIT(x) x
@@ -9305,4 +9356,8 @@ rb_thread_group(thread)
     th->thgroup = thgroup_default;\
     th->locals = 0;\
+    th->separated_stdio = 0;\
+    th->stdout_save = Qnil;\
+    th->stderr_save = Qnil;\
+    th->stdin_save = Qnil;\
 } while (0)
 
@@ -9422,4 +9477,5 @@ rb_thread_start_0(fn, arg, th_arg)
 	th->priority = curr_thread->priority;
 	th->thgroup = curr_thread->thgroup;
+	th->separated_stdio = curr_thread->separated_stdio;
     }
 
@@ -10104,4 +10160,7 @@ Init_Thread()
     rb_define_method(rb_cThread, "group", rb_thread_group, 0);
 
+    rb_define_method(rb_cThread, "separated_stdio", rb_thread_separated_stdio, 0);
+    rb_define_method(rb_cThread, "separated_stdio=", rb_thread_separated_stdio_set, 1);
+
     rb_define_method(rb_cThread, "[]", rb_thread_aref, 1);
     rb_define_method(rb_cThread, "[]=", rb_thread_aset, 2);


―[ Tietew ]――――――――――――――――――――――――――
 メ : tietew / tietew.net / tietew / raug.net / tietew / masuclub.net
ホペ: http://www.tietew.net/     Tietew Windows Lab.
      http://www.masuclub.net/   鱒倶楽部
指紋: 26CB 71BB B595 09C4 0153  81C4 773C 963A D51B 8CAA