ruby / tk

Tk interface module using tcltklib
Other
117 stars 22 forks source link

Crash when DISPLAY is not set #12

Closed pterjan closed 3 years ago

pterjan commented 6 years ago
$ DISPLAY= ruby -r tk -e ''
/usr/share/gems/gems/tk-0.2.0/lib/tk.rb:32:in `initialize': tcltklib: fail to Tk_Init(). couldn't connect to display "" (RuntimeError)
    from /usr/share/gems/gems/tk-0.2.0/lib/tk.rb:32:in `initialize'
    from /usr/share/gems/gems/tk-0.2.0/lib/tk.rb:1246:in `new'
    from /usr/share/gems/gems/tk-0.2.0/lib/tk.rb:1246:in `block in <module:TkCore>'
Tcl_AsyncDelete: async handler deleted by the wrong thread
Aborted (core dumped)
(gdb) bt
#0  0x00007ffff75c0818 in raise () from /lib64/libc.so.6
#1  0x00007ffff75c1f2a in abort () from /lib64/libc.so.6
#2  0x00007ffff543d83f in Tcl_PanicVA () from /lib64/libtcl8.6.so
#3  0x00007ffff543d8fc in Tcl_Panic () from /lib64/libtcl8.6.so
#4  0x00007ffff537088e in Tcl_AsyncDelete () from /lib64/libtcl8.6.so
#5  0x00007ffff5373ced in ?? () from /lib64/libtcl8.6.so
#6  0x00007ffff56efde8 in ip_free (p=0x7fffec0008f0) at tcltklib.c:5795
#7  0x00007ffff79d8fe1 in finalize_list () from /lib64/libruby.so.2.4
#8  0x00007ffff79e5750 in rb_gc_call_finalizer_at_exit () from /lib64/libruby.so.2.4
#9  0x00007ffff79d05ed in ruby_cleanup () from /lib64/libruby.so.2.4
#10 0x00007ffff79d0777 in ruby_run_node () from /lib64/libruby.so.2.4
#11 0x00000000004007fb in main (argc=<optimized out>, argv=<optimized out>) at main.c:36
jeremyevans commented 3 years ago

I can't reproduce the core dump with the current version of Ruby. Is this still a problem in your environment with the current version of Ruby?

pterjan commented 3 years ago

I am still seing the problem:

[pterjan@mageia ~]$ DISPLAY= ruby -r tk -e ''
#<Thread:0x0000000001ddb5e8 /usr/share/gems/gems/tk-0.2.0/lib/tk.rb:1243 run> terminated with exception (report_on_exception is true):
Traceback (most recent call last):
    3: from /usr/share/gems/gems/tk-0.2.0/lib/tk.rb:1246:in `block in <module:TkCore>'
    2: from /usr/share/gems/gems/tk-0.2.0/lib/tk.rb:1246:in `new'
    1: from /usr/share/gems/gems/tk-0.2.0/lib/tk.rb:32:in `initialize'
/usr/share/gems/gems/tk-0.2.0/lib/tk.rb:32:in `initialize': tcltklib: fail to Tk_Init(). couldn't connect to display "" (RuntimeError)
Traceback (most recent call last):
    1: from /usr/share/rubygems/rubygems/core_ext/kernel_require.rb:92:in `require'
/usr/share/rubygems/rubygems/core_ext/kernel_require.rb:92:in `require': cannot load such file -- tk (LoadError)
    3: from /usr/share/gems/gems/tk-0.2.0/lib/tk.rb:1246:in `block in <module:TkCore>'
    2: from /usr/share/gems/gems/tk-0.2.0/lib/tk.rb:1246:in `new'
    1: from /usr/share/gems/gems/tk-0.2.0/lib/tk.rb:32:in `initialize'
/usr/share/gems/gems/tk-0.2.0/lib/tk.rb:32:in `initialize': tcltklib: fail to Tk_Init(). couldn't connect to display "" (RuntimeError)
Tcl_AsyncDelete: async handler deleted by the wrong thread
Aborted (core dumped)

[pterjan@mageia ~]$ ruby -r tk -e ''
/usr/share/gems/gems/tk-0.2.0/lib/tk.rb:1495: warning: rb_safe_level will be removed in Ruby 3.0
/usr/share/gems/gems/tk-0.2.0/lib/tk.rb:1301: warning: rb_safe_level will be removed in Ruby 3.0
/usr/share/gems/gems/tk-0.2.0/lib/tk.rb:837: warning: rb_safe_level will be removed in Ruby 3.0
/usr/share/gems/gems/tk-0.2.0/lib/tk.rb:1301: warning: rb_safe_level will be removed in Ruby 3.0
/usr/share/gems/gems/tk-0.2.0/lib/tk.rb:1497: warning: rb_safe_level will be removed in Ruby 3.0
/usr/share/gems/gems/tk-0.2.0/lib/tk.rb:1301: warning: rb_safe_level will be removed in Ruby 3.0
/usr/share/gems/gems/tk-0.2.0/lib/tk.rb:1469: warning: rb_safe_level will be removed in Ruby 3.0
/usr/share/gems/gems/tk-0.2.0/lib/tk.rb:1301: warning: rb_safe_level will be removed in Ruby 3.0
/usr/share/gems/gems/tk-0.2.0/lib/tk.rb:1469: warning: rb_safe_level will be removed in Ruby 3.0
/usr/share/gems/gems/tk-0.2.0/lib/tk.rb:1301: warning: rb_safe_level will be removed in Ruby 3.0
/usr/share/gems/gems/tk-0.2.0/lib/tk.rb:2166: warning: rb_safe_level will be removed in Ruby 3.0
/usr/share/gems/gems/tk-0.2.0/lib/tk.rb:1301: warning: rb_safe_level will be removed in Ruby 3.0
/usr/share/gems/gems/tk-0.2.0/lib/tk.rb:2167: warning: rb_safe_level will be removed in Ruby 3.0
/usr/share/gems/gems/tk-0.2.0/lib/tk.rb:1301: warning: rb_safe_level will be removed in Ruby 3.0
/usr/share/gems/gems/tk-0.2.0/lib/tk.rb:2173: warning: rb_safe_level will be removed in Ruby 3.0
/usr/share/gems/gems/tk-0.2.0/lib/tk.rb:1301: warning: rb_safe_level will be removed in Ruby 3.0
/usr/share/gems/gems/tk-0.2.0/lib/tk.rb:2174: warning: rb_safe_level will be removed in Ruby 3.0
/usr/share/gems/gems/tk-0.2.0/lib/tk.rb:1301: warning: rb_safe_level will be removed in Ruby 3.0
/usr/share/gems/gems/tk-0.2.0/lib/tk.rb:2180: warning: rb_safe_level will be removed in Ruby 3.0
/usr/share/gems/gems/tk-0.2.0/lib/tk.rb:1301: warning: rb_safe_level will be removed in Ruby 3.0
/usr/share/gems/gems/tk-0.2.0/lib/tk.rb:2608: warning: rb_set_safe_level_force will be removed in Ruby 3.0
/usr/share/gems/gems/tk-0.2.0/lib/tk.rb:2054: warning: rb_safe_level will be removed in Ruby 3.0
/usr/share/gems/gems/tk-0.2.0/lib/tk.rb:1301: warning: rb_safe_level will be removed in Ruby 3.0
/usr/share/gems/gems/tk-0.2.0/lib/tk.rb:1456: warning: rb_eval_cmd will be removed in Ruby 3.0
/usr/share/gems/gems/tk-0.2.0/lib/tk.rb:1479: warning: rb_safe_level will be removed in Ruby 3.0
/usr/share/gems/gems/tk-0.2.0/lib/tk.rb:1315: warning: rb_safe_level will be removed in Ruby 3.0

[pterjan@mageia ~]$ ruby --version
ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-linux]
jeremyevans commented 3 years ago

OK. Can you provide more details about your environment, such as operating system, and versions of tcl/tk in use?

Can you also try this and see if you get the same error: DISPLAY= ruby -r tk -e 'at_exit{exit!}'

pterjan commented 3 years ago

Same problem with the at_exit

This is on Mageia Linux x86_64, using tcl/tk 8.6.10

jeremyevans commented 3 years ago

Can you try with tcl/tk 8.5? I'm not sure how well ruby-tk supports tcl/tk 8.6. Support was added for tcl/tk 8.6 in 2014, but I'm guessing tcl/tk 8.5 is better supported.

pterjan commented 3 years ago

I looked more into it last night and this is what I found.

It seems RUN_EVENTLOOP_ON_MAIN_THREAD is true on cygwin and MacOS X which means the problem can not occur there. I confirmed that setting it to true fixes the problem as there is no thread to mismatch.

Here the interpreter gets created in a thread, and TclTkIp.new throws an exception and I guess the thread ends so cleanup ends up happening outside of it:

      INTERP_THREAD = Thread.new{
        begin
          #Thread.current[:interp] = interp = TclTkIp.new(name, opts)
          interp = TclTkIp.new(name, opts)
        rescue => e
          Thread.current[:interp] = e
          raise e
        end

Further down there is a block to run the main loop and that one specifically handles the same problem by deleting the interpreter before the thread exists:

        ensure
          # interp must be deleted before the thread for interp is dead.
          # If not, raise Tcl_Panic on Tcl_AsyncDelete because async handler
          # deleted by the wrong thread.
          interp.delete
        end

So my understanding is that the problem occurs on platforms where RUN_EVENTLOOP_ON_MAIN_THREAD is true, when Tk_Init fails.

I tried this which seems to work:

--- /tmp/tcltklib.c 2020-10-06 12:19:28.491065555 +0000
+++ tcltklib.c  2020-10-06 13:20:46.869578812 +0000
@@ -6256,19 +6256,26 @@
     st = ruby_tcl_stubs_init();
     /* from Tcl_AppInit() */
     if (with_tk) {
+        char *error;
         DUMP1("Tk_Init");
         st = ruby_tk_stubs_init(ptr->ip);
+        if (st != TCLTK_STUBS_OK) {
+            error = Tcl_GetStringResult(ptr->ip);
+            ip_finalize(ptr->ip);
+            Tcl_DeleteInterp(ptr->ip);
+            Tcl_Release(ptr->ip);
+            ptr->ip = (Tcl_Interp*)NULL;
+   }
         switch(st) {
+
         case TCLTK_STUBS_OK:
             break;
         case NO_Tk_Init:
             rb_raise(rb_eLoadError, "tcltklib: can't find Tk_Init()");
         case FAIL_Tk_Init:
-            rb_raise(rb_eRuntimeError, "tcltklib: fail to Tk_Init(). %s",
-                     Tcl_GetStringResult(ptr->ip));
+            rb_raise(rb_eRuntimeError, "tcltklib: fail to Tk_Init(). %s", error);
         case FAIL_Tk_InitStubs:
-            rb_raise(rb_eRuntimeError, "tcltklib: fail to Tk_InitStubs(). %s",
-                     Tcl_GetStringResult(ptr->ip));
+            rb_raise(rb_eRuntimeError, "tcltklib: fail to Tk_InitStubs(). %s", error);
         default:
             rb_raise(rb_eRuntimeError, "tcltklib: unknown error(%d) on ruby_tk_stubs_init", st);
         }
jeremyevans commented 3 years ago

@pterjan Thank you very much for debugging this issue. I'll try your patch later today.

jeremyevans commented 3 years ago

@pterjan I slightly modified your patch and incorporated it into #20. Can you test pull request #20 and see if it works for you. If so, I'll probably be releasing that branch as 0.3.0.

pterjan commented 3 years ago

Thank you, I can confirm this is fixed with pull request #20