diff --git a/ChangeLog b/ChangeLog index 01c244df20..ca81ef8447 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,42 @@ +Fri Jul 22 07:01:42 2005 Hidetoshi NAGAI + + * ext/tk/tkutil/tkutil.c (tk_conv_args): forget to revert + thread_critical and gc_disable when raise ArgumentError. + + * ext/tk/lib/remote-tk.rb: RemoteTkIp doesn't need to include TkUtil. + + * ext/tk/tcltklib.c: add TclTkIp#has_mainwindow? method. + + * ext/tk/lib/tk.rb: add Tk.has_mainwindow? method. + + * ext/tk/lib/multi-tk.rb: add MultiTkIp#has_mainwindow? method. + + * ext/tk/lib/remote-tk.rb: add RemoteTkIp#has_mainwindow? method. + + * ext/tk/lib/multi-tk.rb: slave IP fail to exit itself when $SAFE==4. + + * ext/tk/lib/multi-tk.rb: remove constants from MultiTkIp module to + avoid access from external. + + * ext/tk/lib/multi-tk.rb: check_root flag is ignored on slave IPs' + mainloop. + + * ext/tk/lib/multi-tk.rb: hang-up Tk.mainloop called on a slave IP + with $SAFE==4. + + * ext/tk/lib/multi-tk.rb: MultiTkIp#bg_eval_proc doesn't work + properly. + + * ext/tk/lib/multi-tk.rb: add MultiTkIp#set_cb_error(proc) and + cb_error(exc) to log errors at callbacks on safe slave IPs. + + * ext/tk/lib/multi-tk.rb: fail to get an available slave IP object + when call Tk.mainloop in the block which is given to new_* method, + because cannot finish initialize while the root widget is alive. + + * ext/tk/lib/multi-tk.rb: fail to control a slave IP when Tk.mainloop + runs on the IP. + Thu Jul 21 01:00:00 2005 NARUSE, Yui * ext/nkf/nkf-utf8/{nkf.c,utf8tbl.c,config.h}: diff --git a/ext/tk/MANUAL_tcltklib.eng b/ext/tk/MANUAL_tcltklib.eng index 9e7bd20d37..1db61f228e 100644 --- a/ext/tk/MANUAL_tcltklib.eng +++ b/ext/tk/MANUAL_tcltklib.eng @@ -310,6 +310,11 @@ class TclTkIp : Check whether the interpreter is already deleted. : If deleted, returns true. + has_mainwindow? + : Check whether the interpreter has a MainWindow (root widget). + : If has, returns true. If doesn't, returns false. + : If IP is already deleted, returns nil. + restart : Restart Tk part of the interpreter. : Use this when you need Tk functions after destroying the diff --git a/ext/tk/MANUAL_tcltklib.eucj b/ext/tk/MANUAL_tcltklib.eucj index f90dcff3e4..5dd36726ba 100644 --- a/ext/tk/MANUAL_tcltklib.eucj +++ b/ext/tk/MANUAL_tcltklib.eucj @@ -422,6 +422,11 @@ require "tcltklib" : delete 済みでコマンドを受け付けない状態になっているならば : true を返す. + has_mainwindow? + : Tcl/Tk インタープリタにメインウィンドウ (root widget) が + : 存在すれば true を,存在しなければ false を返す. + : インタープリタが既に delete 済みであれば nil を返す. + restart : Tcl/Tk インタープリタの Tk 部分の初期化,再起動を行う. : 一旦 root widget を破壊した後に再度 Tk の機能が必要と diff --git a/ext/tk/lib/multi-tk.rb b/ext/tk/lib/multi-tk.rb index e8bbf67de1..3119080734 100644 --- a/ext/tk/lib/multi-tk.rb +++ b/ext/tk/lib/multi-tk.rb @@ -7,7 +7,7 @@ require 'tkutil' require 'thread' if defined? Tk - fail RuntimeError, "'multi-tk' library must be required before requiring 'tk'" + fail RuntimeError,"'multi-tk' library must be required before requiring 'tk'" end ################################################ @@ -54,7 +54,7 @@ MultiTkIp_OK.freeze ################################################ # methods for construction class MultiTkIp - SLAVE_IP_ID = ['slave'.freeze, '0'.taint].freeze + @@SLAVE_IP_ID = ['slave'.freeze, '0'.taint].freeze @@IP_TABLE = {}.taint unless defined?(@@IP_TABLE) @@ -86,9 +86,26 @@ class MultiTkIp @ip.cb_eval(@cmd, *args) rescue TkCallbackBreak, TkCallbackContinue => e fail e + rescue SecurityError => e + # in 'exit', 'exit!', and 'abort' : security error --> delete IP + if e.backtrace[0] =~ /^(.+?):(\d+):in `(exit|exit!|abort)'/ + @ip.delete + elsif @ip.safe? + if @ip.respond_to?(:cb_error) + @ip.cb_error(e) + else + nil # ignore + end + else + fail e + end rescue Exception => e if @ip.safe? - nil # ignore + if @ip.respond_to?(:cb_error) + @ip.cb_error(e) + else + nil # ignore + end else fail e end @@ -161,6 +178,18 @@ class MultiTkIp ###################################### + def set_cb_error(cmd = Proc.new) + @cb_error_proc[0] = cmd + end + + def cb_error(e) + if @cb_error_proc[0].respond_to?(:call) + @cb_error_proc[0].call(e) + end + end + + ###################################### + def set_safe_level(safe) if safe > @safe_level[0] @safe_level[0] = safe @@ -192,7 +221,7 @@ class MultiTkIp end def running_mainloop? - @wait_on_mainloop[1] + @wait_on_mainloop[1] > 0 end def _destroy_slaves_of_slaveIP(ip) @@ -442,13 +471,21 @@ class MultiTkIp private :_receiver_eval_proc, :_receiver_eval_proc_core def _receiver_mainloop(check_root) - Thread.new{ - while !@interp.deleted? - inf = @interp._invoke_without_enc('info', 'command', '.') - break if !inf.kind_of?(String) || inf != '.' - sleep 0.5 - end - } + if @evloop_thread[0] && @evloop_thread[0].alive? + @evloop_thread[0] + else + @evloop_thread[0] = Thread.new{ + while !@interp.deleted? + #if check_root + # inf = @interp._invoke_without_enc('info', 'command', '.') + # break if !inf.kind_of?(String) || inf != '.' + #end + break if check_root && !@interp.has_mainwindow? + sleep 0.5 + end + } + @evloop_thread[0] + end end def _create_receiver_and_watchdog(lvl = $SAFE) @@ -456,7 +493,7 @@ class MultiTkIp # command-procedures receiver receiver = Thread.new(lvl){|safe_level| - last_thread = nil + last_thread = {} loop do break if @interp.deleted? @@ -479,8 +516,9 @@ class MultiTkIp else # procedure - last_thread = _receiver_eval_proc(last_thread, safe_level, - thread, cmd, *args) + last_thread[thread] = _receiver_eval_proc(last_thread[thread], + safe_level, thread, + cmd, *args) end end } @@ -539,6 +577,8 @@ class MultiTkIp @slave_ip_top = {}.taint + @evloop_thread = [].taint + unless keys.kind_of? Hash fail ArgumentError, "expecting a Hash object for the 2nd argument" end @@ -548,7 +588,7 @@ class MultiTkIp @system = Object.new - @wait_on_mainloop = [true, false] + @wait_on_mainloop = [true, 0].taint @threadgroup = Thread.current.group @@ -657,7 +697,7 @@ class MultiTkIp ###################################### - SAFE_OPT_LIST = [ + @@SAFE_OPT_LIST = [ 'accessPath'.freeze, 'statics'.freeze, 'nested'.freeze, @@ -676,7 +716,7 @@ class MultiTkIp name = v elsif k_str == 'safe' safe = v - elsif SAFE_OPT_LIST.member?(k_str) + elsif @@SAFE_OPT_LIST.member?(k_str) safe_opts[k_str] = v else tk_opts[k_str] = v @@ -692,8 +732,8 @@ class MultiTkIp private :_parse_slaveopts def _create_slave_ip_name - name = SLAVE_IP_ID.join('') - SLAVE_IP_ID[1].succ! + name = @@SLAVE_IP_ID.join('') + @@SLAVE_IP_ID[1].succ! name end private :_create_slave_ip_name @@ -924,11 +964,15 @@ class MultiTkIp @tk_table_list = [] @slave_ip_tbl = {} @slave_ip_top = {} + @cb_error_proc = [] + @evloop_thread = [] @tk_windows.taint unless @tk_windows.tainted? @tk_table_list.taint unless @tk_table_list.tainted? @slave_ip_tbl.taint unless @slave_ip_tbl.tainted? @slave_ip_top.taint unless @slave_ip_top.tainted? + @cb_error_proc.taint unless @cb_error_proc.tainted? + @evloop_thread.taint unless @evloop_thread.tainted? name, safe, safe_opts, tk_opts = _parse_slaveopts(keys) @@ -975,7 +1019,8 @@ class MultiTkIp @system = Object.new - @wait_on_mainloop = [true, false] + @wait_on_mainloop = [true, 0].taint + # @wait_on_mainloop = [false, 0].taint @threadgroup = ThreadGroup.new @@ -1088,7 +1133,10 @@ class << MultiTkIp end ip = __new(__getip, nil, keys) - ip.eval_proc(proc{$SAFE=ip.safe_level; Proc.new}.call) if block_given? + #ip.eval_proc(proc{$SAFE=ip.safe_level; Proc.new}.call) if block_given? + if block_given? + Thread.new{ip.eval_proc(proc{$SAFE=ip.safe_level; Proc.new}.call)} + end ip end @@ -1109,7 +1157,10 @@ class << MultiTkIp end ip = __new(__getip, false, keys) - ip.eval_proc(proc{$SAFE=ip.safe_level; Proc.new}.call) if block_given? + # ip.eval_proc(proc{$SAFE=ip.safe_level; Proc.new}.call) if block_given? + if block_given? + Thread.new{ip.eval_proc(proc{$SAFE=ip.safe_level; Proc.new}.call)} + end ip end alias new_trusted_slave new_slave @@ -1127,7 +1178,10 @@ class << MultiTkIp end ip = __new(__getip, true, keys) - ip.eval_proc(proc{$SAFE=ip.safe_level; Proc.new}.call) if block_given? + # ip.eval_proc(proc{$SAFE=ip.safe_level; Proc.new}.call) if block_given? + if block_given? + Thread.new{ip.eval_proc(proc{$SAFE=ip.safe_level; Proc.new}.call)} + end ip end alias new_safeTk new_safe_slave @@ -1504,7 +1558,11 @@ class MultiTkIp backup_ip = current['callback_ip'] current['callback_ip'] = self begin - eval_proc_core(false, cmd, safe_level, *args) + eval_proc_core(false, + proc{|safe, *params| + $SAFE=safe if $SAFE < safe + cmd.call(*params) + }, safe_level, *args) ensure current['callback_ip'] = backup_ip end @@ -1514,7 +1572,7 @@ class MultiTkIp $SAFE=safe if $SAFE < safe Thread.new(*params, &cmd).value }, - *args) + safe_level, *args) end end alias call eval_proc @@ -1528,15 +1586,19 @@ class MultiTkIp end end Thread.new{ + eval_proc(cmd, *args) +=begin eval_proc_core(false, proc{|safe, *params| $SAFE=safe if $SAFE < safe Thread.new(*params, &cmd).value }, - *args) + safe_level, *args) +=end } end alias background_eval_proc bg_eval_proc + alias thread_eval_proc bg_eval_proc alias bg_call bg_eval_proc alias background_call bg_eval_proc @@ -1550,7 +1612,7 @@ class MultiTkIp proc{|safe| $SAFE=safe if $SAFE < safe Kernel.eval(cmd, *eval_args) - }) + }, safe_level) end alias eval_str eval_string @@ -1564,7 +1626,7 @@ class MultiTkIp proc{|safe| $SAFE=safe if $SAFE < safe Kernel.eval(cmd, *eval_args) - }) + }, safe_level) } end alias background_eval_string bg_eval_string @@ -1679,6 +1741,10 @@ class << MultiTkIp __getip.deleted? end + def has_mainwindow? + __getip.has_mainwindow? + end + def invalid_namespace? __getip.invalid_namespace? end @@ -1860,22 +1926,24 @@ class MultiTkIp if self != @@DEFAULT_MASTER if @wait_on_mainloop[0] begin - @wait_on_mainloop[1] = true - @cmd_queue.enq([@system, 'call_mainloop', - Thread.current, check_root]) - Thread.stop + @wait_on_mainloop[1] += 1 + if $SAFE >= 4 + _receiver_mainloop(check_root).join + else + @cmd_queue.enq([@system, 'call_mainloop', + Thread.current, check_root]) + Thread.stop + end rescue MultiTkIp_OK => ret # return value - @wait_on_mainloop[1] = false if ret.value.kind_of?(Thread) return ret.value.value else return ret.value end - rescue SystemExit + rescue SystemExit => e # exit IP warn("Warning: " + $! + " on " + self.inspect) if $DEBUG - @wait_on_mainloop[1] = false begin self._eval_without_enc('exit') rescue Exception @@ -1887,17 +1955,18 @@ class MultiTkIp ((e.message.length > 0)? ' "' + e.message + '"': '') + " on " + self.inspect) end - @wait_on_mainloop[1] = false return e + rescue Exception => e + return e ensure - @wait_on_mainloop[1] = false + @wait_on_mainloop[1] -= 1 end end return end unless restart_on_dead - @wait_on_mainloop[1] = true + @wait_on_mainloop[1] += 1 =begin begin @interp.mainloop(check_root) @@ -1909,11 +1978,13 @@ class MultiTkIp end end =end - @interp.mainloop(check_root) - @wait_on_mainloop[1] = false + begin + @interp.mainloop(check_root) + ensure + @wait_on_mainloop[1] -= 1 + end else loop do - @wait_on_mainloop[1] = true break unless self.alive? if check_root begin @@ -1924,6 +1995,7 @@ class MultiTkIp end break if @interp.deleted? begin + @wait_on_mainloop[1] += 1 @interp.mainloop(check_root) rescue StandardError => e if TclTkLib.mainloop_abort_on_exception != nil @@ -1948,7 +2020,7 @@ class MultiTkIp =end raise e ensure - @wait_on_mainloop[1] = false + @wait_on_mainloop[1] -= 1 Thread.pass # avoid eventloop conflict end end @@ -2040,6 +2112,10 @@ class MultiTkIp @interp.deleted? end + def has_mainwindow? + @interp.has_mainwindow? + end + def invalid_namespace? @interp.invalid_namespace? end diff --git a/ext/tk/lib/remote-tk.rb b/ext/tk/lib/remote-tk.rb index ed7f75b336..6e64a1ca2d 100644 --- a/ext/tk/lib/remote-tk.rb +++ b/ext/tk/lib/remote-tk.rb @@ -60,8 +60,6 @@ class << RemoteTkIp end class RemoteTkIp - include TkUtil - def initialize(remote_ip, displayof=nil, timeout=5) if $SAFE >= 4 fail SecurityError, "cannot access another interpreter at level #{$SAFE}" @@ -96,7 +94,7 @@ class RemoteTkIp @safe_level = [$SAFE] - @wait_on_mainloop = [true, false] + @wait_on_mainloop = [true, 0] @cmd_queue = Queue.new @@ -179,7 +177,7 @@ class RemoteTkIp fail SecurityError, "cannot send tainted commands at level #{$SAFE}" end - cmds = @interp._merge_tklist(*_conv_args([], enc_mode, *cmds)) + cmds = @interp._merge_tklist(*TkUtil::_conv_args([], enc_mode, *cmds)) if @displayof if async @interp.__invoke('send', '-async', '-displayof', @displayof, @@ -283,6 +281,19 @@ class RemoteTkIp end end + def has_mainwindow? + begin + inf = @interp._invoke_without_enc('info', 'command', '.') + rescue Exception + return nil + end + if !inf.kind_of?(String) || inf != '.' + false + else + true + end + end + def invalid_namespace? false end @@ -343,25 +354,25 @@ class RemoteTkIp def _get_variable(var_name, flag) # ignore flag - _appsend(false, 'set', _get_eval_string(var_name)) + _appsend(false, 'set', TkComm::_get_eval_string(var_name)) end def _get_variable2(var_name, index_name, flag) # ignore flag - _appsend(false, 'set', "#{_get_eval_string(var_name)}(#{_get_eval_string(index_name)})") + _appsend(false, 'set', "#{TkComm::_get_eval_string(var_name)}(#{TkComm::_get_eval_string(index_name)})") end def _set_variable(var_name, value, flag) # ignore flag - _appsend(false, 'set', _get_eval_string(var_name), _get_eval_string(value)) + _appsend(false, 'set', TkComm::_get_eval_string(var_name), TkComm::_get_eval_string(value)) end def _set_variable2(var_name, index_name, value, flag) # ignore flag - _appsend(false, 'set', "#{_get_eval_string(var_name)}(#{_get_eval_string(index_name)})", _get_eval_string(value)) + _appsend(false, 'set', "#{TkComm::_get_eval_string(var_name)}(#{TkComm::_get_eval_string(index_name)})", TkComm::_get_eval_string(value)) end def _unset_variable(var_name, flag) # ignore flag - _appsend(false, 'unset', _get_eval_string(var_name)) + _appsend(false, 'unset', TkComm::_get_eval_string(var_name)) end def _unset_variable2(var_name, index_name, flag) # ignore flag @@ -369,21 +380,21 @@ class RemoteTkIp end def _get_global_var(var_name) - _appsend(false, 'set', _get_eval_string(var_name)) + _appsend(false, 'set', TkComm::_get_eval_string(var_name)) end def _get_global_var2(var_name, index_name) - _appsend(false, 'set', "#{_get_eval_string(var_name)}(#{_get_eval_string(index_name)})") + _appsend(false, 'set', "#{TkComm::_get_eval_string(var_name)}(#{TkComm::_get_eval_string(index_name)})") end def _set_global_var(var_name, value) - _appsend(false, 'set', _get_eval_string(var_name), _get_eval_string(value)) + _appsend(false, 'set', TkComm::_get_eval_string(var_name), TkComm::_get_eval_string(value)) end def _set_global_var2(var_name, index_name, value) - _appsend(false, 'set', "#{_get_eval_string(var_name)}(#{_get_eval_string(index_name)})", _get_eval_string(value)) + _appsend(false, 'set', "#{TkComm::_get_eval_string(var_name)}(#{TkComm::_get_eval_string(index_name)})", TkComm::_get_eval_string(value)) end def _unset_global_var(var_name) - _appsend(false, 'unset', _get_eval_string(var_name)) + _appsend(false, 'unset', TkComm::_get_eval_string(var_name)) end def _unset_global_var2(var_name, index_name) _appsend(false, 'unset', "#{var_name}(#{index_name})") diff --git a/ext/tk/lib/tk.rb b/ext/tk/lib/tk.rb index 004e9e8f68..9f6290952e 100644 --- a/ext/tk/lib/tk.rb +++ b/ext/tk/lib/tk.rb @@ -1833,6 +1833,10 @@ module Tk code end + def Tk.has_mainwindow? + INTERP.has_mainwindow? + end + def root TkRoot.new end @@ -4195,7 +4199,7 @@ end #Tk.freeze module Tk - RELEASE_DATE = '2005-07-19'.freeze + RELEASE_DATE = '2005-07-22'.freeze autoload :AUTO_PATH, 'tk/variable' autoload :TCL_PACKAGE_PATH, 'tk/variable' diff --git a/ext/tk/tcltklib.c b/ext/tk/tcltklib.c index b088ef7cf7..d1e5f9d0d0 100644 --- a/ext/tk/tcltklib.c +++ b/ext/tk/tcltklib.c @@ -4,7 +4,7 @@ * Oct. 24, 1997 Y. Matsumoto */ -#define TCLTKLIB_RELEASE_DATE "2005-07-19" +#define TCLTKLIB_RELEASE_DATE "2005-07-22" #include "ruby.h" #include "rubysig.h" @@ -511,6 +511,7 @@ get_ip(self) } if (ptr->ip == (Tcl_Interp*)NULL) { /* rb_raise(rb_eRuntimeError, "deleted IP"); */ + return((struct tcltkip *)NULL); } return ptr; } @@ -5293,6 +5294,22 @@ ip_is_deleted_p(self) } } +static VALUE +ip_has_mainwindow_p(self) + VALUE self; +{ + struct tcltkip *ptr = get_ip(self); + + if (ptr == (struct tcltkip *)NULL || ptr->ip == (Tcl_Interp *)NULL + || Tcl_InterpDeleted(ptr->ip)) { + return Qnil; + } else if (Tk_MainWindow(ptr->ip) == (Tk_Window)NULL) { + return Qfalse; + } else { + return Qtrue; + } +} + static VALUE #ifdef HAVE_STDARG_PROTOTYPES @@ -8557,6 +8574,7 @@ Init_tcltklib() 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, "has_mainwindow?", ip_has_mainwindow_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); diff --git a/ext/tk/tkutil/tkutil.c b/ext/tk/tkutil/tkutil.c index 464bbafa9c..496649f8c5 100644 --- a/ext/tk/tkutil/tkutil.c +++ b/ext/tk/tkutil/tkutil.c @@ -8,7 +8,7 @@ ************************************************/ -#define TKUTIL_RELEASE_DATE "2005-07-05" +#define TKUTIL_RELEASE_DATE "2005-07-22" #include "ruby.h" #include "rubysig.h" @@ -892,14 +892,14 @@ tk_conv_args(argc, argv, self) int thr_crit_bup; VALUE old_gc; - thr_crit_bup = rb_thread_critical; - rb_thread_critical = Qtrue; - - old_gc = rb_gc_disable(); - if (argc < 2) { rb_raise(rb_eArgError, "too few arguments"); } + + thr_crit_bup = rb_thread_critical; + rb_thread_critical = Qtrue; + old_gc = rb_gc_disable(); + for(size = 0, idx = 2; idx < argc; idx++) { if (TYPE(argv[idx]) == T_HASH) { size += 2 * RHASH(argv[idx])->tbl->num_entries; @@ -1605,6 +1605,7 @@ Init_tkutil() tk_get_eval_string, -1); rb_define_singleton_method(mTK, "_get_eval_enc_str", tk_get_eval_enc_str, 1); + rb_define_singleton_method(mTK, "_conv_args", tk_conv_args, -1); rb_define_singleton_method(mTK, "bool", tcl2rb_bool, 1); rb_define_singleton_method(mTK, "number", tcl2rb_number, 1);