永井@知能.九工大です.

From: Hidetoshi NAGAI <nagai / ai.kyutech.ac.jp>
Subject: [ruby-dev:25578] Re: some problems on ext/tk/sample/**/*.rb
Date: Fri, 28 Jan 2005 12:16:58 +0900
Message-ID: <20050128.121645.74737034.nagai / ai.kyutech.ac.jp>
> > 問題は「なぜ bcc32 では namespace が変化するのか」ですが・・・
> 
> スレッド切り替えのタイミングということなのかもしれませんが,
> わからないですね.
> たまたま bcc32 では発生しやすいだけという可能性もあります.
> 
> とはいえ,これまで生じていた SEGV のかなりの部分の理由が
> 判明したということで,とりあえずはほっとしています.

関数呼び出しをするとスレッド切り替えが発生して 
namespace が破壊される危険が高まる可能性があると考え,
チェックをマクロで定義するようにしたパッチです.
問題がなければこれを今回の問題に対する修正の最終版として
commit したいと思います.

diff -urN --exclude=CVS tk.orig3/lib/multi-tk.rb tk/lib/multi-tk.rb
--- tk.orig3/lib/multi-tk.rb	2005-01-25 13:10:40.000000000 +0900
+++ tk/lib/multi-tk.rb	2005-01-28 12:24:21.000000000 +0900
@@ -1568,8 +1568,8 @@
     __getip.deleted?
   end
 
-  def null_namespace?
-    __getip.null_namespace?
+  def invalid_namespace?
+    __getip.invalid_namespace?
   end
 
   def abort(msg = nil)
@@ -1894,8 +1894,8 @@
     @interp.deleted?
   end
 
-  def null_namespace?
-    @interp.null_namespace?
+  def invalid_namespace?
+    @interp.invalid_namespace?
   end
 
   def abort(msg = nil)
diff -urN --exclude=CVS tk.orig3/lib/remote-tk.rb tk/lib/remote-tk.rb
--- tk.orig3/lib/remote-tk.rb	2005-01-25 13:10:54.000000000 +0900
+++ tk/lib/remote-tk.rb	2005-01-28 12:24:29.000000000 +0900
@@ -282,7 +282,7 @@
     end
   end
 
-  def null_namespace?
+  def invalid_namespace?
     false
   end
 
diff -urN --exclude=CVS tk.orig3/lib/tk.rb tk/lib/tk.rb
--- tk.orig3/lib/tk.rb	2005-01-25 14:09:18.000000000 +0900
+++ tk/lib/tk.rb	2005-01-28 14:16:38.000000000 +0900
@@ -3940,7 +3940,7 @@
 #Tk.freeze
 
 module Tk
-  RELEASE_DATE = '2005-01-25'.freeze
+  RELEASE_DATE = '2005-01-28'.freeze
 
   autoload :AUTO_PATH,        'tk/variable'
   autoload :TCL_PACKAGE_PATH, 'tk/variable'
diff -urN --exclude=CVS tk.orig3/tcltklib.c tk/tcltklib.c
--- tk.orig3/tcltklib.c	2005-01-28 11:21:52.000000000 +0900
+++ tk/tcltklib.c	2005-01-28 16:04:13.000000000 +0900
@@ -4,7 +4,7 @@
  *              Oct. 24, 1997   Y. Matsumoto
  */
 
-#define TCLTKLIB_RELEASE_DATE "2005-01-27"
+#define TCLTKLIB_RELEASE_DATE "2005-01-28"
 
 #include "ruby.h"
 #include "rubysig.h"
@@ -204,15 +204,17 @@
 static int ip_ruby_cmd _((ClientData, Tcl_Interp *, int, char **));
 #endif
 
-static int ip_null_namespace _((Tcl_Interp *));
-
 #if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION < 5
+/* Tcl7.x doesn't have namespace support.                            */
+/* Tcl8.5+ has definition of Tcl_GetCurrentNamespace() in tclDecls.h */
 #  ifndef Tcl_GetCurrentNamespace
 EXTERN Tcl_Namespace *  Tcl_GetCurrentNamespace _((Tcl_Interp *));
 #  endif
 #  if defined(USE_TCL_STUBS) && !defined(USE_TCL_STUB_PROCS)
 #    ifndef Tcl_GetCurrentNamespace
-#define FunctionNum_of_GetCurrentNamespace 124
+#      ifndef FunctionNum_of_GetCurrentNamespace
+#        define FunctionNum_of_GetCurrentNamespace 124
+#      endif
 struct DummyTclIntStubs {
   int magic;
   struct TclIntStubHooks *hooks;
@@ -251,6 +253,25 @@
     return ptr;
 }
 
+
+/* namespace check */
+/* ip_null_namespace(Tcl_Interp *interp) */
+#if TCL_MAJOR_VERSION < 8
+#define ip_null_namespace(interp) (0)
+#else /* support namespace */
+#define ip_null_namespace(interp) \
+    (Tcl_GetCurrentNamespace(interp) == (Tcl_Namespace *)NULL)
+#endif
+
+/* rbtk_invalid_namespace(tcltkip *ptr) */
+#if TCL_MAJOR_VERSION < 8
+#define rbtk_invalid_namespace(ptr) (0)
+#else /* support namespace */
+#define rbtk_invalid_namespace(ptr) \
+    ((ptr)->default_ns == (Tcl_Namespace*)NULL || Tcl_GetCurrentNamespace((ptr)->ip) != (ptr)->default_ns)
+#endif
+
+
 /* increment/decrement reference count of tcltkip */
 static int
 rbtk_preserve_ip(ptr)
@@ -3402,9 +3423,7 @@
 
         DUMP2("IP ref_count = %d", ptr->ref_count);
 
-        if (!Tcl_InterpDeleted(ptr->ip) && 
-            !ip_null_namespace(ptr->ip) && 
-            Tcl_GetCurrentNamespace(ptr->ip) == ptr->default_ns) {
+        if (!Tcl_InterpDeleted(ptr->ip) && !rbtk_invalid_namespace(ptr)) {
             DUMP2("IP(%lx) is not deleted", ptr->ip);
             /* Tcl_Preserve(ptr->ip); */
             rbtk_preserve_ip(ptr);
@@ -3413,20 +3432,16 @@
 
             Tcl_ResetResult(ptr->ip);
 
-            if (!Tcl_InterpDeleted(ptr->ip) && !ip_null_namespace(ptr->ip) && 
-                Tcl_GetCurrentNamespace(ptr->ip) == ptr->default_ns && 
-                Tcl_GetCommandInfo(ptr->ip, finalize_hook_name, &info)) {
+            if (!Tcl_InterpDeleted(ptr->ip) && !rbtk_invalid_namespace(ptr)
+                && Tcl_GetCommandInfo(ptr->ip, finalize_hook_name, &info)) {
                 DUMP2("call finalize hook proc '%s'", finalize_hook_name);
                 Tcl_Eval(ptr->ip, finalize_hook_name);
             }
 
-            if (!Tcl_InterpDeleted(ptr->ip) && 
-                Tcl_GetCurrentNamespace(ptr->ip) == ptr->default_ns && 
-                Tcl_Eval(ptr->ip, DEF_CANCEL_AFTER_SCRIPTS_PROC) == TCL_OK) {
-                if (!Tcl_InterpDeleted(ptr->ip) && 
-                    !ip_null_namespace(ptr->ip) && 
-                    Tcl_GetCurrentNamespace(ptr->ip) == ptr->default_ns && 
-                    Tcl_GetCommandInfo(ptr->ip, CANCEL_AFTER_SCRIPTS, &info)) {
+            if (!Tcl_InterpDeleted(ptr->ip) && !rbtk_invalid_namespace(ptr)
+                && Tcl_Eval(ptr->ip, DEF_CANCEL_AFTER_SCRIPTS_PROC) == TCL_OK) {
+                if (!Tcl_InterpDeleted(ptr->ip) && !rbtk_invalid_namespace(ptr)
+                    && Tcl_GetCommandInfo(ptr->ip, CANCEL_AFTER_SCRIPTS, &info)) {
                     DUMP2("call cancel after scripts proc '%s'", 
                           CANCEL_AFTER_SCRIPTS);
                     Tcl_Eval(ptr->ip, CANCEL_AFTER_SCRIPTS);
@@ -3497,8 +3512,13 @@
         rb_raise(rb_eRuntimeError, "fail to create a new Tk interpreter");
     }
 
+#if TCL_MAJOR_VERSION >= 8
     DUMP1("get current namespace");
-    ptr->default_ns = Tcl_GetCurrentNamespace(ptr->ip);
+    if ((ptr->default_ns = Tcl_GetCurrentNamespace(ptr->ip)) 
+        == (Tcl_Namespace*)NULL) {
+      rb_raise(rb_eRuntimeError, "a new Tk interpreter has a NULL namespace");
+    }
+#endif
 
     rbtk_preserve_ip(ptr);
     DUMP2("IP ref_count = %d", ptr->ref_count);
@@ -3741,7 +3761,9 @@
         rb_thread_critical = thr_crit_bup;
         rb_raise(rb_eRuntimeError, "fail to create the new slave interpreter");
     }
+#if TCL_MAJOR_VERSION >= 8
     slave->default_ns = Tcl_GetCurrentNamespace(slave->ip);
+#endif
     rbtk_preserve_ip(slave);
 
     slave->has_orig_exit 
@@ -3909,19 +3931,16 @@
     delete_slaves(ptr->ip);
 
     DUMP1("finalize operation");
-    if (!Tcl_InterpDeleted(ptr->ip) && !ip_null_namespace(ptr->ip) && 
-        Tcl_GetCurrentNamespace(ptr->ip) == ptr->default_ns && 
-        Tcl_GetCommandInfo(ptr->ip, finalize_hook_name, &info)) {
+    if (!Tcl_InterpDeleted(ptr->ip) && !rbtk_invalid_namespace(ptr)
+        && Tcl_GetCommandInfo(ptr->ip, finalize_hook_name, &info)) {
         DUMP2("call finalize hook proc '%s'", finalize_hook_name);
         Tcl_Eval(ptr->ip, finalize_hook_name);
     }
 
-    if (!Tcl_InterpDeleted(ptr->ip) && 
-        Tcl_GetCurrentNamespace(ptr->ip) == ptr->default_ns && 
-        Tcl_Eval(ptr->ip, DEF_CANCEL_AFTER_SCRIPTS_PROC) == TCL_OK) {
-        if (!Tcl_InterpDeleted(ptr->ip) && !ip_null_namespace(ptr->ip) && 
-            Tcl_GetCurrentNamespace(ptr->ip) == ptr->default_ns && 
-            Tcl_GetCommandInfo(ptr->ip, CANCEL_AFTER_SCRIPTS, &info)) {
+    if (!Tcl_InterpDeleted(ptr->ip) && !rbtk_invalid_namespace(ptr)
+        && Tcl_Eval(ptr->ip, DEF_CANCEL_AFTER_SCRIPTS_PROC) == TCL_OK) {
+        if (!Tcl_InterpDeleted(ptr->ip) && !rbtk_invalid_namespace(ptr)
+            && Tcl_GetCommandInfo(ptr->ip, CANCEL_AFTER_SCRIPTS, &info)) {
             DUMP2("call cancel after scripts proc '%s'", 
                   CANCEL_AFTER_SCRIPTS);
             Tcl_Eval(ptr->ip, CANCEL_AFTER_SCRIPTS);
@@ -3944,25 +3963,13 @@
 }
 
 /* is deleted? */
-static int
-ip_null_namespace(interp)
-    Tcl_Interp *interp;
-{
-#if TCL_MAJOR_VERSION < 8
-    return 0;
-#else /* support Namespace */
-    DUMP2("current namespace %lx",Tcl_GetCurrentNamespace(interp));
-    return ( Tcl_GetCurrentNamespace(interp) == (Tcl_Namespace *)NULL );
-#endif
-}
-
 static VALUE
-ip_has_null_namespace_p(self)
+ip_has_invalid_namespace_p(self)
     VALUE self;
 {
     struct tcltkip *ptr = get_ip(self);
 
-    if (ip_null_namespace(ptr->ip)) {
+    if (rbtk_invalid_namespace(ptr)) {
         return Qtrue;
     } else {
         return Qfalse;
@@ -3975,7 +3982,7 @@
 {
     struct tcltkip *ptr = get_ip(self);
 
-    if (Tcl_InterpDeleted(ptr->ip)) {
+    if (Tcl_InterpDeleted(ptr->ip) || rbtk_invalid_namespace(ptr)) {
         return Qtrue;
     } else {
         return Qfalse;
@@ -4091,8 +4098,7 @@
       Tcl_IncrRefCount(cmd);
 
       /* ip is deleted? */
-      if (Tcl_InterpDeleted(ptr->ip) || ip_null_namespace(ptr->ip)
-          || Tcl_GetCurrentNamespace(ptr->ip) != ptr->default_ns) {
+      if (Tcl_InterpDeleted(ptr->ip) || rbtk_invalid_namespace(ptr)) {
           DUMP1("ip is deleted");
           Tcl_DecrRefCount(cmd);
           rb_thread_critical = thr_crit_bup;
@@ -4133,8 +4139,7 @@
     DUMP2("Tcl_Eval(%s)", cmd_str);
 
     /* ip is deleted? */
-    if (Tcl_InterpDeleted(ptr->ip) || ip_null_namespace(ptr->ip)
-        || Tcl_GetCurrentNamespace(ptr->ip) != ptr->default_ns) {
+    if (Tcl_InterpDeleted(ptr->ip) || rbtk_invalid_namespace(ptr)) {
         DUMP1("ip is deleted");
         ptr->return_value = TCL_OK;
         return rb_tainted_str_new2("");
@@ -4339,8 +4344,7 @@
     rb_secure(4);
 
     /* ip is deleted? */
-    if (Tcl_InterpDeleted(ptr->ip) || ip_null_namespace(ptr->ip)
-        || Tcl_GetCurrentNamespace(ptr->ip) != ptr->default_ns) {
+    if (Tcl_InterpDeleted(ptr->ip) || rbtk_invalid_namespace(ptr)) {
         DUMP1("ip is deleted");
         rb_raise(rb_eRuntimeError, "interpreter is deleted");
     }
@@ -4829,8 +4833,7 @@
     ptr = get_ip(interp);
 
     /* ip is deleted? */
-    if (Tcl_InterpDeleted(ptr->ip) || ip_null_namespace(ptr->ip)
-        || Tcl_GetCurrentNamespace(ptr->ip) != ptr->default_ns) {
+    if (Tcl_InterpDeleted(ptr->ip) || rbtk_invalid_namespace(ptr)) {
         DUMP1("ip is deleted");
         return rb_tainted_str_new2("");
     }
@@ -5312,8 +5315,7 @@
         Tcl_IncrRefCount(nameobj);
 
         /* ip is deleted? */
-        if (Tcl_InterpDeleted(ptr->ip) || ip_null_namespace(ptr->ip)
-            || Tcl_GetCurrentNamespace(ptr->ip) != ptr->default_ns) {
+        if (Tcl_InterpDeleted(ptr->ip) || rbtk_invalid_namespace(ptr)) {
             DUMP1("ip is deleted");
             Tcl_DecrRefCount(nameobj);
             rb_thread_critical = thr_crit_bup;
@@ -5377,8 +5379,7 @@
         char *ret;
 
         /* ip is deleted? */
-        if (Tcl_InterpDeleted(ptr->ip) || ip_null_namespace(ptr->ip)
-            || Tcl_GetCurrentNamespace(ptr->ip) != ptr->default_ns) {
+        if (Tcl_InterpDeleted(ptr->ip) || rbtk_invalid_namespace(ptr)) {
             DUMP1("ip is deleted");
             return rb_tainted_str_new2("");
         } else {
@@ -5450,8 +5451,7 @@
         Tcl_IncrRefCount(idxobj);
 
         /* ip is deleted? */
-        if (Tcl_InterpDeleted(ptr->ip) || ip_null_namespace(ptr->ip)
-            || Tcl_GetCurrentNamespace(ptr->ip) != ptr->default_ns) {
+        if (Tcl_InterpDeleted(ptr->ip) || rbtk_invalid_namespace(ptr)) {
             DUMP1("ip is deleted");
             Tcl_DecrRefCount(nameobj);
             Tcl_DecrRefCount(idxobj);
@@ -5516,8 +5516,7 @@
         char *ret;
 
         /* ip is deleted? */
-        if (Tcl_InterpDeleted(ptr->ip) || ip_null_namespace(ptr->ip)
-            || Tcl_GetCurrentNamespace(ptr->ip) != ptr->default_ns) {
+        if (Tcl_InterpDeleted(ptr->ip) || rbtk_invalid_namespace(ptr)) {
             DUMP1("ip is deleted");
             return rb_tainted_str_new2("");
         } else {
@@ -5614,8 +5613,7 @@
 # endif
 
         /* ip is deleted? */
-        if (Tcl_InterpDeleted(ptr->ip) || ip_null_namespace(ptr->ip)
-            || Tcl_GetCurrentNamespace(ptr->ip) != ptr->default_ns) {
+        if (Tcl_InterpDeleted(ptr->ip) || rbtk_invalid_namespace(ptr)) {
             DUMP1("ip is deleted");
             Tcl_DecrRefCount(nameobj);
             Tcl_DecrRefCount(valobj);
@@ -5682,8 +5680,7 @@
         CONST char *ret;
 
         /* ip is deleted? */
-        if (Tcl_InterpDeleted(ptr->ip) || ip_null_namespace(ptr->ip)
-            || Tcl_GetCurrentNamespace(ptr->ip) != ptr->default_ns) {
+        if (Tcl_InterpDeleted(ptr->ip) || rbtk_invalid_namespace(ptr)) {
             DUMP1("ip is deleted");
             return rb_tainted_str_new2("");
         } else {
@@ -5780,8 +5777,7 @@
         Tcl_IncrRefCount(valobj);
 
         /* ip is deleted? */
-        if (Tcl_InterpDeleted(ptr->ip) || ip_null_namespace(ptr->ip)
-            || Tcl_GetCurrentNamespace(ptr->ip) != ptr->default_ns) {
+        if (Tcl_InterpDeleted(ptr->ip) || rbtk_invalid_namespace(ptr)) {
             DUMP1("ip is deleted");
             Tcl_DecrRefCount(nameobj);
             Tcl_DecrRefCount(idxobj);
@@ -5842,8 +5838,7 @@
         CONST char *ret;
 
         /* ip is deleted? */
-        if (Tcl_InterpDeleted(ptr->ip) || ip_null_namespace(ptr->ip)
-            || Tcl_GetCurrentNamespace(ptr->ip) != ptr->default_ns) {
+        if (Tcl_InterpDeleted(ptr->ip) || rbtk_invalid_namespace(ptr)) {
             DUMP1("ip is deleted");
             return rb_tainted_str_new2("");
         } else {
@@ -5887,8 +5882,7 @@
     StringValue(varname);
 
     /* ip is deleted? */
-    if (Tcl_InterpDeleted(ptr->ip) || ip_null_namespace(ptr->ip)
-        || Tcl_GetCurrentNamespace(ptr->ip) != ptr->default_ns) {
+    if (Tcl_InterpDeleted(ptr->ip) || rbtk_invalid_namespace(ptr)) {
         DUMP1("ip is deleted");
         return Qtrue;
     }
@@ -5930,8 +5924,7 @@
     StringValue(index);
 
     /* ip is deleted? */
-    if (Tcl_InterpDeleted(ptr->ip) || ip_null_namespace(ptr->ip)
-        || Tcl_GetCurrentNamespace(ptr->ip) != ptr->default_ns) {
+    if (Tcl_InterpDeleted(ptr->ip) || rbtk_invalid_namespace(ptr)) {
         DUMP1("ip is deleted");
         return Qtrue;
     }
@@ -6524,7 +6517,7 @@
     rb_define_method(ip, "allow_ruby_exit=", ip_allow_ruby_exit_set, 1);
     rb_define_method(ip, "delete", ip_delete, 0);
     rb_define_method(ip, "deleted?", ip_is_deleted_p, 0);
-    rb_define_method(ip, "null_namespace?", ip_has_null_namespace_p, 0);
+    rb_define_method(ip, "invalid_namespace?", ip_has_invalid_namespace_p, 0);
     rb_define_method(ip, "_eval", ip_eval, 1);
     rb_define_method(ip, "_toUTF8", ip_toUTF8, -1);
     rb_define_method(ip, "_fromUTF8", ip_fromUTF8, -1);

-- 
                                       永井 秀利 (九工大 知能情報)
                                           nagai / ai.kyutech.ac.jp